diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-04-28 16:42:11 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-04-28 16:42:11 +0000 |
| commit | a6a1785a30d6fe011eeabec3c19e154dc475b1b0 (patch) | |
| tree | 2233696369eac228e4fe16caa802888d6fb0b17b /shell | |
| parent | 706fdc98c3d30687d1ce359f58424c87e253017c (diff) | |
| download | busybox-w32-a6a1785a30d6fe011eeabec3c19e154dc475b1b0.tar.gz busybox-w32-a6a1785a30d6fe011eeabec3c19e154dc475b1b0.tar.bz2 busybox-w32-a6a1785a30d6fe011eeabec3c19e154dc475b1b0.zip | |
hush: add ctrl-Z handling for nofork'ed case
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/hush.c | 95 |
1 files changed, 82 insertions, 13 deletions
diff --git a/shell/hush.c b/shell/hush.c index e2ce3676d..c87f3b566 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -431,6 +431,56 @@ static const struct built_in_command bltins[] = { | |||
| 431 | { NULL, NULL, NULL } | 431 | { NULL, NULL, NULL } |
| 432 | }; | 432 | }; |
| 433 | 433 | ||
| 434 | /* move to libbb? */ | ||
| 435 | static void signal_SA_RESTART(int sig, void (*handler)(int)) | ||
| 436 | { | ||
| 437 | struct sigaction sa; | ||
| 438 | sa.sa_handler = handler; | ||
| 439 | sa.sa_flags = SA_RESTART; | ||
| 440 | sigemptyset(&sa.sa_mask); | ||
| 441 | sigaction(sig, &sa, NULL); | ||
| 442 | } | ||
| 443 | |||
| 444 | static sigjmp_buf nofork_jb; | ||
| 445 | static smallint nofork_flag; | ||
| 446 | static struct pipe *nofork_pipe; | ||
| 447 | |||
| 448 | static void handler_ctrl_z(int sig) | ||
| 449 | { | ||
| 450 | pid_t pid; | ||
| 451 | |||
| 452 | fprintf(stderr, "got tty sig %d\n", sig); | ||
| 453 | if (!nofork_flag) | ||
| 454 | return; | ||
| 455 | pid = fork(); | ||
| 456 | if (pid < 0) /* can't fork. Pretend there were no Ctrl-Z */ | ||
| 457 | return; | ||
| 458 | fprintf(stderr, "bg'ing nofork\n"); | ||
| 459 | nofork_flag = 0; | ||
| 460 | nofork_pipe->running_progs = 1; | ||
| 461 | nofork_pipe->stopped_progs = 0; | ||
| 462 | if (!pid) { /* child */ | ||
| 463 | fprintf(stderr, "setting pgrp for child\n"); | ||
| 464 | setpgrp(); | ||
| 465 | signal(sig, SIG_DFL); /* make child do default action (stop) */ | ||
| 466 | raise(sig); /* resend TSTP so that child will be stopped */ | ||
| 467 | fprintf(stderr, "returning to child\n"); | ||
| 468 | /* return to nofork, it will eventually exit now, | ||
| 469 | * not return back to shell */ | ||
| 470 | return; | ||
| 471 | } | ||
| 472 | /* parent */ | ||
| 473 | /* finish filling up pipe info */ | ||
| 474 | nofork_pipe->pgrp = pid; /* child is in its own pgrp */ | ||
| 475 | nofork_pipe->progs[0].pid = pid; | ||
| 476 | nofork_pipe->running_progs = 1; | ||
| 477 | nofork_pipe->stopped_progs = 0; | ||
| 478 | /* parent needs to longjmp out of running nofork. | ||
| 479 | * we will "return" exitcode 0, with child put in background */ | ||
| 480 | // as usual we can have all kinds of nasty problems with leaked malloc data here | ||
| 481 | siglongjmp(nofork_jb, 1); | ||
| 482 | } | ||
| 483 | |||
| 434 | /* Restores tty foreground process group, and exits. | 484 | /* Restores tty foreground process group, and exits. |
| 435 | * May be called as signal handler for fatal signal | 485 | * May be called as signal handler for fatal signal |
| 436 | * (will faithfully resend signal to itself, producing correct exit state) | 486 | * (will faithfully resend signal to itself, producing correct exit state) |
| @@ -1535,14 +1585,32 @@ static int run_pipe_real(struct pipe *pi) | |||
| 1535 | } | 1585 | } |
| 1536 | #if ENABLE_FEATURE_SH_STANDALONE | 1586 | #if ENABLE_FEATURE_SH_STANDALONE |
| 1537 | { | 1587 | { |
| 1538 | // FIXME: applet runs like part of shell - for example, it ignores | ||
| 1539 | // SIGINT! Try to Ctrl-C out of "rm -i"... doesn't work | ||
| 1540 | const struct bb_applet *a = find_applet_by_name(argv[i]); | 1588 | const struct bb_applet *a = find_applet_by_name(argv[i]); |
| 1541 | if (a && a->nofork) { | 1589 | if (a && a->nofork) { |
| 1542 | setup_redirects(child, squirrel); | 1590 | setup_redirects(child, squirrel); |
| 1543 | rcode = run_nofork_applet(a, argv + i); | 1591 | if (sigsetjmp(nofork_jb, 1) == 0) { |
| 1544 | restore_redirects(squirrel); | 1592 | // enable ctrl_z here, not globally? |
| 1545 | return rcode; | 1593 | nofork_flag = 1; |
| 1594 | /* TSTP handler will store pid there etc */ | ||
| 1595 | nofork_pipe = pi; | ||
| 1596 | rcode = run_nofork_applet(a, argv + i); | ||
| 1597 | if (--nofork_flag != 0) | ||
| 1598 | /* Ctrl-Z! forked, we are child */ | ||
| 1599 | exit(rcode); | ||
| 1600 | restore_redirects(squirrel); | ||
| 1601 | return rcode; | ||
| 1602 | } else { | ||
| 1603 | fprintf(stderr, "Exiting nofork early\n"); | ||
| 1604 | /* Ctrl-Z, forked, we are parent. | ||
| 1605 | * Sighandler has longjmped us here */ | ||
| 1606 | //problem: run_nofork_applet did not do the | ||
| 1607 | // "restore" trick and globals are trashed: | ||
| 1608 | // for one, applet_name is not "hush" :) | ||
| 1609 | // need to split run_nofork_applet into setup/run/restore... | ||
| 1610 | restore_redirects(squirrel); | ||
| 1611 | insert_bg_job(pi); | ||
| 1612 | return 0; | ||
| 1613 | } | ||
| 1546 | } | 1614 | } |
| 1547 | } | 1615 | } |
| 1548 | #endif | 1616 | #endif |
| @@ -1585,7 +1653,7 @@ static int run_pipe_real(struct pipe *pi) | |||
| 1585 | /* Don't do pgrp restore anymore on fatal signals */ | 1653 | /* Don't do pgrp restore anymore on fatal signals */ |
| 1586 | set_fatal_sighandler(SIG_DFL); | 1654 | set_fatal_sighandler(SIG_DFL); |
| 1587 | } | 1655 | } |
| 1588 | 1656 | // in non-interactive case fatal sigs are already SIG_DFL | |
| 1589 | close_all(); | 1657 | close_all(); |
| 1590 | if (nextin != 0) { | 1658 | if (nextin != 0) { |
| 1591 | dup2(nextin, 0); | 1659 | dup2(nextin, 0); |
| @@ -2927,17 +2995,16 @@ static void setup_job_control(void) | |||
| 2927 | { | 2995 | { |
| 2928 | pid_t shell_pgrp; | 2996 | pid_t shell_pgrp; |
| 2929 | 2997 | ||
| 2930 | saved_task_pgrp = getpgrp(); | 2998 | saved_task_pgrp = shell_pgrp = getpgrp(); |
| 2931 | debug_printf("saved_task_pgrp=%d\n", saved_task_pgrp); | 2999 | debug_printf("saved_task_pgrp=%d\n", saved_task_pgrp); |
| 2932 | fcntl(interactive_fd, F_SETFD, FD_CLOEXEC); | 3000 | fcntl(interactive_fd, F_SETFD, FD_CLOEXEC); |
| 2933 | 3001 | ||
| 2934 | /* Loop until we are in the foreground. */ | 3002 | /* If we were ran as 'hush &', |
| 2935 | while (1) { | 3003 | * sleep until we are in the foreground. */ |
| 2936 | shell_pgrp = getpgrp(); | 3004 | while (tcgetpgrp(interactive_fd) != shell_pgrp) { |
| 2937 | if (tcgetpgrp(interactive_fd) == shell_pgrp) | 3005 | /* Send TTIN to ourself (will stop us) */ |
| 2938 | break; | ||
| 2939 | // and this does... what? need a comment here | ||
| 2940 | kill(- shell_pgrp, SIGTTIN); | 3006 | kill(- shell_pgrp, SIGTTIN); |
| 3007 | shell_pgrp = getpgrp(); | ||
| 2941 | } | 3008 | } |
| 2942 | 3009 | ||
| 2943 | /* Ignore job-control and misc signals. */ | 3010 | /* Ignore job-control and misc signals. */ |
| @@ -2954,6 +3021,8 @@ static void setup_job_control(void) | |||
| 2954 | 3021 | ||
| 2955 | /* Grab control of the terminal. */ | 3022 | /* Grab control of the terminal. */ |
| 2956 | tcsetpgrp(interactive_fd, shell_pgrp); | 3023 | tcsetpgrp(interactive_fd, shell_pgrp); |
| 3024 | |||
| 3025 | signal_SA_RESTART(SIGTSTP, handler_ctrl_z); | ||
| 2957 | } | 3026 | } |
| 2958 | 3027 | ||
| 2959 | int hush_main(int argc, char **argv); | 3028 | int hush_main(int argc, char **argv); |
