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
This commit is contained in:
parent
42e4af3119
commit
c8653f62f2
90
shell/hush.c
90
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);
|
||||
if (G.saved_tty_pgrp) {
|
||||
/* 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);
|
||||
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) {
|
||||
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)
|
||||
if (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
|
||||
}
|
||||
// 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 (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) {
|
||||
shell_pgrp = getpgrp();
|
||||
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);
|
||||
|
||||
if (G.saved_tty_pgrp) {
|
||||
/* Set other signals to restore saved_tty_pgrp */
|
||||
set_fatal_handlers();
|
||||
/* Put ourselves in our own process group */
|
||||
/* 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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user