#include "config.h"
#include "bashtypes.h"
#include "trap.h"
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "posixtime.h"
#if defined (HAVE_SYS_RESOURCE_H) && defined (HAVE_WAIT3) && !defined (_POSIX_VERSION) && !defined (RLIMTYPE)
# include <sys/resource.h>
#endif
#if defined (HAVE_SYS_FILE_H)
# include <sys/file.h>
#endif
#include "filecntl.h"
#include <sys/ioctl.h>
#include <sys/param.h>
#if defined (BUFFERED_INPUT)
# include "input.h"
#endif
#include "shtty.h"
#if defined (hpux) && !defined (TERMIOS_TTY_DRIVER)
# include <bsdtty.h>
#endif
#include "bashansi.h"
#include "bashintl.h"
#include "shell.h"
#include "jobs.h"
#include "flags.h"
#include "builtins/builtext.h"
#include "builtins/common.h"
#if !defined (errno)
extern int errno;
#endif
#define DEFAULT_CHILD_MAX 32
#if !defined (DEBUG)
#define MAX_JOBS_IN_ARRAY 4096
#else
#define MAX_JOBS_IN_ARRAY 128
#endif
#define DEL_WARNSTOPPED 1
#define DEL_NOBGPID 2
#if defined (ultrix) && defined (mips) && defined (_POSIX_VERSION)
# define WAITPID(pid, statusp, options) \
wait3 ((union wait *)statusp, options, (struct rusage *)0)
#else
# if defined (_POSIX_VERSION) || defined (HAVE_WAITPID)
# define WAITPID(pid, statusp, options) \
waitpid ((pid_t)pid, statusp, options)
# else
# if defined (HAVE_WAIT3)
# define WAITPID(pid, statusp, options) \
wait3 (statusp, options, (struct rusage *)0)
# else
# define WAITPID(pid, statusp, options) \
wait3 (statusp, options, (int *)0)
# endif
# endif
#endif
#if defined (GETPGRP_VOID)
# define getpgid(p) getpgrp ()
#else
# define getpgid(p) getpgrp (p)
#endif
#if defined (MUST_REINSTALL_SIGHANDLERS)
# define REINSTALL_SIGCHLD_HANDLER signal (SIGCHLD, sigchld_handler)
#else
# define REINSTALL_SIGCHLD_HANDLER
#endif
#if !defined (WCONTINUED) || defined (WCONTINUED_BROKEN)
# undef WCONTINUED
# define WCONTINUED 0
#endif
#if !defined (WIFCONTINUED)
# define WIFCONTINUED(s) (0)
#endif
#define JOB_SLOTS 8
typedef int sh_job_map_func_t __P((JOB *, int, int, int));
extern int subshell_environment, line_number;
extern int posixly_correct, shell_level;
extern int last_command_exit_value, last_command_exit_signal;
extern int loop_level, breaking;
extern int sourcelevel;
extern int running_trap;
extern sh_builtin_func_t *this_shell_builtin;
extern char *shell_name, *this_command_name;
extern sigset_t top_level_mask;
extern procenv_t wait_intr_buf;
extern int wait_signal_received;
extern WORD_LIST *subst_assign_varlist;
static struct jobstats zerojs = { -1L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NO_JOB, NO_JOB, 0, 0 };
struct jobstats js = { -1L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NO_JOB, NO_JOB, 0, 0 };
struct bgpids bgpids = { 0, 0, 0 };
JOB **jobs = (JOB **)NULL;
#if 0
int job_slots = 0;
#endif
int shell_tty = -1;
pid_t shell_pgrp = NO_PID;
pid_t terminal_pgrp = NO_PID;
pid_t original_pgrp = NO_PID;
pid_t pipeline_pgrp = (pid_t)0;
#if defined (PGRP_PIPE)
int pgrp_pipe[2] = { -1, -1 };
#endif
#if 0
int current_job = NO_JOB;
int previous_job = NO_JOB;
#endif
pid_t last_made_pid = NO_PID;
pid_t last_asynchronous_pid = NO_PID;
PROCESS *the_pipeline = (PROCESS *)NULL;
int job_control = 1;
int already_making_children = 0;
int check_window_size;
static void run_sigchld_trap __P((int));
static sighandler wait_sigint_handler __P((int));
static sighandler sigchld_handler __P((int));
static sighandler sigcont_sighandler __P((int));
static sighandler sigstop_sighandler __P((int));
static int waitchld __P((pid_t, int));
static PROCESS *find_pipeline __P((pid_t, int, int *));
static PROCESS *find_process __P((pid_t, int, int *));
static char *current_working_directory __P((void));
static char *job_working_directory __P((void));
static char *j_strsignal __P((int));
static char *printable_job_status __P((int, PROCESS *, int));
static PROCESS *find_last_proc __P((int, int));
static pid_t find_last_pid __P((int, int));
static int set_new_line_discipline __P((int));
static int map_over_jobs __P((sh_job_map_func_t *, int, int));
static int job_last_stopped __P((int));
static int job_last_running __P((int));
static int most_recent_job_in_state __P((int, JOB_STATE));
static int find_job __P((pid_t, int, PROCESS **));
static int print_job __P((JOB *, int, int, int));
static int process_exit_status __P((WAIT));
static int process_exit_signal __P((WAIT));
static int job_exit_status __P((int));
static int job_exit_signal __P((int));
static int set_job_status_and_cleanup __P((int));
static WAIT job_signal_status __P((int));
static WAIT raw_job_exit_status __P((int));
static void notify_of_job_status __P((void));
static void reset_job_indices __P((void));
static void cleanup_dead_jobs __P((void));
static int processes_in_job __P((int));
static void realloc_jobs_list __P((void));
static int compact_jobs_list __P((int));
static int discard_pipeline __P((PROCESS *));
static void add_process __P((char *, pid_t));
static void print_pipeline __P((PROCESS *, int, int, FILE *));
static void pretty_print_job __P((int, int, FILE *));
static void set_current_job __P((int));
static void reset_current __P((void));
static void set_job_running __P((int));
static void setjstatus __P((int));
static void mark_all_jobs_as_dead __P((void));
static void mark_dead_jobs_as_notified __P((int));
static void restore_sigint_handler __P((void));
#if defined (PGRP_PIPE)
static void pipe_read __P((int *));
static void pipe_close __P((int *));
#endif
static struct pidstat *bgp_alloc __P((pid_t, int));
static struct pidstat *bgp_add __P((pid_t, int));
static int bgp_delete __P((pid_t));
static void bgp_clear __P((void));
static int bgp_search __P((pid_t));
static void bgp_prune __P((void));
#if defined (ARRAY_VARS)
static int *pstatuses;
static int statsize;
#endif
static int sigchld;
static int queue_sigchld;
#define QUEUE_SIGCHLD(os) (os) = sigchld, queue_sigchld++
#define UNQUEUE_SIGCHLD(os) \
do { \
queue_sigchld--; \
if (queue_sigchld == 0 && os != sigchld) \
waitchld (-1, 0); \
} while (0)
static SigHandler *old_tstp, *old_ttou, *old_ttin;
static SigHandler *old_cont = (SigHandler *)SIG_DFL;
static PROCESS *saved_pipeline;
static int saved_already_making_children;
static int jobs_list_frozen;
static char retcode_name_buffer[64];
static pid_t first_pid = NO_PID;
static int pid_wrap = -1;
#if !defined (_POSIX_VERSION)
#define setpgid(pid, pgrp) setpgrp (pid, pgrp)
#define tcsetpgrp(fd, pgrp) ioctl ((fd), TIOCSPGRP, &(pgrp))
pid_t
tcgetpgrp (fd)
int fd;
{
pid_t pgrp;
if (ioctl (fd, TIOCGPGRP, &pgrp) < 0)
return (-1);
return (pgrp);
}
#endif
void
init_job_stats ()
{
js = zerojs;
first_pid = NO_PID;
pid_wrap = -1;
}
static char *
current_working_directory ()
{
char *dir;
static char d[PATH_MAX];
dir = get_string_value ("PWD");
if (dir == 0 && the_current_working_directory && no_symbolic_links)
dir = the_current_working_directory;
if (dir == 0)
{
dir = getcwd (d, sizeof(d));
if (dir)
dir = d;
}
return (dir == 0) ? "<unknown>" : dir;
}
static char *
job_working_directory ()
{
char *dir;
dir = get_string_value ("PWD");
if (dir)
return (savestring (dir));
dir = get_working_directory ("job-working-directory");
if (dir)
return (dir);
return (savestring ("<unknown>"));
}
void
making_children ()
{
if (already_making_children)
return;
already_making_children = 1;
start_pipeline ();
}
void
stop_making_children ()
{
already_making_children = 0;
}
void
cleanup_the_pipeline ()
{
PROCESS *disposer;
sigset_t set, oset;
BLOCK_CHILD (set, oset);
disposer = the_pipeline;
the_pipeline = (PROCESS *)NULL;
UNBLOCK_CHILD (oset);
if (disposer)
discard_pipeline (disposer);
}
void
save_pipeline (clear)
int clear;
{
saved_pipeline = the_pipeline;
if (clear)
the_pipeline = (PROCESS *)NULL;
saved_already_making_children = already_making_children;
}
void
restore_pipeline (discard)
int discard;
{
PROCESS *old_pipeline;
old_pipeline = the_pipeline;
the_pipeline = saved_pipeline;
already_making_children = saved_already_making_children;
if (discard)
discard_pipeline (old_pipeline);
}
void
start_pipeline ()
{
if (the_pipeline)
{
cleanup_the_pipeline ();
pipeline_pgrp = 0;
#if defined (PGRP_PIPE)
pipe_close (pgrp_pipe);
#endif
}
#if defined (PGRP_PIPE)
if (job_control)
{
if (pipe (pgrp_pipe) == -1)
sys_error ("start_pipeline: pgrp pipe");
}
#endif
}
int
stop_pipeline (async, deferred)
int async;
COMMAND *deferred;
{
register int i, j;
JOB *newjob;
sigset_t set, oset;
BLOCK_CHILD (set, oset);
#if defined (PGRP_PIPE)
pipe_close (pgrp_pipe);
#endif
cleanup_dead_jobs ();
if (js.j_jobslots == 0)
{
js.j_jobslots = JOB_SLOTS;
jobs = (JOB **)xmalloc (js.j_jobslots * sizeof (JOB *));
for (i = 0; i < js.j_jobslots; i++)
jobs[i] = (JOB *)NULL;
js.j_firstj = js.j_lastj = js.j_njobs = 0;
}
if (interactive)
{
for (i = js.j_jobslots; i; i--)
if (jobs[i - 1])
break;
}
else
{
#if 0
for (i = js.j_lastj+1; i != js.j_lastj; i++)
{
if (i >= js.j_jobslots)
i = 0;
if (jobs[i] == 0)
break;
}
if (i == js.j_lastj)
i = js.j_jobslots;
#else
for (i = js.j_lastj ? js.j_lastj + 1 : js.j_lastj; i < js.j_jobslots; i++)
if (jobs[i] == 0)
break;
#endif
}
if ((interactive_shell == 0 || subshell_environment) && i == js.j_jobslots && js.j_jobslots >= MAX_JOBS_IN_ARRAY)
i = compact_jobs_list (0);
if (i == js.j_jobslots)
{
js.j_jobslots += JOB_SLOTS;
jobs = (JOB **)xrealloc (jobs, (js.j_jobslots * sizeof (JOB *)));
for (j = i; j < js.j_jobslots; j++)
jobs[j] = (JOB *)NULL;
}
if (the_pipeline)
{
register PROCESS *p;
int any_running, any_stopped, n;
newjob = (JOB *)xmalloc (sizeof (JOB));
for (n = 1, p = the_pipeline; p->next != the_pipeline; n++, p = p->next)
;
p->next = (PROCESS *)NULL;
newjob->pipe = REVERSE_LIST (the_pipeline, PROCESS *);
for (p = newjob->pipe; p->next; p = p->next)
;
p->next = newjob->pipe;
the_pipeline = (PROCESS *)NULL;
newjob->pgrp = pipeline_pgrp;
pipeline_pgrp = 0;
newjob->flags = 0;
if (job_control)
newjob->flags |= J_JOBCONTROL;
p = newjob->pipe;
any_running = any_stopped = 0;
do
{
any_running |= PRUNNING (p);
any_stopped |= PSTOPPED (p);
p = p->next;
}
while (p != newjob->pipe);
newjob->state = any_running ? JRUNNING : (any_stopped ? JSTOPPED : JDEAD);
newjob->wd = job_working_directory ();
newjob->deferred = deferred;
newjob->j_cleanup = (sh_vptrfunc_t *)NULL;
newjob->cleanarg = (PTR_T) NULL;
jobs[i] = newjob;
if (newjob->state == JDEAD && (newjob->flags & J_FOREGROUND))
setjstatus (i);
if (newjob->state == JDEAD)
{
js.c_reaped += n;
js.j_ndead++;
}
js.c_injobs += n;
js.j_lastj = i;
js.j_njobs++;
}
else
newjob = (JOB *)NULL;
if (newjob)
js.j_lastmade = newjob;
if (async)
{
if (newjob)
{
newjob->flags &= ~J_FOREGROUND;
newjob->flags |= J_ASYNC;
js.j_lastasync = newjob;
}
reset_current ();
}
else
{
if (newjob)
{
newjob->flags |= J_FOREGROUND;
if (job_control && newjob->pgrp && (subshell_environment&SUBSHELL_ASYNC) == 0)
give_terminal_to (newjob->pgrp, 0);
}
}
stop_making_children ();
UNBLOCK_CHILD (oset);
return (js.j_current);
}
static struct pidstat *
bgp_alloc (pid, status)
pid_t pid;
int status;
{
struct pidstat *ps;
ps = (struct pidstat *)xmalloc (sizeof (struct pidstat));
ps->pid = pid;
ps->status = status;
ps->next = (struct pidstat *)0;
return ps;
}
static struct pidstat *
bgp_add (pid, status)
pid_t pid;
int status;
{
struct pidstat *ps;
ps = bgp_alloc (pid, status);
if (bgpids.list == 0)
{
bgpids.list = bgpids.end = ps;
bgpids.npid = 0;
}
else
{
bgpids.end->next = ps;
bgpids.end = ps;
}
bgpids.npid++;
if (bgpids.npid > js.c_childmax)
bgp_prune ();
return ps;
}
static int
bgp_delete (pid)
pid_t pid;
{
struct pidstat *prev, *p;
for (prev = p = bgpids.list; p; prev = p, p = p->next)
if (p->pid == pid)
{
prev->next = p->next;
break;
}
if (p == 0)
return 0;
#if defined (DEBUG)
itrace("bgp_delete: deleting %d", pid);
#endif
if (p == bgpids.list)
bgpids.list = bgpids.list->next;
else if (p == bgpids.end)
bgpids.end = prev;
bgpids.npid--;
if (bgpids.npid == 0)
bgpids.list = bgpids.end = 0;
else if (bgpids.npid == 1)
bgpids.end = bgpids.list;
free (p);
return 1;
}
static void
bgp_clear ()
{
struct pidstat *ps, *p;
for (ps = bgpids.list; ps; )
{
p = ps;
ps = ps->next;
free (p);
}
bgpids.list = bgpids.end = 0;
bgpids.npid = 0;
}
static int
bgp_search (pid)
pid_t pid;
{
struct pidstat *ps;
for (ps = bgpids.list ; ps; ps = ps->next)
if (ps->pid == pid)
return ps->status;
return -1;
}
static void
bgp_prune ()
{
struct pidstat *ps;
while (bgpids.npid > js.c_childmax)
{
ps = bgpids.list;
bgpids.list = bgpids.list->next;
free (ps);
bgpids.npid--;
}
}
static void
reset_job_indices ()
{
int old;
if (jobs[js.j_firstj] == 0)
{
old = js.j_firstj++;
if (old >= js.j_jobslots)
old = js.j_jobslots - 1;
while (js.j_firstj != old)
{
if (js.j_firstj >= js.j_jobslots)
js.j_firstj = 0;
if (jobs[js.j_firstj] || js.j_firstj == old)
break;
js.j_firstj++;
}
if (js.j_firstj == old)
js.j_firstj = js.j_lastj = js.j_njobs = 0;
}
if (jobs[js.j_lastj] == 0)
{
old = js.j_lastj--;
if (old < 0)
old = 0;
while (js.j_lastj != old)
{
if (js.j_lastj < 0)
js.j_lastj = js.j_jobslots - 1;
if (jobs[js.j_lastj] || js.j_lastj == old)
break;
js.j_lastj--;
}
if (js.j_lastj == old)
js.j_firstj = js.j_lastj = js.j_njobs = 0;
}
}
static void
cleanup_dead_jobs ()
{
register int i;
int os;
if (js.j_jobslots == 0 || jobs_list_frozen)
return;
QUEUE_SIGCHLD(os);
for (i = 0; i < js.j_jobslots; i++)
{
#if defined (DEBUG)
if (i < js.j_firstj && jobs[i])
itrace("cleanup_dead_jobs: job %d non-null before js.j_firstj (%d)", i, js.j_firstj);
if (i > js.j_lastj && jobs[i])
itrace("cleanup_dead_jobs: job %d non-null after js.j_lastj (%d)", i, js.j_lastj);
#endif
if (jobs[i] && DEADJOB (i) && IS_NOTIFIED (i))
delete_job (i, 0);
}
UNQUEUE_SIGCHLD(os);
}
static int
processes_in_job (job)
int job;
{
int nproc;
register PROCESS *p;
nproc = 0;
p = jobs[job]->pipe;
do
{
p = p->next;
nproc++;
}
while (p != jobs[job]->pipe);
return nproc;
}
static void
delete_old_job (pid)
pid_t pid;
{
PROCESS *p;
int job;
job = find_job (pid, 0, &p);
if (job != NO_JOB)
{
#ifdef DEBUG
itrace ("delete_old_job: found pid %d in job %d with state %d", pid, job, jobs[job]->state);
#endif
if (JOBSTATE (job) == JDEAD)
delete_job (job, DEL_NOBGPID);
else
{
internal_warning (_("forked pid %d appears in running job %d"), pid, job);
if (p)
p->pid = 0;
}
}
}
static void
realloc_jobs_list ()
{
sigset_t set, oset;
int nsize, i, j, ncur, nprev;
JOB **nlist;
ncur = nprev = NO_JOB;
nsize = ((js.j_njobs + JOB_SLOTS - 1) / JOB_SLOTS);
nsize *= JOB_SLOTS;
i = js.j_njobs % JOB_SLOTS;
if (i == 0 || i > (JOB_SLOTS >> 1))
nsize += JOB_SLOTS;
BLOCK_CHILD (set, oset);
nlist = (js.j_jobslots == nsize) ? jobs : (JOB **) xmalloc (nsize * sizeof (JOB *));
for (i = j = 0; i < js.j_jobslots; i++)
if (jobs[i])
{
if (i == js.j_current)
ncur = j;
if (i == js.j_previous)
nprev = j;
nlist[j++] = jobs[i];
}
#if defined (DEBUG)
itrace ("realloc_jobs_list: resize jobs list from %d to %d", js.j_jobslots, nsize);
itrace ("realloc_jobs_list: j_lastj changed from %d to %d", js.j_lastj, (j > 0) ? j - 1 : 0);
itrace ("realloc_jobs_list: j_njobs changed from %d to %d", js.j_njobs, (j > 0) ? j - 1 : 0);
#endif
js.j_firstj = 0;
js.j_lastj = (j > 0) ? j - 1 : 0;
js.j_njobs = j;
js.j_jobslots = nsize;
for ( ; j < nsize; j++)
nlist[j] = (JOB *)NULL;
if (jobs != nlist)
{
free (jobs);
jobs = nlist;
}
if (ncur != NO_JOB)
js.j_current = ncur;
if (nprev != NO_JOB)
js.j_previous = nprev;
if (js.j_current == NO_JOB || js.j_previous == NO_JOB || js.j_current > js.j_lastj || js.j_previous > js.j_lastj)
reset_current ();
#ifdef DEBUG
itrace ("realloc_jobs_list: reset js.j_current (%d) and js.j_previous (%d)", js.j_current, js.j_previous);
#endif
UNBLOCK_CHILD (oset);
}
static int
compact_jobs_list (flags)
int flags;
{
if (js.j_jobslots == 0 || jobs_list_frozen)
return js.j_jobslots;
reap_dead_jobs ();
realloc_jobs_list ();
#ifdef DEBUG
itrace("compact_jobs_list: returning %d", (js.j_lastj || jobs[js.j_lastj]) ? js.j_lastj + 1 : 0);
#endif
return ((js.j_lastj || jobs[js.j_lastj]) ? js.j_lastj + 1 : 0);
}
void
delete_job (job_index, dflags)
int job_index, dflags;
{
register JOB *temp;
PROCESS *proc;
int ndel;
if (js.j_jobslots == 0 || jobs_list_frozen)
return;
if ((dflags & DEL_WARNSTOPPED) && subshell_environment == 0 && STOPPED (job_index))
internal_warning (_("deleting stopped job %d with process group %ld"), job_index+1, (long)jobs[job_index]->pgrp);
temp = jobs[job_index];
if (temp == 0)
return;
if ((dflags & DEL_NOBGPID) == 0)
{
proc = find_last_proc (job_index, 0);
if (proc)
bgp_add (proc->pid, process_exit_status (proc->status));
}
jobs[job_index] = (JOB *)NULL;
if (temp == js.j_lastmade)
js.j_lastmade = 0;
else if (temp == js.j_lastasync)
js.j_lastasync = 0;
free (temp->wd);
ndel = discard_pipeline (temp->pipe);
js.c_injobs -= ndel;
if (temp->state == JDEAD)
{
js.c_reaped -= ndel;
js.j_ndead--;
if (js.c_reaped < 0)
{
#ifdef DEBUG
itrace("delete_job (%d pgrp %d): js.c_reaped (%d) < 0 ndel = %d js.j_ndead = %d", job_index, temp->pgrp, js.c_reaped, ndel, js.j_ndead);
#endif
js.c_reaped = 0;
}
}
if (temp->deferred)
dispose_command (temp->deferred);
free (temp);
js.j_njobs--;
if (js.j_njobs == 0)
js.j_firstj = js.j_lastj = 0;
else if (jobs[js.j_firstj] == 0 || jobs[js.j_lastj] == 0)
reset_job_indices ();
if (job_index == js.j_current || job_index == js.j_previous)
reset_current ();
}
void
nohup_job (job_index)
int job_index;
{
register JOB *temp;
if (js.j_jobslots == 0)
return;
if (temp = jobs[job_index])
temp->flags |= J_NOHUP;
}
static int
discard_pipeline (chain)
register PROCESS *chain;
{
register PROCESS *this, *next;
int n;
this = chain;
n = 0;
do
{
next = this->next;
FREE (this->command);
free (this);
n++;
this = next;
}
while (this != chain);
return n;
}
static void
add_process (name, pid)
char *name;
pid_t pid;
{
PROCESS *t, *p;
#if defined (RECYCLES_PIDS)
int j;
p = find_process (pid, 0, &j);
if (p)
{
# ifdef DEBUG
if (j == NO_JOB)
internal_warning ("add_process: process %5ld (%s) in the_pipeline", (long)p->pid, p->command);
# endif
if (PALIVE (p))
internal_warning ("add_process: pid %5ld (%s) marked as still alive", (long)p->pid, p->command);
p->running = PS_RECYCLED;
}
#endif
t = (PROCESS *)xmalloc (sizeof (PROCESS));
t->next = the_pipeline;
t->pid = pid;
WSTATUS (t->status) = 0;
t->running = PS_RUNNING;
t->command = name;
the_pipeline = t;
if (t->next == 0)
t->next = t;
else
{
p = t->next;
while (p->next != t->next)
p = p->next;
p->next = t;
}
}
#if 0
int
rotate_the_pipeline ()
{
PROCESS *p;
if (the_pipeline->next == the_pipeline)
return;
for (p = the_pipeline; p->next != the_pipeline; p = p->next)
;
the_pipeline = p;
}
int
reverse_the_pipeline ()
{
PROCESS *p, *n;
if (the_pipeline->next == the_pipeline)
return;
for (p = the_pipeline; p->next != the_pipeline; p = p->next)
;
p->next = (PROCESS *)NULL;
n = REVERSE_LIST (the_pipeline, PROCESS *);
the_pipeline = n;
for (p = the_pipeline; p->next; p = p->next)
;
p->next = the_pipeline;
}
#endif
static int
map_over_jobs (func, arg1, arg2)
sh_job_map_func_t *func;
int arg1, arg2;
{
register int i;
int result;
sigset_t set, oset;
if (js.j_jobslots == 0)
return 0;
BLOCK_CHILD (set, oset);
for (i = result = 0; i < js.j_jobslots; i++)
{
#if defined (DEBUG)
if (i < js.j_firstj && jobs[i])
itrace("map_over_jobs: job %d non-null before js.j_firstj (%d)", i, js.j_firstj);
if (i > js.j_lastj && jobs[i])
itrace("map_over_jobs: job %d non-null after js.j_lastj (%d)", i, js.j_lastj);
#endif
if (jobs[i])
{
result = (*func)(jobs[i], arg1, arg2, i);
if (result)
break;
}
}
UNBLOCK_CHILD (oset);
return (result);
}
void
terminate_current_pipeline ()
{
if (pipeline_pgrp && pipeline_pgrp != shell_pgrp)
{
killpg (pipeline_pgrp, SIGTERM);
killpg (pipeline_pgrp, SIGCONT);
}
}
void
terminate_stopped_jobs ()
{
register int i;
for (i = 0; i < js.j_jobslots; i++)
{
if (jobs[i] && STOPPED (i))
{
killpg (jobs[i]->pgrp, SIGTERM);
killpg (jobs[i]->pgrp, SIGCONT);
}
}
}
void
hangup_all_jobs ()
{
register int i;
for (i = 0; i < js.j_jobslots; i++)
{
if (jobs[i])
{
if (jobs[i]->flags & J_NOHUP)
continue;
killpg (jobs[i]->pgrp, SIGHUP);
if (STOPPED (i))
killpg (jobs[i]->pgrp, SIGCONT);
}
}
}
void
kill_current_pipeline ()
{
stop_making_children ();
start_pipeline ();
}
static PROCESS *
find_pipeline (pid, alive_only, jobp)
pid_t pid;
int alive_only;
int *jobp;
{
int job;
PROCESS *p;
if (jobp)
*jobp = NO_JOB;
if (the_pipeline)
{
p = the_pipeline;
do
{
if (p->pid == pid && ((alive_only == 0 && PRECYCLED(p) == 0) || PALIVE(p)))
return (p);
p = p->next;
}
while (p != the_pipeline);
}
job = find_job (pid, alive_only, &p);
if (jobp)
*jobp = job;
return (job == NO_JOB) ? (PROCESS *)NULL : jobs[job]->pipe;
}
static PROCESS *
find_process (pid, alive_only, jobp)
pid_t pid;
int alive_only;
int *jobp;
{
PROCESS *p;
p = find_pipeline (pid, alive_only, jobp);
while (p && p->pid != pid)
p = p->next;
return p;
}
static int
find_job (pid, alive_only, procp)
pid_t pid;
int alive_only;
PROCESS **procp;
{
register int i;
PROCESS *p;
for (i = 0; i < js.j_jobslots; i++)
{
#if defined (DEBUG)
if (i < js.j_firstj && jobs[i])
itrace("find_job: job %d non-null before js.j_firstj (%d)", i, js.j_firstj);
if (i > js.j_lastj && jobs[i])
itrace("find_job: job %d non-null after js.j_lastj (%d)", i, js.j_lastj);
#endif
if (jobs[i])
{
p = jobs[i]->pipe;
do
{
if (p->pid == pid && ((alive_only == 0 && PRECYCLED(p) == 0) || PALIVE(p)))
{
if (procp)
*procp = p;
return (i);
}
p = p->next;
}
while (p != jobs[i]->pipe);
}
}
return (NO_JOB);
}
int
get_job_by_pid (pid, block)
pid_t pid;
int block;
{
int job;
sigset_t set, oset;
if (block)
BLOCK_CHILD (set, oset);
job = find_job (pid, 0, NULL);
if (block)
UNBLOCK_CHILD (oset);
return job;
}
void
describe_pid (pid)
pid_t pid;
{
int job;
sigset_t set, oset;
BLOCK_CHILD (set, oset);
job = find_job (pid, 0, NULL);
if (job != NO_JOB)
fprintf (stderr, "[%d] %ld\n", job + 1, (long)pid);
else
programming_error (_("describe_pid: %ld: no such pid"), (long)pid);
UNBLOCK_CHILD (oset);
}
static char *
j_strsignal (s)
int s;
{
char *x;
x = strsignal (s);
if (x == 0)
{
x = retcode_name_buffer;
sprintf (x, "Signal %d", s);
}
return x;
}
static char *
printable_job_status (j, p, format)
int j;
PROCESS *p;
int format;
{
static char *temp;
int es;
temp = "Done";
if (STOPPED (j) && format == 0)
{
if (posixly_correct == 0 || p == 0 || (WIFSTOPPED (p->status) == 0))
temp = "Stopped";
else
{
temp = retcode_name_buffer;
sprintf (temp, "Stopped(%s)", signal_name (WSTOPSIG (p->status)));
}
}
else if (RUNNING (j))
temp = "Running";
else
{
if (WIFSTOPPED (p->status))
temp = j_strsignal (WSTOPSIG (p->status));
else if (WIFSIGNALED (p->status))
temp = j_strsignal (WTERMSIG (p->status));
else if (WIFEXITED (p->status))
{
temp = retcode_name_buffer;
es = WEXITSTATUS (p->status);
if (es == 0)
strcpy (temp, "Done");
else if (posixly_correct)
sprintf (temp, "Done(%d)", es);
else
sprintf (temp, "Exit %d", es);
}
else
temp = "Unknown status";
}
return temp;
}
static void
print_pipeline (p, job_index, format, stream)
PROCESS *p;
int job_index, format;
FILE *stream;
{
PROCESS *first, *last, *show;
int es, name_padding;
char *temp;
if (p == 0)
return;
first = last = p;
while (last->next != first)
last = last->next;
for (;;)
{
if (p != first)
fprintf (stream, format ? " " : " |");
if (format != JLIST_STANDARD)
fprintf (stream, "%5ld", (long)p->pid);
fprintf (stream, " ");
if (format > -1 && job_index >= 0)
{
show = format ? p : last;
temp = printable_job_status (job_index, show, format);
if (p != first)
{
if (format)
{
if (show->running == first->running &&
WSTATUS (show->status) == WSTATUS (first->status))
temp = "";
}
else
temp = (char *)NULL;
}
if (temp)
{
fprintf (stream, "%s", temp);
es = STRLEN (temp);
if (es == 0)
es = 2;
name_padding = LONGEST_SIGNAL_DESC - es;
fprintf (stream, "%*s", name_padding, "");
if ((WIFSTOPPED (show->status) == 0) &&
(WIFCONTINUED (show->status) == 0) &&
WIFCORED (show->status))
fprintf (stream, "(core dumped) ");
}
}
if (p != first && format)
fprintf (stream, "| ");
if (p->command)
fprintf (stream, "%s", p->command);
if (p == last && job_index >= 0)
{
temp = current_working_directory ();
if (RUNNING (job_index) && (IS_FOREGROUND (job_index) == 0))
fprintf (stream, " &");
if (strcmp (temp, jobs[job_index]->wd) != 0)
fprintf (stream,
" (wd: %s)", polite_directory_format (jobs[job_index]->wd));
}
if (format || (p == last))
{
if (asynchronous_notification && interactive)
fprintf (stream, "\r\n");
else
fprintf (stream, "\n");
}
if (p == last)
break;
p = p->next;
}
fflush (stream);
}
static void
pretty_print_job (job_index, format, stream)
int job_index, format;
FILE *stream;
{
register PROCESS *p;
if (format == JLIST_PID_ONLY)
{
fprintf (stream, "%ld\n", (long)jobs[job_index]->pipe->pid);
return;
}
if (format == JLIST_CHANGED_ONLY)
{
if (IS_NOTIFIED (job_index))
return;
format = JLIST_STANDARD;
}
if (format != JLIST_NONINTERACTIVE)
fprintf (stream, "[%d]%c ", job_index + 1,
(job_index == js.j_current) ? '+':
(job_index == js.j_previous) ? '-' : ' ');
if (format == JLIST_NONINTERACTIVE)
format = JLIST_LONG;
p = jobs[job_index]->pipe;
print_pipeline (p, job_index, format, stream);
jobs[job_index]->flags |= J_NOTIFIED;
}
static int
print_job (job, format, state, job_index)
JOB *job;
int format, state, job_index;
{
if (state == -1 || (JOB_STATE)state == job->state)
pretty_print_job (job_index, format, stdout);
return (0);
}
void
list_one_job (job, format, ignore, job_index)
JOB *job;
int format, ignore, job_index;
{
pretty_print_job (job_index, format, stdout);
}
void
list_stopped_jobs (format)
int format;
{
cleanup_dead_jobs ();
map_over_jobs (print_job, format, (int)JSTOPPED);
}
void
list_running_jobs (format)
int format;
{
cleanup_dead_jobs ();
map_over_jobs (print_job, format, (int)JRUNNING);
}
void
list_all_jobs (format)
int format;
{
cleanup_dead_jobs ();
map_over_jobs (print_job, format, -1);
}
pid_t
make_child (command, async_p)
char *command;
int async_p;
{
sigset_t set, oset;
pid_t pid;
sigemptyset (&set);
sigaddset (&set, SIGCHLD);
sigaddset (&set, SIGINT);
sigemptyset (&oset);
sigprocmask (SIG_BLOCK, &set, &oset);
making_children ();
#if defined (BUFFERED_INPUT)
if (default_buffered_input != -1 &&
(!async_p || default_buffered_input > 0))
sync_buffered_stream (default_buffered_input);
#endif
if ((pid = fork ()) < 0)
{
sys_error ("fork");
terminate_current_pipeline ();
if (the_pipeline)
kill_current_pipeline ();
throw_to_top_level ();
}
if (pid == 0)
{
pid_t mypid;
mypid = getpid ();
#if defined (BUFFERED_INPUT)
unset_bash_input (0);
#endif
sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
if (job_control)
{
if (pipeline_pgrp == 0)
pipeline_pgrp = mypid;
if (pipeline_pgrp == shell_pgrp)
ignore_tty_job_signals ();
else
default_tty_job_signals ();
if (setpgid (mypid, pipeline_pgrp) < 0)
sys_error ("child setpgid (%ld to %ld)", (long)mypid, (long)pipeline_pgrp);
if (async_p == 0 && pipeline_pgrp != shell_pgrp && ((subshell_environment&SUBSHELL_ASYNC) == 0))
give_terminal_to (pipeline_pgrp, 0);
#if defined (PGRP_PIPE)
if (pipeline_pgrp == mypid)
pipe_read (pgrp_pipe);
#endif
}
else
{
if (pipeline_pgrp == 0)
pipeline_pgrp = shell_pgrp;
default_tty_job_signals ();
}
#if defined (PGRP_PIPE)
pipe_close (pgrp_pipe);
#endif
if (async_p)
last_asynchronous_pid = mypid;
#if defined (RECYCLES_PIDS)
else if (last_asynchronous_pid == mypid)
last_asynchronous_pid = 1;
#endif
}
else
{
if (first_pid == NO_PID)
first_pid = pid;
else if (pid_wrap == -1 && pid < first_pid)
pid_wrap = 0;
else if (pid_wrap == 0 && pid >= first_pid)
pid_wrap = 1;
if (job_control)
{
if (pipeline_pgrp == 0)
{
pipeline_pgrp = pid;
}
setpgid (pid, pipeline_pgrp);
}
else
{
if (pipeline_pgrp == 0)
pipeline_pgrp = shell_pgrp;
}
add_process (command, pid);
if (async_p)
last_asynchronous_pid = pid;
#if defined (RECYCLES_PIDS)
else if (last_asynchronous_pid == pid)
last_asynchronous_pid = 1;
#endif
if (pid_wrap > 0)
delete_old_job (pid);
#if !defined (RECYCLES_PIDS)
if ((js.c_reaped + bgpids.npid) >= js.c_childmax)
#endif
bgp_delete (pid);
last_made_pid = pid;
js.c_totforked++;
js.c_living++;
sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
}
return (pid);
}
void
ignore_tty_job_signals ()
{
set_signal_handler (SIGTSTP, SIG_IGN);
set_signal_handler (SIGTTIN, SIG_IGN);
set_signal_handler (SIGTTOU, SIG_IGN);
}
void
default_tty_job_signals ()
{
set_signal_handler (SIGTSTP, SIG_DFL);
set_signal_handler (SIGTTIN, SIG_DFL);
set_signal_handler (SIGTTOU, SIG_DFL);
}
static TTYSTRUCT shell_tty_info;
#if defined (NEW_TTY_DRIVER)
static struct tchars shell_tchars;
static struct ltchars shell_ltchars;
#endif
#if defined (NEW_TTY_DRIVER) && defined (DRAIN_OUTPUT)
static int ttspeeds[] =
{
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
1800, 2400, 4800, 9600, 19200, 38400
};
static void
draino (fd, ospeed)
int fd, ospeed;
{
register int delay = ttspeeds[ospeed];
int n;
if (!delay)
return;
while ((ioctl (fd, TIOCOUTQ, &n) == 0) && n)
{
if (n > (delay / 100))
{
struct timeval tv;
n *= 10;
tv.tv_sec = n / delay;
tv.tv_usec = ((n % delay) * 1000000) / delay;
select (fd, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv);
}
else
break;
}
}
#endif
#define input_tty() (shell_tty != -1) ? shell_tty : fileno (stderr)
int
get_tty_state ()
{
int tty;
tty = input_tty ();
if (tty != -1)
{
#if defined (NEW_TTY_DRIVER)
ioctl (tty, TIOCGETP, &shell_tty_info);
ioctl (tty, TIOCGETC, &shell_tchars);
ioctl (tty, TIOCGLTC, &shell_ltchars);
#endif
#if defined (TERMIO_TTY_DRIVER)
ioctl (tty, TCGETA, &shell_tty_info);
#endif
#if defined (TERMIOS_TTY_DRIVER)
if (tcgetattr (tty, &shell_tty_info) < 0)
{
#if 0
if (interactive)
sys_error ("[%ld: %d] tcgetattr", (long)getpid (), shell_level);
#endif
return -1;
}
#endif
if (check_window_size)
get_new_window_size (0, (int *)0, (int *)0);
}
return 0;
}
int
set_tty_state ()
{
int tty;
tty = input_tty ();
if (tty != -1)
{
#if defined (NEW_TTY_DRIVER)
# if defined (DRAIN_OUTPUT)
draino (tty, shell_tty_info.sg_ospeed);
# endif
ioctl (tty, TIOCSETN, &shell_tty_info);
ioctl (tty, TIOCSETC, &shell_tchars);
ioctl (tty, TIOCSLTC, &shell_ltchars);
#endif
#if defined (TERMIO_TTY_DRIVER)
ioctl (tty, TCSETAW, &shell_tty_info);
#endif
#if defined (TERMIOS_TTY_DRIVER)
if (tcsetattr (tty, TCSADRAIN, &shell_tty_info) < 0)
{
if (interactive)
sys_error ("[%ld: %d] tcsetattr", (long)getpid (), shell_level);
return -1;
}
#endif
}
return 0;
}
static PROCESS *
find_last_proc (job, block)
int job;
int block;
{
register PROCESS *p;
sigset_t set, oset;
if (block)
BLOCK_CHILD (set, oset);
p = jobs[job]->pipe;
while (p && p->next != jobs[job]->pipe)
p = p->next;
if (block)
UNBLOCK_CHILD (oset);
return (p);
}
static pid_t
find_last_pid (job, block)
int job;
int block;
{
PROCESS *p;
p = find_last_proc (job, block);
return p->pid;
}
int
wait_for_single_pid (pid)
pid_t pid;
{
register PROCESS *child;
sigset_t set, oset;
int r, job;
BLOCK_CHILD (set, oset);
child = find_pipeline (pid, 0, (int *)NULL);
UNBLOCK_CHILD (oset);
if (child == 0)
{
r = bgp_search (pid);
if (r >= 0)
return r;
}
if (child == 0)
{
internal_error (_("wait: pid %ld is not a child of this shell"), (long)pid);
return (127);
}
r = wait_for (pid);
BLOCK_CHILD (set, oset);
job = find_job (pid, 0, NULL);
if (job != NO_JOB && jobs[job] && DEADJOB (job))
jobs[job]->flags |= J_NOTIFIED;
UNBLOCK_CHILD (oset);
if (posixly_correct)
{
cleanup_dead_jobs ();
bgp_delete (pid);
}
return r;
}
void
wait_for_background_pids ()
{
register int i, r, waited_for;
sigset_t set, oset;
pid_t pid;
for (waited_for = 0;;)
{
BLOCK_CHILD (set, oset);
for (i = 0; i < js.j_jobslots; i++)
{
#if defined (DEBUG)
if (i < js.j_firstj && jobs[i])
itrace("wait_for_background_pids: job %d non-null before js.j_firstj (%d)", i, js.j_firstj);
if (i > js.j_lastj && jobs[i])
itrace("wait_for_background_pids: job %d non-null after js.j_lastj (%d)", i, js.j_lastj);
#endif
if (jobs[i] && RUNNING (i) && IS_FOREGROUND (i) == 0)
break;
}
if (i == js.j_jobslots)
{
UNBLOCK_CHILD (oset);
break;
}
pid = find_last_pid (i, 0);
UNBLOCK_CHILD (oset);
QUIT;
errno = 0;
r = wait_for_single_pid (pid);
if (r == -1)
{
if (errno == ECHILD)
mark_all_jobs_as_dead ();
}
else
waited_for++;
}
mark_dead_jobs_as_notified (1);
cleanup_dead_jobs ();
bgp_clear ();
}
#define INVALID_SIGNAL_HANDLER (SigHandler *)wait_for_background_pids
static SigHandler *old_sigint_handler = INVALID_SIGNAL_HANDLER;
static void
restore_sigint_handler ()
{
if (old_sigint_handler != INVALID_SIGNAL_HANDLER)
{
set_signal_handler (SIGINT, old_sigint_handler);
old_sigint_handler = INVALID_SIGNAL_HANDLER;
}
}
static int wait_sigint_received;
static sighandler
wait_sigint_handler (sig)
int sig;
{
SigHandler *sigint_handler;
if (interrupt_immediately ||
(this_shell_builtin && this_shell_builtin == wait_builtin))
{
last_command_exit_value = EXECUTION_FAILURE;
restore_sigint_handler ();
if (this_shell_builtin && this_shell_builtin == wait_builtin &&
signal_is_trapped (SIGINT) &&
((sigint_handler = trap_to_sighandler (SIGINT)) == trap_handler))
{
interrupt_immediately = 0;
trap_handler (SIGINT);
wait_signal_received = SIGINT;
longjmp (wait_intr_buf, 1);
}
ADDINTERRUPT;
QUIT;
}
wait_sigint_received = 1;
SIGRETURN (0);
}
static int
process_exit_signal (status)
WAIT status;
{
return (WIFSIGNALED (status) ? WTERMSIG (status) : 0);
}
static int
process_exit_status (status)
WAIT status;
{
if (WIFSIGNALED (status))
return (128 + WTERMSIG (status));
else if (WIFSTOPPED (status) == 0)
return (WEXITSTATUS (status));
else
return (EXECUTION_SUCCESS);
}
static WAIT
job_signal_status (job)
int job;
{
register PROCESS *p;
WAIT s;
p = jobs[job]->pipe;
do
{
s = p->status;
if (WIFSIGNALED(s) || WIFSTOPPED(s))
break;
p = p->next;
}
while (p != jobs[job]->pipe);
return s;
}
static WAIT
raw_job_exit_status (job)
int job;
{
register PROCESS *p;
int fail;
if (pipefail_opt)
{
fail = 0;
p = jobs[job]->pipe;
do
{
if (p->status != EXECUTION_SUCCESS) fail = p->status;
p = p->next;
}
while (p != jobs[job]->pipe);
return fail;
}
for (p = jobs[job]->pipe; p->next != jobs[job]->pipe; p = p->next)
;
return (p->status);
}
static int
job_exit_status (job)
int job;
{
return (process_exit_status (raw_job_exit_status (job)));
}
static int
job_exit_signal (job)
int job;
{
return (process_exit_signal (raw_job_exit_status (job)));
}
#define FIND_CHILD(pid, child) \
do \
{ \
child = find_pipeline (pid, 0, (int *)NULL); \
if (child == 0) \
{ \
give_terminal_to (shell_pgrp, 0); \
UNBLOCK_CHILD (oset); \
internal_error (_("wait_for: No record of process %ld"), (long)pid); \
restore_sigint_handler (); \
return (termination_state = 127); \
} \
} \
while (0)
int
wait_for (pid)
pid_t pid;
{
int job, termination_state, r;
WAIT s;
register PROCESS *child;
sigset_t set, oset;
BLOCK_CHILD (set, oset);
wait_sigint_received = 0;
if (job_control == 0 || (subshell_environment&SUBSHELL_COMSUB))
{
old_sigint_handler = set_signal_handler (SIGINT, wait_sigint_handler);
if (old_sigint_handler == SIG_IGN)
set_signal_handler (SIGINT, old_sigint_handler);
}
termination_state = last_command_exit_value;
if (interactive && job_control == 0)
QUIT;
job = NO_JOB;
do
{
FIND_CHILD (pid, child);
if (job == NO_JOB)
job = find_job (pid, 0, NULL);
if (PRUNNING(child) || (job != NO_JOB && RUNNING (job)))
{
#if defined (WAITPID_BROKEN)
sigset_t suspend_set;
sigemptyset (&suspend_set);
sigsuspend (&suspend_set);
#else
# if defined (MUST_UNBLOCK_CHLD)
struct sigaction act, oact;
sigset_t nullset, chldset;
sigemptyset (&nullset);
sigemptyset (&chldset);
sigprocmask (SIG_SETMASK, &nullset, &chldset);
act.sa_handler = SIG_DFL;
sigemptyset (&act.sa_mask);
sigemptyset (&oact.sa_mask);
act.sa_flags = 0;
sigaction (SIGCHLD, &act, &oact);
# endif
queue_sigchld = 1;
r = waitchld (pid, 1);
# if defined (MUST_UNBLOCK_CHLD)
sigaction (SIGCHLD, &oact, (struct sigaction *)NULL);
sigprocmask (SIG_SETMASK, &chldset, (sigset_t *)NULL);
# endif
queue_sigchld = 0;
if (r == -1 && errno == ECHILD && this_shell_builtin == wait_builtin)
{
termination_state = -1;
goto wait_for_return;
}
if (r == -1 && errno == ECHILD)
{
child->running = PS_DONE;
child->status = 0;
js.c_living = 0;
if (job != NO_JOB)
{
jobs[job]->state = JDEAD;
js.c_reaped++;
js.j_ndead++;
}
}
#endif
}
if (interactive && job_control == 0)
QUIT;
}
while (PRUNNING (child) || (job != NO_JOB && RUNNING (job)));
termination_state = (job != NO_JOB) ? job_exit_status (job)
: process_exit_status (child->status);
last_command_exit_signal = (job != NO_JOB) ? job_exit_signal (job)
: process_exit_signal (child->status);
if ((job != NO_JOB && JOBSTATE (job) == JSTOPPED) || WIFSTOPPED (child->status))
termination_state = 128 + WSTOPSIG (child->status);
if (job == NO_JOB || IS_JOBCONTROL (job))
{
#if 0
if (job == NO_JOB)
itrace("wait_for: job == NO_JOB, giving the terminal to shell_pgrp (%ld)", (long)shell_pgrp);
#endif
give_terminal_to (shell_pgrp, 0);
}
if (job != NO_JOB)
{
if (interactive_shell && subshell_environment == 0)
{
s = job_signal_status (job);
if (WIFSIGNALED (s) || WIFSTOPPED (s))
{
set_tty_state ();
if (check_window_size && (job == js.j_current || IS_FOREGROUND (job)))
get_new_window_size (0, (int *)0, (int *)0);
}
else
get_tty_state ();
if (job_control && IS_JOBCONTROL (job) && IS_FOREGROUND (job) &&
WIFSIGNALED (s) && WTERMSIG (s) == SIGINT)
{
if (signal_is_trapped (SIGINT) == 0 && loop_level)
ADDINTERRUPT;
else
{
putchar ('\n');
fflush (stdout);
}
}
}
else if ((subshell_environment & SUBSHELL_COMSUB) && wait_sigint_received)
{
s = job_signal_status (job);
if (WIFSIGNALED (s) && WTERMSIG (s) == SIGINT && signal_is_trapped (SIGINT) == 0)
{
UNBLOCK_CHILD (oset);
restore_sigint_handler ();
old_sigint_handler = set_signal_handler (SIGINT, SIG_DFL);
if (old_sigint_handler == SIG_IGN)
restore_sigint_handler ();
else
kill (getpid (), SIGINT);
}
}
if (DEADJOB (job) && IS_FOREGROUND (job) )
setjstatus (job);
notify_and_cleanup ();
}
wait_for_return:
UNBLOCK_CHILD (oset);
restore_sigint_handler ();
return (termination_state);
}
int
wait_for_job (job)
int job;
{
pid_t pid;
int r;
sigset_t set, oset;
BLOCK_CHILD(set, oset);
if (JOBSTATE (job) == JSTOPPED)
internal_warning (_("wait_for_job: job %d is stopped"), job+1);
pid = find_last_pid (job, 0);
UNBLOCK_CHILD(oset);
r = wait_for (pid);
BLOCK_CHILD (set, oset);
if (job != NO_JOB && jobs[job] && DEADJOB (job))
jobs[job]->flags |= J_NOTIFIED;
UNBLOCK_CHILD (oset);
return r;
}
void
notify_and_cleanup ()
{
if (jobs_list_frozen)
return;
if (interactive || interactive_shell == 0 || sourcelevel)
notify_of_job_status ();
cleanup_dead_jobs ();
}
void
reap_dead_jobs ()
{
mark_dead_jobs_as_notified (0);
cleanup_dead_jobs ();
}
static int
most_recent_job_in_state (job, state)
int job;
JOB_STATE state;
{
register int i, result;
sigset_t set, oset;
BLOCK_CHILD (set, oset);
for (result = NO_JOB, i = job - 1; i >= 0; i--)
{
if (jobs[i] && (JOBSTATE (i) == state))
{
result = i;
break;
}
}
UNBLOCK_CHILD (oset);
return (result);
}
static int
job_last_stopped (job)
int job;
{
return (most_recent_job_in_state (job, JSTOPPED));
}
static int
job_last_running (job)
int job;
{
return (most_recent_job_in_state (job, JRUNNING));
}
static void
set_current_job (job)
int job;
{
int candidate;
if (js.j_current != job)
{
js.j_previous = js.j_current;
js.j_current = job;
}
if (js.j_previous != js.j_current &&
js.j_previous != NO_JOB &&
jobs[js.j_previous] &&
STOPPED (js.j_previous))
return;
candidate = NO_JOB;
if (STOPPED (js.j_current))
{
candidate = job_last_stopped (js.j_current);
if (candidate != NO_JOB)
{
js.j_previous = candidate;
return;
}
}
candidate = RUNNING (js.j_current) ? job_last_running (js.j_current)
: job_last_running (js.j_jobslots);
if (candidate != NO_JOB)
{
js.j_previous = candidate;
return;
}
js.j_previous = js.j_current;
}
static void
reset_current ()
{
int candidate;
if (js.j_jobslots && js.j_current != NO_JOB && jobs[js.j_current] && STOPPED (js.j_current))
candidate = js.j_current;
else
{
candidate = NO_JOB;
if (js.j_previous != NO_JOB && jobs[js.j_previous] && STOPPED (js.j_previous))
candidate = js.j_previous;
if (candidate == NO_JOB)
candidate = job_last_stopped (js.j_jobslots);
if (candidate == NO_JOB)
candidate = job_last_running (js.j_jobslots);
}
if (candidate != NO_JOB)
set_current_job (candidate);
else
js.j_current = js.j_previous = NO_JOB;
}
static void
set_job_running (job)
int job;
{
register PROCESS *p;
p = jobs[job]->pipe;
do
{
if (WIFSTOPPED (p->status))
p->running = PS_RUNNING;
p = p->next;
}
while (p != jobs[job]->pipe);
JOBSTATE (job) = JRUNNING;
}
int
start_job (job, foreground)
int job, foreground;
{
register PROCESS *p;
int already_running;
sigset_t set, oset;
char *wd, *s;
static TTYSTRUCT save_stty;
BLOCK_CHILD (set, oset);
if (DEADJOB (job))
{
internal_error (_("%s: job has terminated"), this_command_name);
UNBLOCK_CHILD (oset);
return (-1);
}
already_running = RUNNING (job);
if (foreground == 0 && already_running)
{
internal_error (_("%s: job %d already in background"), this_command_name, job + 1);
UNBLOCK_CHILD (oset);
return (0);
}
wd = current_working_directory ();
jobs[job]->flags &= ~J_NOTIFIED;
if (foreground)
{
set_current_job (job);
jobs[job]->flags |= J_FOREGROUND;
}
p = jobs[job]->pipe;
if (foreground == 0)
{
if (posixly_correct == 0)
s = (job == js.j_current) ? "+ ": ((job == js.j_previous) ? "- " : " ");
else
s = " ";
printf ("[%d]%s", job + 1, s);
}
do
{
printf ("%s%s",
p->command ? p->command : "",
p->next != jobs[job]->pipe? " | " : "");
p = p->next;
}
while (p != jobs[job]->pipe);
if (foreground == 0)
printf (" &");
if (strcmp (wd, jobs[job]->wd) != 0)
printf (" (wd: %s)", polite_directory_format (jobs[job]->wd));
printf ("\n");
if (already_running == 0)
set_job_running (job);
if (foreground)
{
get_tty_state ();
save_stty = shell_tty_info;
if (IS_JOBCONTROL (job))
give_terminal_to (jobs[job]->pgrp, 0);
}
else
jobs[job]->flags &= ~J_FOREGROUND;
if (already_running == 0)
{
jobs[job]->flags |= J_NOTIFIED;
killpg (jobs[job]->pgrp, SIGCONT);
}
if (foreground)
{
pid_t pid;
int st;
pid = find_last_pid (job, 0);
UNBLOCK_CHILD (oset);
st = wait_for (pid);
shell_tty_info = save_stty;
set_tty_state ();
return (st);
}
else
{
reset_current ();
UNBLOCK_CHILD (oset);
return (0);
}
}
int
kill_pid (pid, sig, group)
pid_t pid;
int sig, group;
{
register PROCESS *p;
int job, result, negative;
sigset_t set, oset;
if (pid < -1)
{
pid = -pid;
group = negative = 1;
}
else
negative = 0;
result = EXECUTION_SUCCESS;
if (group)
{
BLOCK_CHILD (set, oset);
p = find_pipeline (pid, 0, &job);
if (job != NO_JOB)
{
jobs[job]->flags &= ~J_NOTIFIED;
if (negative && jobs[job]->pgrp == shell_pgrp)
result = killpg (pid, sig);
else if (jobs[job]->pgrp == shell_pgrp)
{
p = jobs[job]->pipe;
do
{
if (PALIVE (p) == 0)
continue;
kill (p->pid, sig);
if (PEXITED (p) && (sig == SIGTERM || sig == SIGHUP))
kill (p->pid, SIGCONT);
p = p->next;
}
while (p != jobs[job]->pipe);
}
else
{
result = killpg (jobs[job]->pgrp, sig);
if (p && STOPPED (job) && (sig == SIGTERM || sig == SIGHUP))
killpg (jobs[job]->pgrp, SIGCONT);
if (p && STOPPED (job) && (sig == SIGCONT))
{
set_job_running (job);
jobs[job]->flags &= ~J_FOREGROUND;
jobs[job]->flags |= J_NOTIFIED;
}
}
}
else
result = killpg (pid, sig);
UNBLOCK_CHILD (oset);
}
else
result = kill (pid, sig);
return (result);
}
static sighandler
sigchld_handler (sig)
int sig;
{
int n, oerrno;
oerrno = errno;
REINSTALL_SIGCHLD_HANDLER;
sigchld++;
n = 0;
if (queue_sigchld == 0)
n = waitchld (-1, 0);
errno = oerrno;
SIGRETURN (n);
}
static int
waitchld (wpid, block)
pid_t wpid;
int block;
{
WAIT status;
PROCESS *child;
pid_t pid;
int call_set_current, last_stopped_job, job, children_exited, waitpid_flags;
static int wcontinued = WCONTINUED;
call_set_current = children_exited = 0;
last_stopped_job = NO_JOB;
do
{
waitpid_flags = (job_control && subshell_environment == 0)
? (WUNTRACED|wcontinued)
: 0;
if (sigchld || block == 0)
waitpid_flags |= WNOHANG;
CHECK_TERMSIG;
pid = WAITPID (-1, &status, waitpid_flags);
if (wcontinued && pid < 0 && errno == EINVAL)
{
wcontinued = 0;
continue;
}
if (sigchld > 0 && (waitpid_flags & WNOHANG))
sigchld--;
if (pid < 0 && errno == ECHILD)
{
if (children_exited == 0)
return -1;
else
break;
}
CHECK_TERMSIG;
if (pid <= 0)
continue;
if (WIFCONTINUED(status) == 0)
{
children_exited++;
js.c_living--;
}
child = find_process (pid, 1, &job);
if (child == 0)
continue;
child->status = status;
child->running = WIFCONTINUED(status) ? PS_RUNNING : PS_DONE;
if (PEXITED (child))
{
js.c_totreaped++;
if (job != NO_JOB)
js.c_reaped++;
}
if (job == NO_JOB)
continue;
call_set_current += set_job_status_and_cleanup (job);
if (STOPPED (job))
last_stopped_job = job;
else if (DEADJOB (job) && last_stopped_job == job)
last_stopped_job = NO_JOB;
}
while ((sigchld || block == 0) && pid > (pid_t)0);
if (call_set_current)
{
if (last_stopped_job != NO_JOB)
set_current_job (last_stopped_job);
else
reset_current ();
}
if (job_control && signal_is_trapped (SIGCHLD) && children_exited &&
trap_list[SIGCHLD] != (char *)IGNORE_SIG)
run_sigchld_trap (children_exited);
if (asynchronous_notification && interactive)
notify_of_job_status ();
return (children_exited);
}
static int
set_job_status_and_cleanup (job)
int job;
{
PROCESS *child;
int tstatus, job_state, any_stopped, any_tstped, call_set_current;
SigHandler *temp_handler;
child = jobs[job]->pipe;
jobs[job]->flags &= ~J_NOTIFIED;
call_set_current = 0;
job_state = any_stopped = any_tstped = 0;
do
{
job_state |= PRUNNING (child);
#if 0
if (PEXITED (child) && (WIFSTOPPED (child->status)))
#else
if (PSTOPPED (child))
#endif
{
any_stopped = 1;
any_tstped |= interactive && job_control &&
(WSTOPSIG (child->status) == SIGTSTP);
}
child = child->next;
}
while (child != jobs[job]->pipe);
if (job_state != 0 && JOBSTATE(job) != JSTOPPED)
return 0;
if (any_stopped)
{
jobs[job]->state = JSTOPPED;
jobs[job]->flags &= ~J_FOREGROUND;
call_set_current++;
if (any_tstped && loop_level)
breaking = loop_level;
}
else if (job_state != 0)
{
jobs[job]->state = JRUNNING;
call_set_current++;
}
else
{
jobs[job]->state = JDEAD;
js.j_ndead++;
#if 0
if (IS_FOREGROUND (job))
setjstatus (job);
#endif
if (jobs[job]->j_cleanup)
{
(*jobs[job]->j_cleanup) (jobs[job]->cleanarg);
jobs[job]->j_cleanup = (sh_vptrfunc_t *)NULL;
}
}
if (JOBSTATE (job) == JDEAD)
{
if (wait_sigint_received && interactive_shell == 0 &&
WIFSIGNALED (child->status) == 0 && IS_FOREGROUND (job) &&
signal_is_trapped (SIGINT))
{
int old_frozen;
wait_sigint_received = 0;
last_command_exit_value = process_exit_status (child->status);
old_frozen = jobs_list_frozen;
jobs_list_frozen = 1;
tstatus = maybe_call_trap_handler (SIGINT);
jobs_list_frozen = old_frozen;
}
else if (wait_sigint_received && (WTERMSIG (child->status) == SIGINT) &&
IS_FOREGROUND (job) && IS_JOBCONTROL (job) == 0)
{
int old_frozen;
wait_sigint_received = 0;
if (signal_is_trapped (SIGINT))
last_command_exit_value = process_exit_status (child->status);
old_frozen = jobs_list_frozen;
jobs_list_frozen = 1;
tstatus = maybe_call_trap_handler (SIGINT);
jobs_list_frozen = old_frozen;
if (tstatus == 0 && old_sigint_handler != INVALID_SIGNAL_HANDLER)
{
temp_handler = old_sigint_handler;
if (temp_handler == trap_handler && signal_is_trapped (SIGINT) == 0)
temp_handler = trap_to_sighandler (SIGINT);
restore_sigint_handler ();
if (temp_handler == SIG_DFL)
termsig_handler (SIGINT);
else if (temp_handler != SIG_IGN)
(*temp_handler) (SIGINT);
}
}
}
return call_set_current;
}
static void
setjstatus (j)
int j;
{
#if defined (ARRAY_VARS)
register int i;
register PROCESS *p;
for (i = 1, p = jobs[j]->pipe; p->next != jobs[j]->pipe; p = p->next, i++)
;
i++;
if (statsize < i)
{
pstatuses = (int *)xrealloc (pstatuses, i * sizeof (int));
statsize = i;
}
i = 0;
p = jobs[j]->pipe;
do
{
pstatuses[i++] = process_exit_status (p->status);
p = p->next;
}
while (p != jobs[j]->pipe);
pstatuses[i] = -1;
set_pipestatus_array (pstatuses, i);
#endif
}
static void
run_sigchld_trap (nchild)
int nchild;
{
char *trap_command;
int i;
trap_command = savestring (trap_list[SIGCHLD]);
begin_unwind_frame ("SIGCHLD trap");
unwind_protect_int (last_command_exit_value);
unwind_protect_int (last_command_exit_signal);
unwind_protect_var (last_made_pid);
unwind_protect_int (interrupt_immediately);
unwind_protect_int (jobs_list_frozen);
unwind_protect_pointer (the_pipeline);
unwind_protect_pointer (subst_assign_varlist);
add_unwind_protect (xfree, trap_command);
add_unwind_protect (maybe_set_sigchld_trap, trap_command);
subst_assign_varlist = (WORD_LIST *)NULL;
the_pipeline = (PROCESS *)NULL;
restore_default_signal (SIGCHLD);
jobs_list_frozen = 1;
for (i = 0; i < nchild; i++)
{
interrupt_immediately = 1;
parse_and_execute (savestring (trap_command), "trap", SEVAL_NOHIST|SEVAL_RESETLINE);
}
run_unwind_frame ("SIGCHLD trap");
}
static void
notify_of_job_status ()
{
register int job, termsig;
char *dir;
sigset_t set, oset;
WAIT s;
if (jobs == 0 || js.j_jobslots == 0)
return;
if (old_ttou != 0)
{
sigemptyset (&set);
sigaddset (&set, SIGCHLD);
sigaddset (&set, SIGTTOU);
sigemptyset (&oset);
sigprocmask (SIG_BLOCK, &set, &oset);
}
else
queue_sigchld++;
for (job = 0, dir = (char *)NULL; job < js.j_jobslots; job++)
{
if (jobs[job] && IS_NOTIFIED (job) == 0)
{
s = raw_job_exit_status (job);
termsig = WTERMSIG (s);
if (startup_state == 0 && WIFSIGNALED (s) == 0 &&
((DEADJOB (job) && IS_FOREGROUND (job) == 0) || STOPPED (job)))
continue;
#if 0
if ((job_control == 0 && interactive_shell) || startup_state == 2)
#else
if ((job_control == 0 && interactive_shell) ||
(startup_state == 2 && (subshell_environment & SUBSHELL_COMSUB)))
#endif
{
if (DEADJOB (job) && (interactive_shell || (find_last_pid (job, 0) != last_asynchronous_pid)))
jobs[job]->flags |= J_NOTIFIED;
continue;
}
switch (JOBSTATE (job))
{
case JDEAD:
if (interactive_shell == 0 && termsig && WIFSIGNALED (s) &&
termsig != SIGINT &&
#if defined (DONT_REPORT_SIGPIPE)
termsig != SIGPIPE &&
#endif
signal_is_trapped (termsig) == 0)
{
fprintf (stderr, "%s: line %d: ", get_name_for_error (), (line_number == 0) ? 1 : line_number);
pretty_print_job (job, JLIST_NONINTERACTIVE, stderr);
}
else if (IS_FOREGROUND (job))
{
#if !defined (DONT_REPORT_SIGPIPE)
if (termsig && WIFSIGNALED (s) && termsig != SIGINT)
#else
if (termsig && WIFSIGNALED (s) && termsig != SIGINT && termsig != SIGPIPE)
#endif
{
fprintf (stderr, "%s", j_strsignal (termsig));
if (WIFCORED (s))
fprintf (stderr, " (core dumped)");
fprintf (stderr, "\n");
}
}
else if (job_control)
{
if (dir == 0)
dir = current_working_directory ();
pretty_print_job (job, JLIST_STANDARD, stderr);
if (dir && strcmp (dir, jobs[job]->wd) != 0)
fprintf (stderr,
"(wd now: %s)\n", polite_directory_format (dir));
}
jobs[job]->flags |= J_NOTIFIED;
break;
case JSTOPPED:
fprintf (stderr, "\n");
if (dir == 0)
dir = current_working_directory ();
pretty_print_job (job, JLIST_STANDARD, stderr);
if (dir && (strcmp (dir, jobs[job]->wd) != 0))
fprintf (stderr,
"(wd now: %s)\n", polite_directory_format (dir));
jobs[job]->flags |= J_NOTIFIED;
break;
case JRUNNING:
case JMIXED:
break;
default:
programming_error ("notify_of_job_status");
}
}
}
if (old_ttou != 0)
sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
else
queue_sigchld--;
}
int
initialize_job_control (force)
int force;
{
shell_pgrp = getpgid (0);
if (shell_pgrp == -1)
{
sys_error ("initialize_job_control: getpgrp failed");
exit (1);
}
if (interactive == 0)
{
job_control = 0;
original_pgrp = NO_PID;
shell_tty = fileno (stderr);
}
else
{
shell_tty = dup (fileno (stderr));
shell_tty = move_to_high_fd (shell_tty, 1, -1);
if (shell_pgrp == 0)
{
shell_pgrp = getpid ();
setpgid (0, shell_pgrp);
tcsetpgrp (shell_tty, shell_pgrp);
}
while ((terminal_pgrp = tcgetpgrp (shell_tty)) != -1)
{
if (shell_pgrp != terminal_pgrp)
{
SigHandler *ottin;
ottin = set_signal_handler(SIGTTIN, SIG_DFL);
kill (0, SIGTTIN);
set_signal_handler (SIGTTIN, ottin);
continue;
}
break;
}
if (set_new_line_discipline (shell_tty) < 0)
{
sys_error ("initialize_job_control: line discipline");
job_control = 0;
}
else
{
original_pgrp = shell_pgrp;
shell_pgrp = getpid ();
if ((original_pgrp != shell_pgrp) && (setpgid (0, shell_pgrp) < 0))
{
sys_error ("initialize_job_control: setpgid");
shell_pgrp = original_pgrp;
}
job_control = 1;
if (shell_pgrp != original_pgrp && shell_pgrp != terminal_pgrp)
{
if (give_terminal_to (shell_pgrp, 0) < 0)
{
setpgid (0, original_pgrp);
shell_pgrp = original_pgrp;
job_control = 0;
}
}
}
if (job_control == 0)
internal_error (_("no job control in this shell"));
}
if (shell_tty != fileno (stderr))
SET_CLOSE_ON_EXEC (shell_tty);
set_signal_handler (SIGCHLD, sigchld_handler);
change_flag ('m', job_control ? '-' : '+');
if (interactive)
get_tty_state ();
if (js.c_childmax < 0)
js.c_childmax = getmaxchild ();
if (js.c_childmax < 0)
js.c_childmax = DEFAULT_CHILD_MAX;
return job_control;
}
#ifdef DEBUG
void
debug_print_pgrps ()
{
itrace("original_pgrp = %ld shell_pgrp = %ld terminal_pgrp = %ld",
(long)original_pgrp, (long)shell_pgrp, (long)terminal_pgrp);
itrace("tcgetpgrp(%d) -> %ld, getpgid(0) -> %ld",
shell_tty, (long)tcgetpgrp (shell_tty), (long)getpgid(0));
}
#endif
static int
set_new_line_discipline (tty)
int tty;
{
#if defined (NEW_TTY_DRIVER)
int ldisc;
if (ioctl (tty, TIOCGETD, &ldisc) < 0)
return (-1);
if (ldisc != NTTYDISC)
{
ldisc = NTTYDISC;
if (ioctl (tty, TIOCSETD, &ldisc) < 0)
return (-1);
}
return (0);
#endif
#if defined (TERMIO_TTY_DRIVER)
# if defined (TERMIO_LDISC) && (NTTYDISC)
if (ioctl (tty, TCGETA, &shell_tty_info) < 0)
return (-1);
if (shell_tty_info.c_line != NTTYDISC)
{
shell_tty_info.c_line = NTTYDISC;
if (ioctl (tty, TCSETAW, &shell_tty_info) < 0)
return (-1);
}
# endif
return (0);
#endif
#if defined (TERMIOS_TTY_DRIVER)
# if defined (TERMIOS_LDISC) && defined (NTTYDISC)
if (tcgetattr (tty, &shell_tty_info) < 0)
return (-1);
if (shell_tty_info.c_line != NTTYDISC)
{
shell_tty_info.c_line = NTTYDISC;
if (tcsetattr (tty, TCSADRAIN, &shell_tty_info) < 0)
return (-1);
}
# endif
return (0);
#endif
#if !defined (NEW_TTY_DRIVER) && !defined (TERMIO_TTY_DRIVER) && !defined (TERMIOS_TTY_DRIVER)
return (-1);
#endif
}
void
initialize_job_signals ()
{
if (interactive)
{
set_signal_handler (SIGINT, sigint_sighandler);
set_signal_handler (SIGTSTP, SIG_IGN);
set_signal_handler (SIGTTOU, SIG_IGN);
set_signal_handler (SIGTTIN, SIG_IGN);
}
else if (job_control)
{
old_tstp = set_signal_handler (SIGTSTP, sigstop_sighandler);
old_ttin = set_signal_handler (SIGTTIN, sigstop_sighandler);
old_ttou = set_signal_handler (SIGTTOU, sigstop_sighandler);
}
}
static sighandler
sigcont_sighandler (sig)
int sig;
{
initialize_job_signals ();
set_signal_handler (SIGCONT, old_cont);
kill (getpid (), SIGCONT);
SIGRETURN (0);
}
static sighandler
sigstop_sighandler (sig)
int sig;
{
set_signal_handler (SIGTSTP, old_tstp);
set_signal_handler (SIGTTOU, old_ttou);
set_signal_handler (SIGTTIN, old_ttin);
old_cont = set_signal_handler (SIGCONT, sigcont_sighandler);
give_terminal_to (shell_pgrp, 0);
kill (getpid (), sig);
SIGRETURN (0);
}
int
give_terminal_to (pgrp, force)
pid_t pgrp;
int force;
{
sigset_t set, oset;
int r;
r = 0;
if (job_control || force)
{
sigemptyset (&set);
sigaddset (&set, SIGTTOU);
sigaddset (&set, SIGTTIN);
sigaddset (&set, SIGTSTP);
sigaddset (&set, SIGCHLD);
sigemptyset (&oset);
sigprocmask (SIG_BLOCK, &set, &oset);
if (tcsetpgrp (shell_tty, pgrp) < 0)
{
#if 0
sys_error ("tcsetpgrp(%d) failed: pid %ld to pgrp %ld",
shell_tty, (long)getpid(), (long)pgrp);
#endif
r = -1;
}
else
terminal_pgrp = pgrp;
sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
}
return r;
}
void
delete_all_jobs (running_only)
int running_only;
{
register int i;
sigset_t set, oset;
BLOCK_CHILD (set, oset);
if (js.j_jobslots)
{
js.j_current = js.j_previous = NO_JOB;
for (i = 0; i < js.j_jobslots; i++)
{
#if defined (DEBUG)
if (i < js.j_firstj && jobs[i])
itrace("delete_all_jobs: job %d non-null before js.j_firstj (%d)", i, js.j_firstj);
if (i > js.j_lastj && jobs[i])
itrace("delete_all_jobs: job %d non-null after js.j_lastj (%d)", i, js.j_lastj);
#endif
if (jobs[i] && (running_only == 0 || (running_only && RUNNING(i))))
delete_job (i, DEL_WARNSTOPPED);
}
if (running_only == 0)
{
free ((char *)jobs);
js.j_jobslots = 0;
js.j_firstj = js.j_lastj = js.j_njobs = 0;
}
}
if (running_only == 0)
bgp_clear ();
UNBLOCK_CHILD (oset);
}
void
nohup_all_jobs (running_only)
int running_only;
{
register int i;
sigset_t set, oset;
BLOCK_CHILD (set, oset);
if (js.j_jobslots)
{
for (i = 0; i < js.j_jobslots; i++)
if (jobs[i] && (running_only == 0 || (running_only && RUNNING(i))))
nohup_job (i);
}
UNBLOCK_CHILD (oset);
}
int
count_all_jobs ()
{
int i, n;
sigset_t set, oset;
BLOCK_CHILD (set, oset);
for (i = n = 0; i < js.j_jobslots; i++)
{
#if defined (DEBUG)
if (i < js.j_firstj && jobs[i])
itrace("count_all_jobs: job %d non-null before js.j_firstj (%d)", i, js.j_firstj);
if (i > js.j_lastj && jobs[i])
itrace("count_all_jobs: job %d non-null after js.j_lastj (%d)", i, js.j_lastj);
#endif
if (jobs[i] && DEADJOB(i) == 0)
n++;
}
UNBLOCK_CHILD (oset);
return n;
}
static void
mark_all_jobs_as_dead ()
{
register int i;
sigset_t set, oset;
if (js.j_jobslots == 0)
return;
BLOCK_CHILD (set, oset);
for (i = 0; i < js.j_jobslots; i++)
if (jobs[i])
{
jobs[i]->state = JDEAD;
js.j_ndead++;
}
UNBLOCK_CHILD (oset);
}
static void
mark_dead_jobs_as_notified (force)
int force;
{
register int i, ndead, ndeadproc;
sigset_t set, oset;
if (js.j_jobslots == 0)
return;
BLOCK_CHILD (set, oset);
if (force)
{
for (i = 0; i < js.j_jobslots; i++)
{
if (jobs[i] && DEADJOB (i) && (interactive_shell || (find_last_pid (i, 0) != last_asynchronous_pid)))
jobs[i]->flags |= J_NOTIFIED;
}
UNBLOCK_CHILD (oset);
return;
}
for (i = ndead = ndeadproc = 0; i < js.j_jobslots; i++)
{
#if defined (DEBUG)
if (i < js.j_firstj && jobs[i])
itrace("mark_dead_jobs_as_notified: job %d non-null before js.j_firstj (%d)", i, js.j_firstj);
if (i > js.j_lastj && jobs[i])
itrace("mark_dead_jobs_as_notified: job %d non-null after js.j_lastj (%d)", i, js.j_lastj);
#endif
if (jobs[i] && DEADJOB (i))
{
ndead++;
ndeadproc += processes_in_job (i);
}
}
#ifdef DEBUG
if (ndeadproc != js.c_reaped)
itrace("mark_dead_jobs_as_notified: ndeadproc (%d) != js.c_reaped (%d)", ndeadproc, js.c_reaped);
if (ndead != js.j_ndead)
itrace("mark_dead_jobs_as_notified: ndead (%d) != js.j_ndead (%d)", ndead, js.j_ndead);
#endif
if (js.c_childmax < 0)
js.c_childmax = getmaxchild ();
if (js.c_childmax < 0)
js.c_childmax = DEFAULT_CHILD_MAX;
if (ndeadproc <= js.c_childmax)
{
UNBLOCK_CHILD (oset);
return;
}
#if 0
itrace("mark_dead_jobs_as_notified: child_max = %d ndead = %d ndeadproc = %d", js.c_childmax, ndead, ndeadproc);
#endif
for (i = 0; i < js.j_jobslots; i++)
{
if (jobs[i] && DEADJOB (i) && (interactive_shell || (find_last_pid (i, 0) != last_asynchronous_pid)))
{
#if defined (DEBUG)
if (i < js.j_firstj && jobs[i])
itrace("mark_dead_jobs_as_notified: job %d non-null before js.j_firstj (%d)", i, js.j_firstj);
if (i > js.j_lastj && jobs[i])
itrace("mark_dead_jobs_as_notified: job %d non-null after js.j_lastj (%d)", i, js.j_lastj);
#endif
if ((ndeadproc -= processes_in_job (i)) <= js.c_childmax)
break;
jobs[i]->flags |= J_NOTIFIED;
}
}
UNBLOCK_CHILD (oset);
}
void
unfreeze_jobs_list ()
{
jobs_list_frozen = 0;
}
int
set_job_control (arg)
int arg;
{
int old;
old = job_control;
job_control = arg;
if (job_control != old && job_control)
pipeline_pgrp = 0;
return (old);
}
void
without_job_control ()
{
stop_making_children ();
start_pipeline ();
#if defined (PGRP_PIPE)
pipe_close (pgrp_pipe);
#endif
delete_all_jobs (0);
set_job_control (0);
}
void
end_job_control ()
{
if (interactive_shell)
{
terminate_stopped_jobs ();
if (original_pgrp >= 0)
give_terminal_to (original_pgrp, 1);
}
if (original_pgrp >= 0)
setpgid (0, original_pgrp);
}
void
restart_job_control ()
{
if (shell_tty != -1)
close (shell_tty);
initialize_job_control (0);
}
void
set_sigchld_handler ()
{
set_signal_handler (SIGCHLD, sigchld_handler);
}
#if defined (PGRP_PIPE)
static void
pipe_read (pp)
int *pp;
{
char ch;
if (pp[1] >= 0)
{
close (pp[1]);
pp[1] = -1;
}
if (pp[0] >= 0)
{
while (read (pp[0], &ch, 1) == -1 && errno == EINTR)
;
}
}
static void
pipe_close (pp)
int *pp;
{
if (pp[0] >= 0)
close (pp[0]);
if (pp[1] >= 0)
close (pp[1]);
pp[0] = pp[1] = -1;
}
void
close_pgrp_pipe ()
{
pipe_close (pgrp_pipe);
}
#endif