reserved_style r_mode; /* supports if, for, while, until */
};
-struct jobset {
- struct pipe *head; /* head of list of running jobs */
- struct pipe *fg; /* current foreground job */
-};
-
struct close_me {
int fd;
struct close_me *next;
static int interactive;
static struct close_me *close_me_head;
static const char *cwd;
-static struct jobset *job_list;
+static struct pipe *job_list;
static unsigned int last_bg_pid;
+static unsigned int last_jobid;
+static unsigned int ctty;
static char *PS1;
static char *PS2;
struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 };
static int free_pipe(struct pipe *pi, int indent);
/* really run the final data structures: */
static int setup_redirects(struct child_prog *prog, int squirrel[]);
-static int pipe_wait(struct pipe *pi);
static int run_list_real(struct pipe *pi);
static void pseudo_exec(struct child_prog *child) __attribute__ ((noreturn));
-int controlling_tty(int check_pgrp);
static int run_pipe_real(struct pipe *pi);
/* extended glob support: */
static int globhack(const char *src, int flags, glob_t *pglob);
static int parse_string_outer(const char *s);
static int parse_file_outer(FILE *f);
/* job management: */
-static void checkjobs();
+static int checkjobs(struct pipe* fg_pipe);
static void insert_bg_job(struct pipe *pi);
static void remove_bg_job(struct pipe *pi);
/* local variable support */
int i, jobnum;
struct pipe *pi=NULL;
+ if (!interactive)
+ return EXIT_FAILURE;
/* If they gave us no args, assume they want the last backgrounded task */
if (!child->argv[1]) {
- for (pi = job_list->head; pi; pi = pi->next) {
- if (pi->progs && pi->progs->pid == last_bg_pid) {
+ for (pi = job_list; pi; pi = pi->next) {
+ if (pi->jobid == last_jobid) {
break;
}
}
return EXIT_FAILURE;
}
- for (pi = job_list->head; pi; pi = pi->next) {
+ for (pi = job_list; pi; pi = pi->next) {
if (pi->jobid == jobnum) {
break;
}
/* Make this job the foreground job */
signal(SIGTTOU, SIG_IGN);
/* suppress messages when run from /linuxrc mag@sysgo.de */
- if (tcsetpgrp(0, pi->pgrp) && errno != ENOTTY)
+ if (tcsetpgrp(ctty, pi->pgrp) && errno != ENOTTY)
perror_msg("tcsetpgrp-1");
signal(SIGTTOU, SIG_DFL);
- job_list->fg = pi;
}
/* Restart the processes in the job */
struct pipe *job;
char *status_string;
- for (job = job_list->head; job; job = job->next) {
+ for (job = job_list; job; job = job->next) {
if (job->running_progs == job->stopped_progs)
status_string = "Stopped";
else
}
}
-/* XXX this definitely needs some more thought, work, and
- * cribbing from other shells */
-static int pipe_wait(struct pipe *pi)
-{
- int rcode=0, i, pid, running, status;
- running = pi->num_progs;
- while (running) {
- pid=waitpid(-1, &status, 0);
- if (pid < 0) perror_msg_and_die("waitpid");
- for (i=0; i < pi->num_progs; i++) {
- if (pi->progs[i].pid == pid) {
- if (i==pi->num_progs-1) rcode=WEXITSTATUS(status);
- pi->progs[i].pid = 0;
- running--;
- break;
- }
- }
- }
- return rcode;
-}
-
/* never returns */
/* XXX no exit() here. If you don't exec, use _exit instead.
* The at_exit handlers apparently confuse the calling process,
/* Linear search for the ID of the job to use */
pi->jobid = 1;
- for (thejob = job_list->head; thejob; thejob = thejob->next)
+ for (thejob = job_list; thejob; thejob = thejob->next)
if (thejob->jobid >= pi->jobid)
pi->jobid = thejob->jobid + 1;
/* add thejob to the list of running jobs */
- if (!job_list->head) {
- thejob = job_list->head = xmalloc(sizeof(*thejob));
+ if (!job_list) {
+ thejob = job_list= xmalloc(sizeof(*thejob));
} else {
- for (thejob = job_list->head; thejob->next; thejob = thejob->next) /* nothing */;
+ for (thejob = job_list; thejob->next; thejob = thejob->next) /* nothing */;
thejob->next = xmalloc(sizeof(*thejob));
thejob = thejob->next;
}
to the list of backgrounded thejobs and leave it alone */
printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid);
last_bg_pid = thejob->progs[0].pid;
+ last_jobid = thejob->jobid;
}
-/* remove a backgrounded job from a jobset */
+/* remove a backgrounded job */
static void remove_bg_job(struct pipe *pi)
{
struct pipe *prev_pipe;
- if (pi == job_list->head) {
- job_list->head = pi->next;
+ if (pi == job_list) {
+ job_list= pi->next;
} else {
- prev_pipe = job_list->head;
+ prev_pipe = job_list;
while (prev_pipe->next != pi)
prev_pipe = prev_pipe->next;
prev_pipe->next = pi->next;
free(pi);
}
-/* Checks to see if any background processes have exited -- if they
+/* Checks to see if any processes have exited -- if they
have, figure out why and see if a job has completed */
-static void checkjobs()
+static int checkjobs(struct pipe* fg_pipe)
{
- int status, ctty;
+ int attributes;
+ int status;
int prognum = 0;
struct pipe *pi;
pid_t childpid;
- while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
- for (pi = job_list->head; pi; pi = pi->next) {
+ attributes = WUNTRACED;
+ if (fg_pipe==NULL) {
+ attributes |= WNOHANG;
+ }
+
+ while ((childpid = waitpid(-1, &status, attributes)) > 0) {
+ if (fg_pipe) {
+ int i, rcode = 0;
+ for (i=0; i < fg_pipe->num_progs; i++) {
+ if (fg_pipe->progs[i].pid == childpid) {
+ if (i==fg_pipe->num_progs-1)
+ rcode=WEXITSTATUS(status);
+ (fg_pipe->num_progs)--;
+ return(rcode);
+ }
+ }
+ }
+
+ for (pi = job_list; pi; pi = pi->next) {
prognum = 0;
while (prognum < pi->num_progs &&
pi->progs[prognum].pid != childpid) prognum++;
perror_msg("waitpid");
/* move the shell to the foreground */
- if (interactive && (ctty=controlling_tty(0))!=-1) {
- if (tcsetpgrp(ctty, getpgrp()))
- perror_msg("tcsetpgrp-2");
- }
+ if (interactive && tcsetpgrp(ctty, getpgid(0)))
+ perror_msg("tcsetpgrp-2");
+ return -1;
}
/* Figure out our controlling tty, checking in order stderr,
* we belong to the foreground process group associated with
* that tty. The value of ctty is needed in order to call
* tcsetpgrp(ctty, ...); */
-int controlling_tty(int check_pgrp)
+void controlling_tty(int check_pgrp)
{
pid_t curpgrp;
- int ctty;
if ((curpgrp = tcgetpgrp(ctty = 2)) < 0
- && (curpgrp = tcgetpgrp(ctty = 0)) < 0
- && (curpgrp = tcgetpgrp(ctty = 1)) < 0)
- return errno = ENOTTY, -1;
+ && (curpgrp = tcgetpgrp(ctty = 0)) < 0
+ && (curpgrp = tcgetpgrp(ctty = 1)) < 0)
+ goto ctty_error;
+
+ if (check_pgrp && curpgrp != getpgid(0))
+ goto ctty_error;
- if (check_pgrp && curpgrp != getpgrp())
- return errno = EPERM, -1;
+ return;
- return ctty;
+ctty_error:
+ ctty = -1;
+ return;
}
/* run_pipe_real() starts all the jobs, but doesn't wait for anything
- * to finish. See pipe_wait().
+ * to finish. See checkjobs().
*
* return code is normally -1, when the caller has to wait for children
* to finish to determine the exit status of the pipe. If the pipe
static int run_pipe_real(struct pipe *pi)
{
int i;
- int ctty;
int nextin, nextout;
int pipefds[2]; /* pipefds[0] is for reading */
struct child_prog *child;
struct built_in_command *x;
- ctty = -1;
nextin = 0;
pi->pgrp = -1;
- /* Check if we are supposed to run in the foreground */
- if (interactive && pi->followup!=PIPE_BG) {
- if ((ctty = controlling_tty(pi->pgrp<0)) < 0) return -1;
- }
-
/* Check if this is a simple builtin (not part of a pipe).
* Builtins within pipes have to fork anyway, and are handled in
* pseudo_exec. "echo foo | read bar" doesn't work on bash, either.
if (interactive) {
/* move the new process group into the foreground */
- /* suppress messages when run from /linuxrc mag@sysgo.de */
- /* XXX probably this "0" should come from controlling_tty() */
- if (tcsetpgrp(0, pi->pgrp) && errno != ENOTTY)
+ if (tcsetpgrp(ctty, pi->pgrp) && errno != ENOTTY)
perror_msg("tcsetpgrp-3");
- rcode = pipe_wait(pi);
- if (tcsetpgrp(0, getpgrp()) && errno != ENOTTY)
+ rcode = checkjobs(pi);
+ if (tcsetpgrp(ctty, getpgid(0)) && errno != ENOTTY)
perror_msg("tcsetpgrp-4");
} else {
- rcode = pipe_wait(pi);
+ rcode = checkjobs(pi);
}
debug_printf("pipe_wait returned %d\n",rcode);
}
(rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) )
skip_more_in_this_rmode=rmode;
}
- checkjobs();
+ checkjobs(NULL);
return rcode;
}
}
-/* I think Erik wrote this. It looks imperfect at best */
-void grab_tty_control(void)
-{
- pid_t initialpgrp;
- do {
- initialpgrp = tcgetpgrp(fileno(stderr));
- if (initialpgrp < 0) {
- error_msg("sh: can't access tty; job control disabled\n");
- }
- if (initialpgrp == -1)
- initialpgrp = getpgrp();
- else if (initialpgrp != getpgrp()) {
- killpg(initialpgrp, SIGTTIN);
- continue;
- }
- } while (0);
-}
-
int shell_main(int argc, char **argv)
{
int opt;
FILE *input;
- struct jobset joblist_end = { NULL, NULL };
char **e = environ;
/* XXX what should these be while sourcing /etc/profile? */
interactive = 0;
close_me_head = NULL;
last_bg_pid = 0;
+ last_jobid = 0;
/* Initialize some more globals to non-zero values */
set_cwd();
- job_list = &joblist_end;
+ job_list = NULL;
#ifdef BB_FEATURE_COMMAND_EDITING
cmdedit_set_initial_prompt();
#else
* don't fight over who gets the foreground */
/* don't pay any attention to this signal; it just confuses
things and isn't really meant for shells anyway */
+ controlling_tty(0);
signal(SIGTTOU, SIG_IGN);
setpgid(0, getpid());
- tcsetpgrp(fileno(stderr), getpid());
+ tcsetpgrp(ctty, getpid());
if (argv[0] && argv[0][0] == '-') {
debug_printf("\nsourcing /etc/profile\n");
if (interactive) {
/* Looks like they want an interactive shell */
fprintf(stdout, "\nhush -- the humble shell v0.01 (testing)\n\n");
- grab_tty_control();
}
if (argv[optind]==NULL) {
opt=parse_file_outer(stdin);
reserved_style r_mode; /* supports if, for, while, until */
};
-struct jobset {
- struct pipe *head; /* head of list of running jobs */
- struct pipe *fg; /* current foreground job */
-};
-
struct close_me {
int fd;
struct close_me *next;
static int interactive;
static struct close_me *close_me_head;
static const char *cwd;
-static struct jobset *job_list;
+static struct pipe *job_list;
static unsigned int last_bg_pid;
+static unsigned int last_jobid;
+static unsigned int ctty;
static char *PS1;
static char *PS2;
struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 };
static int free_pipe(struct pipe *pi, int indent);
/* really run the final data structures: */
static int setup_redirects(struct child_prog *prog, int squirrel[]);
-static int pipe_wait(struct pipe *pi);
static int run_list_real(struct pipe *pi);
static void pseudo_exec(struct child_prog *child) __attribute__ ((noreturn));
-int controlling_tty(int check_pgrp);
static int run_pipe_real(struct pipe *pi);
/* extended glob support: */
static int globhack(const char *src, int flags, glob_t *pglob);
static int parse_string_outer(const char *s);
static int parse_file_outer(FILE *f);
/* job management: */
-static void checkjobs();
+static int checkjobs(struct pipe* fg_pipe);
static void insert_bg_job(struct pipe *pi);
static void remove_bg_job(struct pipe *pi);
/* local variable support */
int i, jobnum;
struct pipe *pi=NULL;
+ if (!interactive)
+ return EXIT_FAILURE;
/* If they gave us no args, assume they want the last backgrounded task */
if (!child->argv[1]) {
- for (pi = job_list->head; pi; pi = pi->next) {
- if (pi->progs && pi->progs->pid == last_bg_pid) {
+ for (pi = job_list; pi; pi = pi->next) {
+ if (pi->jobid == last_jobid) {
break;
}
}
return EXIT_FAILURE;
}
- for (pi = job_list->head; pi; pi = pi->next) {
+ for (pi = job_list; pi; pi = pi->next) {
if (pi->jobid == jobnum) {
break;
}
/* Make this job the foreground job */
signal(SIGTTOU, SIG_IGN);
/* suppress messages when run from /linuxrc mag@sysgo.de */
- if (tcsetpgrp(0, pi->pgrp) && errno != ENOTTY)
+ if (tcsetpgrp(ctty, pi->pgrp) && errno != ENOTTY)
perror_msg("tcsetpgrp-1");
signal(SIGTTOU, SIG_DFL);
- job_list->fg = pi;
}
/* Restart the processes in the job */
struct pipe *job;
char *status_string;
- for (job = job_list->head; job; job = job->next) {
+ for (job = job_list; job; job = job->next) {
if (job->running_progs == job->stopped_progs)
status_string = "Stopped";
else
}
}
-/* XXX this definitely needs some more thought, work, and
- * cribbing from other shells */
-static int pipe_wait(struct pipe *pi)
-{
- int rcode=0, i, pid, running, status;
- running = pi->num_progs;
- while (running) {
- pid=waitpid(-1, &status, 0);
- if (pid < 0) perror_msg_and_die("waitpid");
- for (i=0; i < pi->num_progs; i++) {
- if (pi->progs[i].pid == pid) {
- if (i==pi->num_progs-1) rcode=WEXITSTATUS(status);
- pi->progs[i].pid = 0;
- running--;
- break;
- }
- }
- }
- return rcode;
-}
-
/* never returns */
/* XXX no exit() here. If you don't exec, use _exit instead.
* The at_exit handlers apparently confuse the calling process,
/* Linear search for the ID of the job to use */
pi->jobid = 1;
- for (thejob = job_list->head; thejob; thejob = thejob->next)
+ for (thejob = job_list; thejob; thejob = thejob->next)
if (thejob->jobid >= pi->jobid)
pi->jobid = thejob->jobid + 1;
/* add thejob to the list of running jobs */
- if (!job_list->head) {
- thejob = job_list->head = xmalloc(sizeof(*thejob));
+ if (!job_list) {
+ thejob = job_list= xmalloc(sizeof(*thejob));
} else {
- for (thejob = job_list->head; thejob->next; thejob = thejob->next) /* nothing */;
+ for (thejob = job_list; thejob->next; thejob = thejob->next) /* nothing */;
thejob->next = xmalloc(sizeof(*thejob));
thejob = thejob->next;
}
to the list of backgrounded thejobs and leave it alone */
printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid);
last_bg_pid = thejob->progs[0].pid;
+ last_jobid = thejob->jobid;
}
-/* remove a backgrounded job from a jobset */
+/* remove a backgrounded job */
static void remove_bg_job(struct pipe *pi)
{
struct pipe *prev_pipe;
- if (pi == job_list->head) {
- job_list->head = pi->next;
+ if (pi == job_list) {
+ job_list= pi->next;
} else {
- prev_pipe = job_list->head;
+ prev_pipe = job_list;
while (prev_pipe->next != pi)
prev_pipe = prev_pipe->next;
prev_pipe->next = pi->next;
free(pi);
}
-/* Checks to see if any background processes have exited -- if they
+/* Checks to see if any processes have exited -- if they
have, figure out why and see if a job has completed */
-static void checkjobs()
+static int checkjobs(struct pipe* fg_pipe)
{
- int status, ctty;
+ int attributes;
+ int status;
int prognum = 0;
struct pipe *pi;
pid_t childpid;
- while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
- for (pi = job_list->head; pi; pi = pi->next) {
+ attributes = WUNTRACED;
+ if (fg_pipe==NULL) {
+ attributes |= WNOHANG;
+ }
+
+ while ((childpid = waitpid(-1, &status, attributes)) > 0) {
+ if (fg_pipe) {
+ int i, rcode = 0;
+ for (i=0; i < fg_pipe->num_progs; i++) {
+ if (fg_pipe->progs[i].pid == childpid) {
+ if (i==fg_pipe->num_progs-1)
+ rcode=WEXITSTATUS(status);
+ (fg_pipe->num_progs)--;
+ return(rcode);
+ }
+ }
+ }
+
+ for (pi = job_list; pi; pi = pi->next) {
prognum = 0;
while (prognum < pi->num_progs &&
pi->progs[prognum].pid != childpid) prognum++;
perror_msg("waitpid");
/* move the shell to the foreground */
- if (interactive && (ctty=controlling_tty(0))!=-1) {
- if (tcsetpgrp(ctty, getpgrp()))
- perror_msg("tcsetpgrp-2");
- }
+ if (interactive && tcsetpgrp(ctty, getpgid(0)))
+ perror_msg("tcsetpgrp-2");
+ return -1;
}
/* Figure out our controlling tty, checking in order stderr,
* we belong to the foreground process group associated with
* that tty. The value of ctty is needed in order to call
* tcsetpgrp(ctty, ...); */
-int controlling_tty(int check_pgrp)
+void controlling_tty(int check_pgrp)
{
pid_t curpgrp;
- int ctty;
if ((curpgrp = tcgetpgrp(ctty = 2)) < 0
- && (curpgrp = tcgetpgrp(ctty = 0)) < 0
- && (curpgrp = tcgetpgrp(ctty = 1)) < 0)
- return errno = ENOTTY, -1;
+ && (curpgrp = tcgetpgrp(ctty = 0)) < 0
+ && (curpgrp = tcgetpgrp(ctty = 1)) < 0)
+ goto ctty_error;
+
+ if (check_pgrp && curpgrp != getpgid(0))
+ goto ctty_error;
- if (check_pgrp && curpgrp != getpgrp())
- return errno = EPERM, -1;
+ return;
- return ctty;
+ctty_error:
+ ctty = -1;
+ return;
}
/* run_pipe_real() starts all the jobs, but doesn't wait for anything
- * to finish. See pipe_wait().
+ * to finish. See checkjobs().
*
* return code is normally -1, when the caller has to wait for children
* to finish to determine the exit status of the pipe. If the pipe
static int run_pipe_real(struct pipe *pi)
{
int i;
- int ctty;
int nextin, nextout;
int pipefds[2]; /* pipefds[0] is for reading */
struct child_prog *child;
struct built_in_command *x;
- ctty = -1;
nextin = 0;
pi->pgrp = -1;
- /* Check if we are supposed to run in the foreground */
- if (interactive && pi->followup!=PIPE_BG) {
- if ((ctty = controlling_tty(pi->pgrp<0)) < 0) return -1;
- }
-
/* Check if this is a simple builtin (not part of a pipe).
* Builtins within pipes have to fork anyway, and are handled in
* pseudo_exec. "echo foo | read bar" doesn't work on bash, either.
if (interactive) {
/* move the new process group into the foreground */
- /* suppress messages when run from /linuxrc mag@sysgo.de */
- /* XXX probably this "0" should come from controlling_tty() */
- if (tcsetpgrp(0, pi->pgrp) && errno != ENOTTY)
+ if (tcsetpgrp(ctty, pi->pgrp) && errno != ENOTTY)
perror_msg("tcsetpgrp-3");
- rcode = pipe_wait(pi);
- if (tcsetpgrp(0, getpgrp()) && errno != ENOTTY)
+ rcode = checkjobs(pi);
+ if (tcsetpgrp(ctty, getpgid(0)) && errno != ENOTTY)
perror_msg("tcsetpgrp-4");
} else {
- rcode = pipe_wait(pi);
+ rcode = checkjobs(pi);
}
debug_printf("pipe_wait returned %d\n",rcode);
}
(rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) )
skip_more_in_this_rmode=rmode;
}
- checkjobs();
+ checkjobs(NULL);
return rcode;
}
}
-/* I think Erik wrote this. It looks imperfect at best */
-void grab_tty_control(void)
-{
- pid_t initialpgrp;
- do {
- initialpgrp = tcgetpgrp(fileno(stderr));
- if (initialpgrp < 0) {
- error_msg("sh: can't access tty; job control disabled\n");
- }
- if (initialpgrp == -1)
- initialpgrp = getpgrp();
- else if (initialpgrp != getpgrp()) {
- killpg(initialpgrp, SIGTTIN);
- continue;
- }
- } while (0);
-}
-
int shell_main(int argc, char **argv)
{
int opt;
FILE *input;
- struct jobset joblist_end = { NULL, NULL };
char **e = environ;
/* XXX what should these be while sourcing /etc/profile? */
interactive = 0;
close_me_head = NULL;
last_bg_pid = 0;
+ last_jobid = 0;
/* Initialize some more globals to non-zero values */
set_cwd();
- job_list = &joblist_end;
+ job_list = NULL;
#ifdef BB_FEATURE_COMMAND_EDITING
cmdedit_set_initial_prompt();
#else
* don't fight over who gets the foreground */
/* don't pay any attention to this signal; it just confuses
things and isn't really meant for shells anyway */
+ controlling_tty(0);
signal(SIGTTOU, SIG_IGN);
setpgid(0, getpid());
- tcsetpgrp(fileno(stderr), getpid());
+ tcsetpgrp(ctty, getpid());
if (argv[0] && argv[0][0] == '-') {
debug_printf("\nsourcing /etc/profile\n");
if (interactive) {
/* Looks like they want an interactive shell */
fprintf(stdout, "\nhush -- the humble shell v0.01 (testing)\n\n");
- grab_tty_control();
}
if (argv[optind]==NULL) {
opt=parse_file_outer(stdin);