/* "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;
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
#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
*/
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)
/* 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 */
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);
{
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
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);
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");
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)
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 */
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) {
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);
}