From c8653f62f2203d0377933c1b5f0442666e27f91a Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Mon, 27 Apr 2009 23:29:14 +0000 Subject: hush: make it possible to have interactive shell on non-ctty. init=/bin/hush: shows prompt, history works, etc. function old new delta hush_main 888 925 +37 block_signals 139 152 +13 builtin_fg_bg 284 293 +9 checkjobs_and_fg_shell 35 41 +6 sigexit 65 66 +1 reset_traps_to_defaults 165 164 -1 parse_stream 2200 2184 -16 run_list 2502 2475 -27 getpgid 35 - -35 ------------------------------------------------------------------------------ (add/remove: 0/1 grow/shrink: 5/3 up/down: 66/-79) Total: -13 bytes --- shell/hush.c | 138 ++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 80 insertions(+), 58 deletions(-) (limited to 'shell/hush.c') diff --git a/shell/hush.c b/shell/hush.c index d0819f691..45a9733cf 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -417,15 +417,26 @@ struct function { /* "Globals" within this file */ /* Sorted roughly by size (smaller offsets == smaller code) */ struct globals { + /* interactive_fd != 0 means we are an interactive shell. + * If we are, then saved_tty_pgrp can also be != 0, meaning + * that controlling tty is available. With saved_tty_pgrp == 0, + * job control still works, but terminal signals + * (^C, ^Z, ^Y, ^\) won't work at all, and background + * process groups can only be created with "cmd &". + * With saved_tty_pgrp != 0, hush will use tcsetpgrp() + * to give tty to the foreground process group, + * and will take it back when the group is stopped (^Z) + * or killed (^C). + */ #if ENABLE_HUSH_INTERACTIVE /* 'interactive_fd' is a fd# open to ctty, if we have one * _AND_ if we decided to act interactively */ int interactive_fd; const char *PS1; const char *PS2; -#define G_interactive_fd (G.interactive_fd) +# define G_interactive_fd (G.interactive_fd) #else -#define G_interactive_fd 0 +# define G_interactive_fd 0 #endif #if ENABLE_FEATURE_EDITING line_input_t *line_input_state; @@ -434,10 +445,9 @@ struct globals { pid_t last_bg_pid; #if ENABLE_HUSH_JOB int run_list_level; - pid_t saved_tty_pgrp; int last_jobid; + pid_t saved_tty_pgrp; struct pipe *job_list; - struct pipe *toplevel_list; #endif smallint flag_SIGINT; #if ENABLE_HUSH_LOOPS @@ -446,7 +456,7 @@ struct globals { #if ENABLE_HUSH_FUNCTIONS /* 0: outside of a function (or sourced file) * -1: inside of a function, ok to use return builtin - * 1: return is invoked, skip all till end of func. + * 1: return is invoked, skip all till end of func */ smallint flag_return_in_progress; #endif @@ -1089,12 +1099,16 @@ static void restore_G_args(save_arg_t *sv, char **argv) */ enum { SPECIAL_INTERACTIVE_SIGS = 0 -#if ENABLE_HUSH_JOB - | (1 << SIGTTIN) | (1 << SIGTTOU) | (1 << SIGTSTP) - | (1 << SIGHUP) -#endif | (1 << SIGTERM) | (1 << SIGINT) + | (1 << SIGHUP) + , +#if ENABLE_HUSH_JOB + SPECIAL_JOB_SIGS = 0 + | (1 << SIGTTIN) + | (1 << SIGTTOU) + | (1 << SIGTSTP) +#endif }; //static void SIGCHLD_handler(int sig UNUSED_PARAM) @@ -1122,7 +1136,7 @@ static void sigexit(int sig) /* Careful: we can end up here after [v]fork. Do not restore * tty pgrp then, only top-level shell process does that */ - if (G_interactive_fd && getpid() == G.root_pid) + if (G.saved_tty_pgrp && getpid() == G.root_pid) tcsetpgrp(G_interactive_fd, G.saved_tty_pgrp); /* Not a signal, just exit */ @@ -3131,7 +3145,7 @@ static const char *get_cmdtext(struct pipe *pi) len += strlen(*argv) + 1; } while (*++argv); p = xmalloc(len); - pi->cmdtext = p;// = xmalloc(len); + pi->cmdtext = p; argv = pi->cmds[0].argv; do { len = strlen(*argv); @@ -3351,10 +3365,12 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe) { pid_t p; int rcode = checkjobs(fg_pipe); - /* Job finished, move the shell to the foreground */ - p = getpgid(0); /* pgid of our process */ - debug_printf_jobs("fg'ing ourself: getpgid(0)=%d\n", (int)p); - tcsetpgrp(G_interactive_fd, p); + if (G.saved_tty_pgrp) { + /* Job finished, move the shell to the foreground */ + p = getpgrp(); /* our process group id */ + debug_printf_jobs("fg'ing ourself: getpgrp()=%d\n", (int)p); + tcsetpgrp(G_interactive_fd, p); + } return rcode; } #endif @@ -3606,7 +3622,10 @@ static int run_pipe(struct pipe *pi) pgrp = pi->pgrp; if (pgrp < 0) /* true for 1st process only */ pgrp = getpid(); - if (setpgid(0, pgrp) == 0 && pi->followup != PIPE_BG) { + if (setpgid(0, pgrp) == 0 + && pi->followup != PIPE_BG + && G.saved_tty_pgrp /* we have ctty */ + ) { /* We do it in *every* child, not just first, * to avoid races */ tcsetpgrp(G_interactive_fd, pgrp); @@ -4023,11 +4042,7 @@ static int run_list(struct pipe *pi) check_and_run_traps(0); #if ENABLE_HUSH_JOB if (G.run_list_level == 1) -{ -debug_printf_exec("insert_bg_job1\n"); insert_bg_job(pi); -debug_printf_exec("insert_bg_job2\n"); -} #endif G.last_exitcode = rcode = EXIT_SUCCESS; debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); @@ -5896,8 +5911,11 @@ static void block_signals(int second_time) unsigned mask; mask = (1 << SIGQUIT); - if (G_interactive_fd) + if (G_interactive_fd) { mask = (1 << SIGQUIT) | SPECIAL_INTERACTIVE_SIGS; + if (G.saved_tty_pgrp) /* we have ctty, job control sigs work */ + mask |= SPECIAL_JOB_SIGS; + } G.non_DFL_mask = mask; if (!second_time) @@ -6179,52 +6197,55 @@ int hush_main(int argc, char **argv) if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { G.saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); debug_printf("saved_tty_pgrp:%d\n", G.saved_tty_pgrp); -//TODO: "interactive" and "have job control" are two different things. -//If tcgetpgrp fails here, "have job control" is false, but "interactive" -//should stay on! Currently, we mix these into one. - if (G.saved_tty_pgrp >= 0) { - /* try to dup stdin to high fd#, >= 255 */ - G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); + if (G.saved_tty_pgrp < 0) + G.saved_tty_pgrp = 0; + + /* try to dup stdin to high fd#, >= 255 */ + G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); + if (G_interactive_fd < 0) { + /* try to dup to any fd */ + G_interactive_fd = dup(STDIN_FILENO); if (G_interactive_fd < 0) { - /* try to dup to any fd */ - G_interactive_fd = dup(STDIN_FILENO); - if (G_interactive_fd < 0) - /* give up */ - G_interactive_fd = 0; + /* give up */ + G_interactive_fd = 0; + G.saved_tty_pgrp = 0; } -// TODO: track & disallow any attempts of user -// to (inadvertently) close/redirect it } +// TODO: track & disallow any attempts of user +// to (inadvertently) close/redirect G_interactive_fd } debug_printf("interactive_fd:%d\n", G_interactive_fd); if (G_interactive_fd) { - pid_t shell_pgrp; - - /* We are indeed interactive shell, and we will perform - * job control. Setting up for that. */ - close_on_exec_on(G_interactive_fd); - /* If we were run as 'hush &', sleep until we are - * in the foreground (tty pgrp == our pgrp). - * If we get started under a job aware app (like bash), - * make sure we are now in charge so we don't fight over - * who gets the foreground */ - while (1) { - shell_pgrp = getpgrp(); - G.saved_tty_pgrp = tcgetpgrp(G_interactive_fd); - if (G.saved_tty_pgrp == shell_pgrp) - break; - /* send TTIN to ourself (should stop us) */ - kill(- shell_pgrp, SIGTTIN); + + if (G.saved_tty_pgrp) { + /* If we were run as 'hush &', sleep until we are + * in the foreground (tty pgrp == our pgrp). + * If we get started under a job aware app (like bash), + * make sure we are now in charge so we don't fight over + * who gets the foreground */ + while (1) { + pid_t shell_pgrp = getpgrp(); + G.saved_tty_pgrp = tcgetpgrp(G_interactive_fd); + if (G.saved_tty_pgrp == shell_pgrp) + break; + /* send TTIN to ourself (should stop us) */ + kill(- shell_pgrp, SIGTTIN); + } } + /* Block some signals */ block_signals(signal_mask_is_inited); - /* Set other signals to restore saved_tty_pgrp */ - set_fatal_handlers(); - /* Put ourselves in our own process group */ - bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ - /* Grab control of the terminal */ - tcsetpgrp(G_interactive_fd, getpid()); + + if (G.saved_tty_pgrp) { + /* Set other signals to restore saved_tty_pgrp */ + set_fatal_handlers(); + /* Put ourselves in our own process group + * (bash, too, does this only if ctty is available) */ + bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ + /* Grab control of the terminal */ + tcsetpgrp(G_interactive_fd, getpid()); + } /* -1 is special - makes xfuncs longjmp, not exit * (we reset die_sleep = 0 whereever we [v]fork) */ enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */ @@ -6604,6 +6625,7 @@ static int builtin_fg_bg(char **argv) if (!G_interactive_fd) return EXIT_FAILURE; + /* If they gave us no args, assume they want the last backgrounded task */ if (!argv[1]) { for (pi = G.job_list; pi; pi = pi->next) { @@ -6628,7 +6650,7 @@ static int builtin_fg_bg(char **argv) found: /* TODO: bash prints a string representation * of job being foregrounded (like "sleep 1 | cat") */ - if (argv[0][0] == 'f') { + if (argv[0][0] == 'f' && G.saved_tty_pgrp) { /* Put the job into the foreground. */ tcsetpgrp(G_interactive_fd, pi->pgrp); } -- cgit v1.2.3