hush: begin fixing non-functional job control
This commit is contained in:
parent
5f786c24e4
commit
54e7ffb3a4
@ -10,14 +10,16 @@
|
|||||||
#include "libbb.h"
|
#include "libbb.h"
|
||||||
|
|
||||||
int die_sleep;
|
int die_sleep;
|
||||||
#if ENABLE_FEATURE_PREFER_APPLETS
|
#if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_HUSH
|
||||||
jmp_buf die_jmp;
|
jmp_buf die_jmp;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void xfunc_die(void)
|
void xfunc_die(void)
|
||||||
{
|
{
|
||||||
if (die_sleep) {
|
if (die_sleep) {
|
||||||
if (ENABLE_FEATURE_PREFER_APPLETS && die_sleep < 0) {
|
if ((ENABLE_FEATURE_PREFER_APPLETS || ENABLE_HUSH)
|
||||||
|
&& die_sleep < 0
|
||||||
|
) {
|
||||||
/* Special case. We arrive here if NOFORK applet
|
/* Special case. We arrive here if NOFORK applet
|
||||||
* calls xfunc, which then decides to die.
|
* calls xfunc, which then decides to die.
|
||||||
* We don't die, but jump instead back to caller.
|
* We don't die, but jump instead back to caller.
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
Various bits of what is known about busybox shells, in no particular order.
|
Various bits of what is known about busybox shells, in no particular order.
|
||||||
|
|
||||||
|
2007-04-21
|
||||||
|
hush: removed setsid() and made job control sort-of-sometimes-work.
|
||||||
|
Ctrl-C in "rm -i" works now except for SH_STANDALONE case.
|
||||||
|
"sleep 1 | exit 3" + "echo $?" works, "sleep 1 | exit 3; echo $?"
|
||||||
|
shows exitcode 0 (should be 3). "sleep 1 | sleep 2 &" fails horribly.
|
||||||
|
|
||||||
2007-04-14
|
2007-04-14
|
||||||
lash, hush: both do setsid() and as a result don't have ctty!
|
lash, hush: both do setsid() and as a result don't have ctty!
|
||||||
Ctrl-C doesn't work for any child (try rm -i), etc...
|
Ctrl-C doesn't work for any child (try rm -i), etc...
|
||||||
|
270
shell/hush.c
270
shell/hush.c
@ -81,9 +81,6 @@
|
|||||||
#include <glob.h> /* glob, of course */
|
#include <glob.h> /* glob, of course */
|
||||||
#include <getopt.h> /* should be pretty obvious */
|
#include <getopt.h> /* should be pretty obvious */
|
||||||
|
|
||||||
//#include <sys/wait.h>
|
|
||||||
//#include <signal.h>
|
|
||||||
|
|
||||||
/* #include <dmalloc.h> */
|
/* #include <dmalloc.h> */
|
||||||
/* #define DEBUG_SHELL */
|
/* #define DEBUG_SHELL */
|
||||||
|
|
||||||
@ -91,7 +88,7 @@
|
|||||||
#define SPECIAL_VAR_SYMBOL 03
|
#define SPECIAL_VAR_SYMBOL 03
|
||||||
#define FLAG_EXIT_FROM_LOOP 1
|
#define FLAG_EXIT_FROM_LOOP 1
|
||||||
#define FLAG_PARSE_SEMICOLON (1 << 1) /* symbol ';' is special for parser */
|
#define FLAG_PARSE_SEMICOLON (1 << 1) /* symbol ';' is special for parser */
|
||||||
#define FLAG_REPARSING (1 << 2) /* >=2nd pass */
|
#define FLAG_REPARSING (1 << 2) /* >=2nd pass */
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
REDIRECT_INPUT = 1,
|
REDIRECT_INPUT = 1,
|
||||||
@ -193,7 +190,7 @@ struct child_prog {
|
|||||||
struct pipe {
|
struct pipe {
|
||||||
int jobid; /* job number */
|
int jobid; /* job number */
|
||||||
int num_progs; /* total number of programs in job */
|
int num_progs; /* total number of programs in job */
|
||||||
int running_progs; /* number of programs running */
|
int running_progs; /* number of programs running (not exited) */
|
||||||
char *text; /* name of job */
|
char *text; /* name of job */
|
||||||
char *cmdbuf; /* buffer various argv's point into */
|
char *cmdbuf; /* buffer various argv's point into */
|
||||||
pid_t pgrp; /* process group ID for the job */
|
pid_t pgrp; /* process group ID for the job */
|
||||||
@ -229,13 +226,17 @@ extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */
|
|||||||
static const char *ifs;
|
static const char *ifs;
|
||||||
static unsigned char map[256];
|
static unsigned char map[256];
|
||||||
static int fake_mode;
|
static int fake_mode;
|
||||||
static int interactive;
|
|
||||||
static struct close_me *close_me_head;
|
static struct close_me *close_me_head;
|
||||||
static const char *cwd;
|
static const char *cwd;
|
||||||
static struct pipe *job_list;
|
static struct pipe *job_list;
|
||||||
static unsigned last_bg_pid;
|
static unsigned last_bg_pid;
|
||||||
static int last_jobid;
|
static int last_jobid;
|
||||||
static unsigned shell_terminal;
|
/* 'interactive_fd' is a fd# open to ctty, if we have one
|
||||||
|
* _AND_ if we decided to mess with job control */
|
||||||
|
static int interactive_fd;
|
||||||
|
static pid_t saved_task_pgrp;
|
||||||
|
static pid_t saved_tty_pgrp;
|
||||||
|
|
||||||
static const char *PS1;
|
static const char *PS1;
|
||||||
static const char *PS2;
|
static const char *PS2;
|
||||||
static struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 };
|
static struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 };
|
||||||
@ -308,7 +309,7 @@ static void __syntax(const char *file, int line)
|
|||||||
{
|
{
|
||||||
bb_error_msg("syntax error %s:%d", file, line);
|
bb_error_msg("syntax error %s:%d", file, line);
|
||||||
}
|
}
|
||||||
// NB: was __FILE__, but that produces full path sometimes, so...
|
/* NB: was __FILE__, but that produces full path sometimes, so... */
|
||||||
#define syntax() __syntax("hush.c", __LINE__)
|
#define syntax() __syntax("hush.c", __LINE__)
|
||||||
|
|
||||||
/* Index of subroutines: */
|
/* Index of subroutines: */
|
||||||
@ -425,6 +426,84 @@ static const struct built_in_command bltins[] = {
|
|||||||
{ NULL, NULL, NULL }
|
{ NULL, NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Restores tty foreground process group, and exits.
|
||||||
|
* May be called as signal handler for fatal signal
|
||||||
|
* (will faithfully resend signal to itself, producing correct exit state)
|
||||||
|
* or called directly with -EXITCODE.
|
||||||
|
* We also call it if xfunc is exiting. */
|
||||||
|
static void sigexit(int sig) ATTRIBUTE_NORETURN;
|
||||||
|
static void sigexit(int sig)
|
||||||
|
{
|
||||||
|
sigset_t block_all;
|
||||||
|
|
||||||
|
/* Disable all signals: job control, SIGPIPE, etc. */
|
||||||
|
sigfillset(&block_all);
|
||||||
|
sigprocmask(SIG_SETMASK, &block_all, NULL);
|
||||||
|
|
||||||
|
if (interactive_fd) {
|
||||||
|
if (sig > 0) {
|
||||||
|
enum { KILLED = sizeof("Killed by signal ")-1 };
|
||||||
|
char buf[KILLED + sizeof(int)*3 + 1];
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
/* bash actually says "Illegal instruction" and the like */
|
||||||
|
strcpy(buf, "Killed by signal ");
|
||||||
|
p = utoa_to_buf(sig, buf+KILLED, sizeof(buf)-KILLED);
|
||||||
|
*p++ = '\n';
|
||||||
|
write(interactive_fd, buf, p-buf);
|
||||||
|
}
|
||||||
|
tcsetpgrp(interactive_fd, saved_tty_pgrp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not a signal, just exit */
|
||||||
|
if (sig <= 0)
|
||||||
|
_exit(- sig);
|
||||||
|
|
||||||
|
/* Enable only this sig and kill ourself with it */
|
||||||
|
signal(sig, SIG_DFL);
|
||||||
|
sigdelset(&block_all, sig);
|
||||||
|
sigprocmask(SIG_SETMASK, &block_all, NULL);
|
||||||
|
raise(sig);
|
||||||
|
_exit(1); /* Should not reach it */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restores tty foreground process group, and exits. */
|
||||||
|
static void hush_exit(int exitcode) ATTRIBUTE_NORETURN;
|
||||||
|
static void hush_exit(int exitcode)
|
||||||
|
{
|
||||||
|
fflush(NULL); /* flush all streams */
|
||||||
|
sigexit(- (exitcode & 0xff));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Signals are grouped, we handle them in batches */
|
||||||
|
static void set_fatal_sighandler(void (*handler)(int))
|
||||||
|
{
|
||||||
|
signal(SIGILL , handler);
|
||||||
|
signal(SIGTRAP, handler);
|
||||||
|
signal(SIGABRT, handler);
|
||||||
|
signal(SIGFPE , handler);
|
||||||
|
signal(SIGBUS , handler);
|
||||||
|
signal(SIGSEGV, handler);
|
||||||
|
/* bash 3.2 seems to handle these just like 'fatal' ones,
|
||||||
|
* but _without_ printing signal name. TODO: mimic this too? */
|
||||||
|
signal(SIGHUP , handler);
|
||||||
|
signal(SIGPIPE, handler);
|
||||||
|
signal(SIGALRM, handler);
|
||||||
|
}
|
||||||
|
static void set_jobctrl_sighandler(void (*handler)(int))
|
||||||
|
{
|
||||||
|
signal(SIGTSTP, handler);
|
||||||
|
signal(SIGTTIN, handler);
|
||||||
|
signal(SIGTTOU, handler);
|
||||||
|
}
|
||||||
|
static void set_misc_sighandler(void (*handler)(int))
|
||||||
|
{
|
||||||
|
signal(SIGINT , handler);
|
||||||
|
signal(SIGQUIT, handler);
|
||||||
|
signal(SIGTERM, handler);
|
||||||
|
}
|
||||||
|
/* SIGCHLD is special and handled separately */
|
||||||
|
|
||||||
static const char *set_cwd(void)
|
static const char *set_cwd(void)
|
||||||
{
|
{
|
||||||
if (cwd == bb_msg_unknown)
|
if (cwd == bb_msg_unknown)
|
||||||
@ -493,13 +572,15 @@ static int builtin_exec(struct child_prog *child)
|
|||||||
/* built-in 'exit' handler */
|
/* built-in 'exit' handler */
|
||||||
static int builtin_exit(struct child_prog *child)
|
static int builtin_exit(struct child_prog *child)
|
||||||
{
|
{
|
||||||
/* bash prints "exit\n" here, then: */
|
// TODO: bash does it ONLY on top-level sh exit (+interacive only?)
|
||||||
|
//puts("exit"); /* bash does it */
|
||||||
|
|
||||||
if (child->argv[1] == NULL)
|
if (child->argv[1] == NULL)
|
||||||
exit(last_return_code);
|
hush_exit(last_return_code);
|
||||||
/* mimic bash: exit 123abc == exit 255 + error msg */
|
/* mimic bash: exit 123abc == exit 255 + error msg */
|
||||||
xfunc_error_retval = 255;
|
xfunc_error_retval = 255;
|
||||||
/* bash: exit -2 == exit 254, no error msg */
|
/* bash: exit -2 == exit 254, no error msg */
|
||||||
exit(xatoi(child->argv[1]));
|
hush_exit(xatoi(child->argv[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* built-in 'export VAR=value' handler */
|
/* built-in 'export VAR=value' handler */
|
||||||
@ -555,7 +636,7 @@ static int builtin_fg_bg(struct child_prog *child)
|
|||||||
int i, jobnum;
|
int i, jobnum;
|
||||||
struct pipe *pi;
|
struct pipe *pi;
|
||||||
|
|
||||||
if (!interactive)
|
if (!interactive_fd)
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
/* If they gave us no args, assume they want the last backgrounded task */
|
/* If they gave us no args, assume they want the last backgrounded task */
|
||||||
if (!child->argv[1]) {
|
if (!child->argv[1]) {
|
||||||
@ -581,7 +662,7 @@ static int builtin_fg_bg(struct child_prog *child)
|
|||||||
found:
|
found:
|
||||||
if (*child->argv[0] == 'f') {
|
if (*child->argv[0] == 'f') {
|
||||||
/* Put the job into the foreground. */
|
/* Put the job into the foreground. */
|
||||||
tcsetpgrp(shell_terminal, pi->pgrp);
|
tcsetpgrp(interactive_fd, pi->pgrp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Restart the processes in the job */
|
/* Restart the processes in the job */
|
||||||
@ -915,8 +996,8 @@ static int file_get(struct in_str *i)
|
|||||||
} else {
|
} else {
|
||||||
/* need to double check i->file because we might be doing something
|
/* need to double check i->file because we might be doing something
|
||||||
* more complicated by now, like sourcing or substituting. */
|
* more complicated by now, like sourcing or substituting. */
|
||||||
if (i->__promptme && interactive && i->file == stdin) {
|
if (i->__promptme && interactive_fd && i->file == stdin) {
|
||||||
while (!i->p || !(interactive && strlen(i->p))) {
|
while (!i->p || !(interactive_fd && strlen(i->p))) {
|
||||||
get_user_input(i);
|
get_user_input(i);
|
||||||
}
|
}
|
||||||
i->promptmode = 2;
|
i->promptmode = 2;
|
||||||
@ -1116,7 +1197,7 @@ static void pseudo_exec(struct child_prog *child)
|
|||||||
|
|
||||||
if (child->group) {
|
if (child->group) {
|
||||||
debug_printf("runtime nesting to group\n");
|
debug_printf("runtime nesting to group\n");
|
||||||
interactive = 0; /* crucial!!!! */
|
interactive_fd = 0; /* crucial!!!! */
|
||||||
rcode = run_list_real(child->group);
|
rcode = run_list_real(child->group);
|
||||||
/* OK to leak memory by not calling free_pipe_list,
|
/* OK to leak memory by not calling free_pipe_list,
|
||||||
* since this process is about to exit */
|
* since this process is about to exit */
|
||||||
@ -1203,25 +1284,39 @@ static int checkjobs(struct pipe* fg_pipe)
|
|||||||
int prognum = 0;
|
int prognum = 0;
|
||||||
struct pipe *pi;
|
struct pipe *pi;
|
||||||
pid_t childpid;
|
pid_t childpid;
|
||||||
|
int rcode = 0;
|
||||||
|
|
||||||
attributes = WUNTRACED;
|
attributes = WUNTRACED;
|
||||||
|
//WUNTRACED?? huh, what will happed on Ctrl-Z? fg waiting code
|
||||||
|
//doesn't seem to be ready for stopped children! (only exiting ones)...
|
||||||
if (fg_pipe == NULL) {
|
if (fg_pipe == NULL) {
|
||||||
attributes |= WNOHANG;
|
attributes |= WNOHANG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wait_more:
|
||||||
while ((childpid = waitpid(-1, &status, attributes)) > 0) {
|
while ((childpid = waitpid(-1, &status, attributes)) > 0) {
|
||||||
|
/* Were we asked to wait for fg pipe? */
|
||||||
if (fg_pipe) {
|
if (fg_pipe) {
|
||||||
int i, rcode = 0;
|
int i;
|
||||||
for (i = 0; i < fg_pipe->num_progs; i++) {
|
for (i = 0; i < fg_pipe->num_progs; i++) {
|
||||||
if (fg_pipe->progs[i].pid == childpid) {
|
if (fg_pipe->progs[i].pid == childpid) {
|
||||||
|
/* printf("process %d exit %d\n", i, WEXITSTATUS(status)); */
|
||||||
|
fg_pipe->progs[i].pid = 0;
|
||||||
if (i == fg_pipe->num_progs-1)
|
if (i == fg_pipe->num_progs-1)
|
||||||
|
/* last process gives overall exitstatus */
|
||||||
rcode = WEXITSTATUS(status);
|
rcode = WEXITSTATUS(status);
|
||||||
fg_pipe->num_progs--;
|
if (--fg_pipe->running_progs <= 0)
|
||||||
return rcode;
|
/* All processes in fg pipe have exited */
|
||||||
|
return rcode;
|
||||||
|
/* There are still running processes in the fg pipe */
|
||||||
|
goto wait_more;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We asked to wait for bg or orphaned children */
|
||||||
|
/* No need to remember exitcode in this case */
|
||||||
|
|
||||||
for (pi = job_list; pi; pi = pi->next) {
|
for (pi = job_list; pi; pi = pi->next) {
|
||||||
prognum = 0;
|
prognum = 0;
|
||||||
while (prognum < pi->num_progs) {
|
while (prognum < pi->num_progs) {
|
||||||
@ -1257,9 +1352,9 @@ static int checkjobs(struct pipe* fg_pipe)
|
|||||||
bb_perror_msg("waitpid");
|
bb_perror_msg("waitpid");
|
||||||
|
|
||||||
/* move the shell to the foreground */
|
/* move the shell to the foreground */
|
||||||
//if (interactive && tcsetpgrp(shell_terminal, getpgid(0)))
|
//if (interactive_fd && tcsetpgrp(interactive_fd, getpgid(0)))
|
||||||
// bb_perror_msg("tcsetpgrp-2");
|
// bb_perror_msg("tcsetpgrp-2");
|
||||||
return -1;
|
return rcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* run_pipe_real() starts all the jobs, but doesn't wait for anything
|
/* run_pipe_real() starts all the jobs, but doesn't wait for anything
|
||||||
@ -1376,6 +1471,8 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
}
|
}
|
||||||
#if ENABLE_FEATURE_SH_STANDALONE
|
#if ENABLE_FEATURE_SH_STANDALONE
|
||||||
{
|
{
|
||||||
|
// FIXME: applet runs like part of shell - for example, it ignores
|
||||||
|
// SIGINT! Try to Ctrl-C out of "rm -i"... doesn't work
|
||||||
const struct bb_applet *a = find_applet_by_name(child->argv[i]);
|
const struct bb_applet *a = find_applet_by_name(child->argv[i]);
|
||||||
if (a && a->nofork) {
|
if (a && a->nofork) {
|
||||||
setup_redirects(child, squirrel);
|
setup_redirects(child, squirrel);
|
||||||
@ -1387,6 +1484,11 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Disable job control signals for shell (parent) and
|
||||||
|
* for initial child code after fork */
|
||||||
|
set_jobctrl_sighandler(SIG_IGN);
|
||||||
|
|
||||||
|
pi->running_progs = 0;
|
||||||
for (i = 0; i < pi->num_progs; i++) {
|
for (i = 0; i < pi->num_progs; i++) {
|
||||||
child = &(pi->progs[i]);
|
child = &(pi->progs[i]);
|
||||||
|
|
||||||
@ -1406,18 +1508,22 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
#else
|
#else
|
||||||
child->pid = vfork();
|
child->pid = vfork();
|
||||||
#endif
|
#endif
|
||||||
if (!child->pid) {
|
if (!child->pid) { /* child */
|
||||||
/* Set the handling for job control signals back to the default. */
|
/* Every child adds itself to new process group
|
||||||
signal(SIGINT, SIG_DFL);
|
* with pgid == pid of first child in pipe */
|
||||||
signal(SIGQUIT, SIG_DFL);
|
if (interactive_fd) {
|
||||||
signal(SIGTERM, SIG_DFL);
|
if (pi->pgrp < 0) /* true for 1st process only */
|
||||||
signal(SIGTSTP, SIG_DFL);
|
pi->pgrp = getpid();
|
||||||
signal(SIGTTIN, SIG_DFL);
|
if (setpgid(0, pi->pgrp) == 0 && pi->followup != PIPE_BG) {
|
||||||
signal(SIGTTOU, SIG_DFL);
|
/* We do it in *every* child, not just first,
|
||||||
signal(SIGCHLD, SIG_DFL);
|
* to avoid races */
|
||||||
|
tcsetpgrp(interactive_fd, pi->pgrp);
|
||||||
|
}
|
||||||
|
/* Don't do pgrp restore anymore on fatal signals */
|
||||||
|
set_fatal_sighandler(SIG_DFL);
|
||||||
|
}
|
||||||
|
|
||||||
close_all();
|
close_all();
|
||||||
|
|
||||||
if (nextin != 0) {
|
if (nextin != 0) {
|
||||||
dup2(nextin, 0);
|
dup2(nextin, 0);
|
||||||
close(nextin);
|
close(nextin);
|
||||||
@ -1429,33 +1535,28 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
if (pipefds[0] != -1) {
|
if (pipefds[0] != -1) {
|
||||||
close(pipefds[0]); /* opposite end of our output pipe */
|
close(pipefds[0]); /* opposite end of our output pipe */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Like bash, explicit redirects override pipes,
|
/* Like bash, explicit redirects override pipes,
|
||||||
* and the pipe fd is available for dup'ing. */
|
* and the pipe fd is available for dup'ing. */
|
||||||
setup_redirects(child, NULL);
|
setup_redirects(child, NULL);
|
||||||
|
|
||||||
if (interactive && pi->followup != PIPE_BG) {
|
/* Restore default handlers just prior to exec */
|
||||||
/* If we (the child) win the race, put ourselves in the process
|
set_jobctrl_sighandler(SIG_DFL);
|
||||||
* group whose leader is the first process in this pipe. */
|
set_misc_sighandler(SIG_DFL);
|
||||||
if (pi->pgrp < 0) {
|
signal(SIGCHLD, SIG_DFL);
|
||||||
pi->pgrp = getpid();
|
|
||||||
}
|
|
||||||
if (setpgid(0, pi->pgrp) == 0) {
|
|
||||||
tcsetpgrp(2, pi->pgrp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pseudo_exec(child);
|
pseudo_exec(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* put our child in the process group whose leader is the
|
pi->running_progs++;
|
||||||
first process in this pipe */
|
|
||||||
if (pi->pgrp < 0) {
|
/* Second and next children need to know ipd of first one */
|
||||||
|
if (pi->pgrp < 0)
|
||||||
pi->pgrp = child->pid;
|
pi->pgrp = child->pid;
|
||||||
}
|
|
||||||
/* Don't check for errors. The child may be dead already,
|
/* Don't check for errors. The child may be dead already,
|
||||||
* in which case setpgid returns error code EACCES. */
|
* in which case setpgid returns error code EACCES. */
|
||||||
setpgid(child->pid, pi->pgrp);
|
//why we do it at all?? child does it itself
|
||||||
|
//if (interactive_fd)
|
||||||
|
// setpgid(child->pid, pi->pgrp);
|
||||||
|
|
||||||
if (nextin != 0)
|
if (nextin != 0)
|
||||||
close(nextin);
|
close(nextin);
|
||||||
@ -1580,14 +1681,14 @@ static int run_list_real(struct pipe *pi)
|
|||||||
insert_bg_job(pi);
|
insert_bg_job(pi);
|
||||||
rcode = EXIT_SUCCESS;
|
rcode = EXIT_SUCCESS;
|
||||||
} else {
|
} else {
|
||||||
if (interactive) {
|
if (interactive_fd) {
|
||||||
/* move the new process group into the foreground */
|
pid_t p;
|
||||||
if (tcsetpgrp(shell_terminal, pi->pgrp) && errno != ENOTTY)
|
|
||||||
bb_perror_msg("tcsetpgrp-3");
|
|
||||||
rcode = checkjobs(pi);
|
rcode = checkjobs(pi);
|
||||||
/* move the shell to the foreground */
|
/* move the shell to the foreground */
|
||||||
if (tcsetpgrp(shell_terminal, getpgid(0)) && errno != ENOTTY)
|
p = getpgid(0);
|
||||||
|
if (tcsetpgrp(interactive_fd, p) && errno != ENOTTY)
|
||||||
bb_perror_msg("tcsetpgrp-4");
|
bb_perror_msg("tcsetpgrp-4");
|
||||||
|
debug_printf("getpgid(0)=%d\n", (int)p);
|
||||||
} else {
|
} else {
|
||||||
rcode = checkjobs(pi);
|
rcode = checkjobs(pi);
|
||||||
}
|
}
|
||||||
@ -2660,28 +2761,33 @@ static int parse_file_outer(FILE *f)
|
|||||||
* we don't fight over who gets the foreground */
|
* we don't fight over who gets the foreground */
|
||||||
static void setup_job_control(void)
|
static void setup_job_control(void)
|
||||||
{
|
{
|
||||||
/*static --why?? */ pid_t shell_pgrp;
|
pid_t shell_pgrp;
|
||||||
|
|
||||||
|
fcntl(interactive_fd, F_SETFD, FD_CLOEXEC);
|
||||||
|
|
||||||
/* Loop until we are in the foreground. */
|
/* Loop until we are in the foreground. */
|
||||||
while (tcgetpgrp(shell_terminal) != (shell_pgrp = getpgrp()))
|
while (1) {
|
||||||
|
shell_pgrp = getpgrp();
|
||||||
|
if (tcgetpgrp(interactive_fd) == shell_pgrp)
|
||||||
|
break;
|
||||||
|
// and this does... what? need a comment here
|
||||||
kill(- shell_pgrp, SIGTTIN);
|
kill(- shell_pgrp, SIGTTIN);
|
||||||
|
}
|
||||||
|
|
||||||
/* Ignore interactive and job-control signals. */
|
/* Ignore job-control and misc signals. */
|
||||||
signal(SIGINT, SIG_IGN);
|
set_jobctrl_sighandler(SIG_IGN);
|
||||||
signal(SIGQUIT, SIG_IGN);
|
set_misc_sighandler(SIG_IGN);
|
||||||
signal(SIGTERM, SIG_IGN);
|
//huh? signal(SIGCHLD, SIG_IGN);
|
||||||
signal(SIGTSTP, SIG_IGN);
|
|
||||||
signal(SIGTTIN, SIG_IGN);
|
/* We _must_ do cleanup on fatal signals */
|
||||||
signal(SIGTTOU, SIG_IGN);
|
set_fatal_sighandler(sigexit);
|
||||||
signal(SIGCHLD, SIG_IGN);
|
|
||||||
|
|
||||||
/* Put ourselves in our own process group. */
|
/* Put ourselves in our own process group. */
|
||||||
setsid();
|
|
||||||
shell_pgrp = getpid();
|
shell_pgrp = getpid();
|
||||||
setpgid(shell_pgrp, shell_pgrp);
|
setpgrp(); /* is the same as setpgid(shell_pgrp, shell_pgrp); */
|
||||||
|
|
||||||
/* Grab control of the terminal. */
|
/* Grab control of the terminal. */
|
||||||
tcsetpgrp(shell_terminal, shell_pgrp);
|
tcsetpgrp(interactive_fd, shell_pgrp);
|
||||||
}
|
}
|
||||||
|
|
||||||
int hush_main(int argc, char **argv);
|
int hush_main(int argc, char **argv);
|
||||||
@ -2705,7 +2811,7 @@ int hush_main(int argc, char **argv)
|
|||||||
ifs = NULL;
|
ifs = NULL;
|
||||||
/* map[] is taken care of with call to update_ifs_map() */
|
/* map[] is taken care of with call to update_ifs_map() */
|
||||||
fake_mode = 0;
|
fake_mode = 0;
|
||||||
interactive = 0;
|
interactive_fd = 0;
|
||||||
close_me_head = NULL;
|
close_me_head = NULL;
|
||||||
last_bg_pid = 0;
|
last_bg_pid = 0;
|
||||||
job_list = NULL;
|
job_list = NULL;
|
||||||
@ -2747,7 +2853,7 @@ int hush_main(int argc, char **argv)
|
|||||||
opt = parse_string_outer(optarg, FLAG_PARSE_SEMICOLON);
|
opt = parse_string_outer(optarg, FLAG_PARSE_SEMICOLON);
|
||||||
goto final_return;
|
goto final_return;
|
||||||
case 'i':
|
case 'i':
|
||||||
interactive++;
|
/*interactive_fd++;*/ //huh??
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
fake_mode++;
|
fake_mode++;
|
||||||
@ -2772,11 +2878,25 @@ int hush_main(int argc, char **argv)
|
|||||||
if (argv[optind] == NULL && input == stdin
|
if (argv[optind] == NULL && input == stdin
|
||||||
&& isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
|
&& isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
|
||||||
) {
|
) {
|
||||||
interactive++;
|
saved_tty_pgrp = tcgetpgrp(STDIN_FILENO);
|
||||||
|
debug_printf("saved_tty_pgrp=%d\n", saved_tty_pgrp);
|
||||||
|
if (saved_tty_pgrp >= 0) {
|
||||||
|
saved_task_pgrp = getpgrp();
|
||||||
|
debug_printf("saved_task_pgrp=%d\n", saved_task_pgrp);
|
||||||
|
/* try to dup to high fd#, >= 255 */
|
||||||
|
interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255);
|
||||||
|
if (interactive_fd < 0) {
|
||||||
|
/* try to dup to any fd */
|
||||||
|
interactive_fd = dup(STDIN_FILENO);
|
||||||
|
if (interactive_fd < 0)
|
||||||
|
/* give up */
|
||||||
|
interactive_fd = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_printf("\ninteractive=%d\n", interactive);
|
debug_printf("\ninteractive_fd=%d\n", interactive_fd);
|
||||||
if (interactive) {
|
if (interactive_fd) {
|
||||||
/* Looks like they want an interactive shell */
|
/* Looks like they want an interactive shell */
|
||||||
#if !ENABLE_FEATURE_SH_EXTRA_QUIET
|
#if !ENABLE_FEATURE_SH_EXTRA_QUIET
|
||||||
printf( "\n\n%s hush - the humble shell v0.01 (testing)\n",
|
printf( "\n\n%s hush - the humble shell v0.01 (testing)\n",
|
||||||
@ -2784,6 +2904,12 @@ int hush_main(int argc, char **argv)
|
|||||||
printf( "Enter 'help' for a list of built-in commands.\n\n");
|
printf( "Enter 'help' for a list of built-in commands.\n\n");
|
||||||
#endif
|
#endif
|
||||||
setup_job_control();
|
setup_job_control();
|
||||||
|
/* Make xfuncs do cleanup on exit */
|
||||||
|
die_sleep = -1; /* flag */
|
||||||
|
if (setjmp(die_jmp)) {
|
||||||
|
/* xfunc has failed! die die die */
|
||||||
|
hush_exit(xfunc_error_retval);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argv[optind] == NULL) {
|
if (argv[optind] == NULL) {
|
||||||
@ -2815,7 +2941,7 @@ int hush_main(int argc, char **argv)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
final_return:
|
final_return:
|
||||||
return opt ? opt : last_return_code;
|
hush_exit(opt ? opt : last_return_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *insert_var_value(char *inp)
|
static char *insert_var_value(char *inp)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user