summaryrefslogtreecommitdiff
path: root/src/cmd/ksh93/sh/jobs.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/ksh93/sh/jobs.c
downloadksh-upstream.tar.gz
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/ksh93/sh/jobs.c')
-rw-r--r--src/cmd/ksh93/sh/jobs.c2055
1 files changed, 2055 insertions, 0 deletions
diff --git a/src/cmd/ksh93/sh/jobs.c b/src/cmd/ksh93/sh/jobs.c
new file mode 100644
index 0000000..5dca257
--- /dev/null
+++ b/src/cmd/ksh93/sh/jobs.c
@@ -0,0 +1,2055 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1982-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * Job control for UNIX Shell
+ *
+ * David Korn
+ * AT&T Labs
+ *
+ * Written October, 1982
+ * Rewritten April, 1988
+ * Revised January, 1992
+ */
+
+#include "defs.h"
+#include <wait.h>
+#include "io.h"
+#include "jobs.h"
+#include "history.h"
+
+#if !defined(WCONTINUED) || !defined(WIFCONTINUED)
+# undef WCONTINUED
+# define WCONTINUED 0
+# undef WIFCONTINUED
+# define WIFCONTINUED(wstat) (0)
+#endif
+
+#define NJOB_SAVELIST 4
+
+/*
+ * temporary hack to get W* macros to work
+ */
+#undef wait
+#define wait ______wait
+/*
+ * This struct saves a link list of processes that have non-zero exit
+ * status, have had $! saved, but haven't been waited for
+ */
+struct jobsave
+{
+ struct jobsave *next;
+ pid_t pid;
+ unsigned short exitval;
+};
+
+static struct jobsave *job_savelist;
+static int njob_savelist;
+static struct process *pwfg;
+static int jobfork;
+
+pid_t pid_fromstring(char *str)
+{
+ pid_t pid;
+ char *last;
+ errno = 0;
+ if(sizeof(pid)==sizeof(Sflong_t))
+ pid = (pid_t)strtoll(str, &last, 10);
+ else
+ pid = (pid_t)strtol(str, &last, 10);
+ if(errno==ERANGE || *last)
+ errormsg(SH_DICT,ERROR_exit(1),"%s: invalid process id",str);
+ return(pid);
+}
+
+static void init_savelist(void)
+{
+ register struct jobsave *jp;
+ while(njob_savelist < NJOB_SAVELIST)
+ {
+ jp = newof(0,struct jobsave,1,0);
+ jp->next = job_savelist;
+ job_savelist = jp;
+ njob_savelist++;
+ }
+}
+
+struct back_save
+{
+ int count;
+ struct jobsave *list;
+ struct back_save *prev;
+};
+
+#define BYTE(n) (((n)+CHAR_BIT-1)/CHAR_BIT)
+#define MAXMSG 25
+#define SH_STOPSIG (SH_EXITSIG<<1)
+
+#ifdef VSUSP
+# ifndef CNSUSP
+# ifdef _POSIX_VDISABLE
+# define CNSUSP _POSIX_VDISABLE
+# else
+# define CNSUSP 0
+# endif /* _POSIX_VDISABLE */
+# endif /* CNSUSP */
+# ifndef CSWTCH
+# ifdef CSUSP
+# define CSWTCH CSUSP
+# else
+# define CSWTCH ('z'&037)
+# endif /* CSUSP */
+# endif /* CSWTCH */
+#endif /* VSUSP */
+
+/* Process states */
+#define P_EXITSAVE 01
+#define P_STOPPED 02
+#define P_NOTIFY 04
+#define P_SIGNALLED 010
+#define P_STTY 020
+#define P_DONE 040
+#define P_COREDUMP 0100
+#define P_DISOWN 0200
+#define P_FG 0400
+#ifdef SHOPT_BGX
+#define P_BG 01000
+#endif /* SHOPT_BGX */
+
+static int job_chksave(pid_t);
+static struct process *job_bypid(pid_t);
+static struct process *job_byjid(int);
+static char *job_sigmsg(int);
+static int job_alloc(void);
+static void job_free(int);
+static struct process *job_unpost(struct process*,int);
+static void job_unlink(struct process*);
+static void job_prmsg(struct process*);
+static struct process *freelist;
+static char beenhere;
+static char possible;
+static struct process dummy;
+static char by_number;
+static Sfio_t *outfile;
+static pid_t lastpid;
+static struct back_save bck;
+
+#ifdef JOBS
+ static void job_set(struct process*);
+ static void job_reset(struct process*);
+ static void job_waitsafe(int);
+ static struct process *job_byname(char*);
+ static struct process *job_bystring(char*);
+ static struct termios my_stty; /* terminal state for shell */
+ static char *job_string;
+#else
+ extern const char e_coredump[];
+#endif /* JOBS */
+
+#ifdef SIGTSTP
+ static void job_unstop(struct process*);
+ static void job_fgrp(struct process*, int);
+# ifndef _lib_tcgetpgrp
+# ifdef TIOCGPGRP
+ static int _i_;
+# define tcgetpgrp(a) (ioctl(a, TIOCGPGRP, &_i_)>=0?_i_:-1)
+# endif /* TIOCGPGRP */
+ int tcsetpgrp(int fd,pid_t pgrp)
+ {
+ int pgid = pgrp;
+# ifdef TIOCGPGRP
+ return(ioctl(fd, TIOCSPGRP, &pgid));
+# else
+ return(-1);
+# endif /* TIOCGPGRP */
+ }
+# endif /* _lib_tcgetpgrp */
+#else
+# define job_unstop(pw)
+# undef CNSUSP
+#endif /* SIGTSTP */
+
+#ifndef OTTYDISC
+# undef NTTYDISC
+#endif /* OTTYDISC */
+
+#ifdef JOBS
+
+typedef int (*Waitevent_f)(int,long,int);
+
+#ifdef SHOPT_BGX
+void job_chldtrap(Shell_t *shp, const char *trap, int unpost)
+{
+ register struct process *pw,*pwnext;
+ pid_t bckpid;
+ int oldexit,trapnote;
+ job_lock();
+ shp->sigflag[SIGCHLD] &= ~SH_SIGTRAP;
+ trapnote = shp->trapnote;
+ shp->trapnote = 0;
+ for(pw=job.pwlist;pw;pw=pwnext)
+ {
+ pwnext = pw->p_nxtjob;
+ if((pw->p_flag&(P_BG|P_DONE)) != (P_BG|P_DONE))
+ continue;
+ pw->p_flag &= ~P_BG;
+ bckpid = shp->bckpid;
+ oldexit = shp->savexit;
+ shp->bckpid = pw->p_pid;
+ shp->savexit = pw->p_exit;
+ if(pw->p_flag&P_SIGNALLED)
+ shp->savexit |= SH_EXITSIG;
+ sh_trap(trap,0);
+ if(pw->p_pid==bckpid && unpost)
+ job_unpost(pw,0);
+ shp->savexit = oldexit;
+ shp->bckpid = bckpid;
+ }
+ shp->trapnote = trapnote;
+ job_unlock();
+}
+#endif /* SHOPT_BGX */
+
+/*
+ * return next on link list of jobsave free list
+ */
+static struct jobsave *jobsave_create(pid_t pid)
+{
+ register struct jobsave *jp = job_savelist;
+ job_chksave(pid);
+ if(++bck.count > shgd->lim.child_max)
+ job_chksave(0);
+ if(jp)
+ {
+ njob_savelist--;
+ job_savelist = jp->next;
+ }
+ else
+ jp = newof(0,struct jobsave,1,0);
+ if(jp)
+ {
+ jp->pid = pid;
+ jp->next = bck.list;
+ bck.list = jp;
+ jp->exitval = 0;
+ }
+ return(jp);
+}
+
+#if SHOPT_COSHELL
+ pid_t sh_copid(struct cosh *csp)
+ {
+ return(COPID_BIT|(csp->id<<16)|csp->cojob->id);
+ }
+
+
+ char *sh_pid2str(Shell_t *shp,pid_t pid)
+ {
+ struct cosh *csp=0;
+ if(pid&COPID_BIT)
+ {
+ int id = (pid>>16) &0x3f;
+ for(csp=job.colist; csp; csp = csp->next)
+ {
+ if(csp->id == id)
+ break;
+ }
+ }
+ if(csp)
+ sfprintf(shp->strbuf,"%s.%d%c",csp->name,pid&0xff,0);
+ else
+ sfprintf(shp->strbuf,"%d%c",pid,0);
+ return(sfstruse(shp->strbuf));
+ }
+
+ int job_cowalk(int (*fun)(struct process*,int),int arg,char *name)
+ {
+ Shell_t *shp = sh_getinterp();
+ struct cosh *csp;
+ struct process *pw,*pwnext;
+ pid_t val;
+ int n,r=0;
+ char *cp = strchr(name,'.');
+ if(!cp)
+ n = strlen(name);
+ else
+ n = cp-name;
+ for(csp=(struct cosh*)job.colist;csp;csp=csp->next)
+ {
+ if(memcmp(name,csp->name,n)==0 && csp->name[n]==0)
+ break;
+ }
+ if(!csp)
+ errormsg(SH_DICT,ERROR_exit(1),e_jobusage,name);
+ if(cp)
+ {
+ n = pid_fromstring(cp+1);
+ val = (csp->id<<16)|n|COPID_BIT;
+ }
+ job_reap(SIGCHLD);
+ for(n=0,pw=job.pwlist; pw; pw=pwnext)
+ {
+ pwnext = pw->p_nxtjob;
+ if((cp && val==pw->p_pid) || (pw->p_cojob && pw->p_cojob->local==(void*)csp))
+ {
+ if(fun)
+ {
+ if(pw->p_flag&P_DONE)
+ continue;
+ r |= (*fun)(pw,arg);
+ }
+ else
+ job_wait(-pw->p_pid);
+ n++;
+ }
+ }
+ if(!n)
+ shp->exitval = fun?1:ERROR_NOENT;
+ else if(fun)
+ shp->exitval = r;
+ return(r);
+ }
+
+#endif /* SHOPT_COSHELL */
+
+/*
+ * Reap one job
+ * When called with sig==0, it does a blocking wait
+ */
+int job_reap(register int sig)
+{
+ Shell_t *shp = sh_getinterp();
+ register pid_t pid;
+ register struct process *pw;
+ struct process *px;
+ register int flags;
+ struct jobsave *jp;
+ int nochild=0, oerrno, wstat;
+ Waitevent_f waitevent = shp->gd->waitevent;
+ static int wcontinued = WCONTINUED;
+#if SHOPT_COSHELL
+ Cojob_t *cjp;
+ int cojobs;
+ long cotimeout = sig?0:-1;
+ for(pw=job.pwlist;pw;pw=pw->p_nxtjob)
+ {
+ if(pw->p_cojob && !(pw->p_flag&P_DONE))
+ break;
+ }
+ cojobs = (pw!=0);
+ pid = 0;
+#endif /* SHOPT_COSHELL */
+ if (vmbusy())
+ {
+ errormsg(SH_DICT,ERROR_warn(0),"vmbusy() inside job_reap() -- should not happen");
+ if (getenv("_AST_KSH_VMBUSY_ABORT"))
+ abort();
+ }
+#ifdef DEBUG
+ if(sfprintf(sfstderr,"ksh: job line %4d: reap pid=%d critical=%d signal=%d\n",__LINE__,getpid(),job.in_critical,sig) <=0)
+ write(2,"waitsafe\n",9);
+ sfsync(sfstderr);
+#endif /* DEBUG */
+ job.savesig = 0;
+ if(sig)
+ flags = WNOHANG|WUNTRACED|wcontinued;
+ else
+ flags = WUNTRACED|wcontinued;
+ shp->gd->waitevent = 0;
+ oerrno = errno;
+ while(1)
+ {
+ if(!(flags&WNOHANG) && !sh.intrap && job.pwlist)
+ {
+ sh_onstate(SH_TTYWAIT);
+ if(waitevent && (*waitevent)(-1,-1L,0))
+ flags |= WNOHANG;
+ }
+#if SHOPT_COSHELL
+ if(cojobs)
+ {
+ if(cjp = cowait(0,0,cotimeout))
+ {
+ struct cosh *csp;
+ csp = (struct cosh*)(cjp->coshell->data);
+ csp->cojob = cjp;
+ pid = sh_copid(csp);
+ if(cjp->status < 256)
+ wstat = cjp->status <<8;
+ else
+ wstat = cjp->status-256;
+ cotimeout = 0;
+ goto cojob;
+ }
+ else if(copending(0)==0)
+ cojobs = 0;
+ cotimeout = 0;
+ }
+#endif /* SHOPT_COSHELL */
+ pid = waitpid((pid_t)-1,&wstat,flags);
+ sh_offstate(SH_TTYWAIT);
+#if SHOPT_COSHELL
+ cojob:
+#endif /* SHOPT_COSHELL */
+
+ /*
+ * some systems (linux 2.6) may return EINVAL
+ * when there are no continued children
+ */
+
+ if (pid<0 && errno==EINVAL && (flags&WCONTINUED))
+ pid = waitpid((pid_t)-1,&wstat,flags&=~WCONTINUED);
+ sh_sigcheck(shp);
+ if(pid<0 && errno==EINTR && (sig||job.savesig))
+ {
+ errno = 0;
+ continue;
+ }
+ if(pid<=0)
+ break;
+ if(wstat==0)
+ job_chksave(pid);
+ flags |= WNOHANG;
+ job.waitsafe++;
+ jp = 0;
+ lastpid = pid;
+ if(!(pw=job_bypid(pid)))
+ {
+#ifdef DEBUG
+ sfprintf(sfstderr,"ksh: job line %4d: reap pid=%d critical=%d unknown job pid=%d pw=%x\n",__LINE__,getpid(),job.in_critical,pid,pw);
+#endif /* DEBUG */
+ if (WIFCONTINUED(wstat) && wcontinued)
+ continue;
+ pw = &dummy;
+ pw->p_exit = 0;
+ pw->p_pgrp = 0;
+ pw->p_exitmin = 0;
+ if(job.toclear)
+ job_clear();
+ jp = jobsave_create(pid);
+ pw->p_flag = 0;
+ lastpid = pw->p_pid = pid;
+ px = 0;
+ if(jp && WIFSTOPPED(wstat))
+ {
+ jp->exitval = SH_STOPSIG;
+ continue;
+ }
+ }
+#ifdef SIGTSTP
+ else
+ px=job_byjid(pw->p_job);
+ if (WIFCONTINUED(wstat) && wcontinued)
+ pw->p_flag &= ~(P_NOTIFY|P_SIGNALLED|P_STOPPED);
+ else if(WIFSTOPPED(wstat))
+ {
+ pw->p_flag |= (P_NOTIFY|P_SIGNALLED|P_STOPPED);
+ pw->p_exit = WSTOPSIG(wstat);
+ if(pw->p_pgrp && pw->p_pgrp==job.curpgid && sh_isstate(SH_STOPOK))
+ sh_fault(pw->p_exit);
+ if(px)
+ {
+ /* move to top of job list */
+ job_unlink(px);
+ px->p_nxtjob = job.pwlist;
+ job.pwlist = px;
+ }
+ continue;
+ }
+ else
+#endif /* SIGTSTP */
+ {
+ /* check for coprocess completion */
+ if(pid==shp->cpid)
+ {
+ sh_close(sh.coutpipe);
+ sh_close(sh.cpipe[1]);
+ sh.cpipe[1] = -1;
+ sh.coutpipe = -1;
+ }
+ else if(shp->subshell)
+ sh_subjobcheck(pid);
+
+ pw->p_flag &= ~(P_STOPPED|P_SIGNALLED);
+ if (WIFSIGNALED(wstat))
+ {
+ pw->p_flag |= (P_DONE|P_NOTIFY|P_SIGNALLED);
+ if (WTERMCORE(wstat))
+ pw->p_flag |= P_COREDUMP;
+ pw->p_exit = WTERMSIG(wstat);
+ /* if process in current jobs terminates from
+ * an interrupt, propogate to parent shell
+ */
+ if(pw->p_pgrp && pw->p_pgrp==job.curpgid && pw->p_exit==SIGINT && sh_isstate(SH_STOPOK))
+ {
+ pw->p_flag &= ~P_NOTIFY;
+ sh_offstate(SH_STOPOK);
+ sh_fault(SIGINT);
+ sh_onstate(SH_STOPOK);
+ }
+ }
+ else
+ {
+ pw->p_flag |= (P_DONE|P_NOTIFY);
+ pw->p_exit = pw->p_exitmin;
+ if(WEXITSTATUS(wstat) > pw->p_exitmin)
+ pw->p_exit = WEXITSTATUS(wstat);
+ }
+#ifdef SHOPT_BGX
+ if((pw->p_flag&P_DONE) && (pw->p_flag&P_BG))
+ {
+ job.numbjob--;
+ if(shp->st.trapcom[SIGCHLD])
+ {
+ shp->sigflag[SIGCHLD] |= SH_SIGTRAP;
+ if(sig==0)
+ job_chldtrap(shp,shp->st.trapcom[SIGCHLD],0);
+ else
+ shp->trapnote |= SH_SIGTRAP;
+ }
+ else
+ pw->p_flag &= ~P_BG;
+ }
+#endif /* SHOPT_BGX */
+ if(pw->p_pgrp==0)
+ pw->p_flag &= ~P_NOTIFY;
+ }
+ if(jp && pw== &dummy)
+ {
+ jp->exitval = pw->p_exit;
+ if(pw->p_flag&P_SIGNALLED)
+ jp->exitval |= SH_EXITSIG;
+ }
+#ifdef DEBUG
+ sfprintf(sfstderr,"ksh: job line %4d: reap pid=%d critical=%d job %d with pid %d flags=%o complete with status=%x exit=%d\n",__LINE__,getpid(),job.in_critical,pw->p_job,pid,pw->p_flag,wstat,pw->p_exit);
+ sfsync(sfstderr);
+#endif /* DEBUG*/
+ /* only top-level process in job should have notify set */
+ if(px && pw != px)
+ pw->p_flag &= ~P_NOTIFY;
+ if(pid==pw->p_fgrp && pid==tcgetpgrp(JOBTTY))
+ {
+ px = job_byjid((int)pw->p_job);
+ for(; px && (px->p_flag&P_DONE); px=px->p_nxtproc);
+ if(!px)
+ tcsetpgrp(JOBTTY,job.mypid);
+ }
+#ifndef SHOPT_BGX
+ if(!shp->intrap && shp->st.trapcom[SIGCHLD] && pid>0 && (pwfg!=job_bypid(pid)))
+ {
+ shp->sigflag[SIGCHLD] |= SH_SIGTRAP;
+ shp->trapnote |= SH_SIGTRAP;
+ }
+#endif
+ }
+ if(errno==ECHILD)
+ {
+ errno = oerrno;
+#ifdef SHOPT_BGX
+ job.numbjob = 0;
+#endif /* SHOPT_BGX */
+ nochild = 1;
+ }
+ shp->gd->waitevent = waitevent;
+ if(sh_isoption(SH_NOTIFY) && sh_isstate(SH_TTYWAIT))
+ {
+ outfile = sfstderr;
+ job_list(pw,JOB_NFLAG|JOB_NLFLAG);
+ job_unpost(pw,1);
+ sfsync(sfstderr);
+ }
+ if(sig)
+ signal(sig, job_waitsafe);
+ return(nochild);
+}
+
+/*
+ * This is the SIGCLD interrupt routine
+ */
+static void job_waitsafe(int sig)
+{
+ if(job.in_critical || vmbusy())
+ {
+ job.savesig = sig;
+ job.waitsafe++;
+ }
+ else
+ job_reap(sig);
+}
+
+/*
+ * initialize job control if possible
+ * if lflag is set the switching driver message will not print
+ */
+void job_init(Shell_t *shp, int lflag)
+{
+ register int ntry=0;
+ job.fd = JOBTTY;
+ signal(SIGCHLD,job_waitsafe);
+# if defined(SIGCLD) && (SIGCLD!=SIGCHLD)
+ signal(SIGCLD,job_waitsafe);
+# endif
+ if(njob_savelist < NJOB_SAVELIST)
+ init_savelist();
+ if(!sh_isoption(SH_INTERACTIVE))
+ return;
+ /* use new line discipline when available */
+#ifdef NTTYDISC
+# ifdef FIOLOOKLD
+ if((job.linedisc = ioctl(JOBTTY, FIOLOOKLD, 0)) <0)
+# else
+ if(ioctl(JOBTTY,TIOCGETD,&job.linedisc) !=0)
+# endif /* FIOLOOKLD */
+ return;
+ if(job.linedisc!=NTTYDISC && job.linedisc!=OTTYDISC)
+ {
+ /* no job control when running with MPX */
+# if SHOPT_VSH
+ sh_onoption(SH_VIRAW);
+# endif /* SHOPT_VSH */
+ return;
+ }
+ if(job.linedisc==NTTYDISC)
+ job.linedisc = -1;
+#endif /* NTTYDISC */
+
+ job.mypgid = getpgrp();
+ /* some systems have job control, but not initialized */
+ if(job.mypgid<=0)
+ {
+ /* Get a controlling terminal and set process group */
+ /* This should have already been done by rlogin */
+ register int fd;
+ register char *ttynam;
+#ifndef SIGTSTP
+ setpgid(0,shp->gd->pid);
+#endif /*SIGTSTP */
+ if(job.mypgid<0 || !(ttynam=ttyname(JOBTTY)))
+ return;
+ close(JOBTTY);
+ if((fd = open(ttynam,O_RDWR)) <0)
+ return;
+ if(fd!=JOBTTY)
+ sh_iorenumber(shp,fd,JOBTTY);
+ job.mypgid = shp->gd->pid;
+#ifdef SIGTSTP
+ tcsetpgrp(JOBTTY,shp->gd->pid);
+ setpgid(0,shp->gd->pid);
+#endif /* SIGTSTP */
+ }
+#ifdef SIGTSTP
+ if(possible = (setpgid(0,job.mypgid)>=0) || errno==EPERM)
+ {
+ /* wait until we are in the foreground */
+ while((job.mytgid=tcgetpgrp(JOBTTY)) != job.mypgid)
+ {
+ if(job.mytgid == -1)
+ return;
+ /* Stop this shell until continued */
+ signal(SIGTTIN,SIG_DFL);
+ kill(shp->gd->pid,SIGTTIN);
+ /* resumes here after continue tries again */
+ if(ntry++ > IOMAXTRY)
+ {
+ errormsg(SH_DICT,0,e_no_start);
+ return;
+ }
+ }
+ }
+#endif /* SIGTTIN */
+
+#ifdef NTTYDISC
+ /* set the line discipline */
+ if(job.linedisc>=0)
+ {
+ int linedisc = NTTYDISC;
+# ifdef FIOPUSHLD
+ tty_get(JOBTTY,&my_stty);
+ if (ioctl(JOBTTY, FIOPOPLD, 0) < 0)
+ return;
+ if (ioctl(JOBTTY, FIOPUSHLD, &linedisc) < 0)
+ {
+ ioctl(JOBTTY, FIOPUSHLD, &job.linedisc);
+ return;
+ }
+ tty_set(JOBTTY,TCSANOW,&my_stty);
+# else
+ if(ioctl(JOBTTY,TIOCSETD,&linedisc) !=0)
+ return;
+# endif /* FIOPUSHLD */
+ if(lflag==0)
+ errormsg(SH_DICT,0,e_newtty);
+ else
+ job.linedisc = -1;
+ }
+#endif /* NTTYDISC */
+ if(!possible)
+ return;
+
+#ifdef SIGTSTP
+ /* make sure that we are a process group leader */
+ setpgid(0,shp->gd->pid);
+# if defined(SA_NOCLDSTOP) || defined(SA_NOCLDWAIT)
+# if !defined(SA_NOCLDSTOP)
+# define SA_NOCLDSTOP 0
+# endif
+# if !defined(SA_NOCLDWAIT)
+# define SA_NOCLDWAIT 0
+# endif
+ sigflag(SIGCHLD, SA_NOCLDSTOP|SA_NOCLDWAIT, 0);
+# endif /* SA_NOCLDSTOP || SA_NOCLDWAIT */
+ signal(SIGTTIN,SIG_IGN);
+ signal(SIGTTOU,SIG_IGN);
+ /* The shell now handles ^Z */
+ signal(SIGTSTP,sh_fault);
+ tcsetpgrp(JOBTTY,shp->gd->pid);
+# ifdef CNSUSP
+ /* set the switch character */
+ tty_get(JOBTTY,&my_stty);
+ job.suspend = (unsigned)my_stty.c_cc[VSUSP];
+ if(job.suspend == (unsigned char)CNSUSP)
+ {
+ my_stty.c_cc[VSUSP] = CSWTCH;
+ tty_set(JOBTTY,TCSAFLUSH,&my_stty);
+ }
+# endif /* CNSUSP */
+ sh_onoption(SH_MONITOR);
+ job.jobcontrol++;
+ job.mypid = shp->gd->pid;
+#endif /* SIGTSTP */
+ return;
+}
+
+
+/*
+ * see if there are any stopped jobs
+ * restore tty driver and pgrp
+ */
+int job_close(Shell_t* shp)
+{
+ register struct process *pw;
+ register int count = 0, running = 0;
+ if(possible && !job.jobcontrol)
+ return(0);
+ else if(!possible && (!sh_isstate(SH_MONITOR) || sh_isstate(SH_FORKED)))
+ return(0);
+ else if(getpid() != job.mypid)
+ return(0);
+ job_lock();
+ if(!tty_check(0))
+ beenhere++;
+ for(pw=job.pwlist;pw;pw=pw->p_nxtjob)
+ {
+ if(!(pw->p_flag&P_STOPPED))
+ {
+ if(!(pw->p_flag&P_DONE))
+ running++;
+ continue;
+ }
+ if(beenhere)
+ killpg(pw->p_pgrp,SIGTERM);
+ count++;
+ }
+ if(beenhere++ == 0 && job.pwlist)
+ {
+ if(count)
+ {
+ errormsg(SH_DICT,0,e_terminate);
+ return(-1);
+ }
+ else if(running && shp->login_sh)
+ {
+ errormsg(SH_DICT,0,e_jobsrunning);
+ return(-1);
+ }
+ }
+ job_unlock();
+# ifdef SIGTSTP
+ if(possible && setpgid(0,job.mypgid)>=0)
+ tcsetpgrp(job.fd,job.mypgid);
+# endif /* SIGTSTP */
+# ifdef NTTYDISC
+ if(job.linedisc>=0)
+ {
+ /* restore old line discipline */
+# ifdef FIOPUSHLD
+ tty_get(job.fd,&my_stty);
+ if (ioctl(job.fd, FIOPOPLD, 0) < 0)
+ return(0);
+ if (ioctl(job.fd, FIOPUSHLD, &job.linedisc) < 0)
+ {
+ job.linedisc = NTTYDISC;
+ ioctl(job.fd, FIOPUSHLD, &job.linedisc);
+ return(0);
+ }
+ tty_set(job.fd,TCSAFLUSH,&my_stty);
+# else
+ if(ioctl(job.fd,TIOCSETD,&job.linedisc) !=0)
+ return(0);
+# endif /* FIOPUSHLD */
+ errormsg(SH_DICT,0,e_oldtty);
+ }
+# endif /* NTTYDISC */
+# ifdef CNSUSP
+ if(possible && job.suspend==CNSUSP)
+ {
+ tty_get(job.fd,&my_stty);
+ my_stty.c_cc[VSUSP] = CNSUSP;
+ tty_set(job.fd,TCSAFLUSH,&my_stty);
+ }
+# endif /* CNSUSP */
+ job.jobcontrol = 0;
+ return(0);
+}
+
+static void job_set(register struct process *pw)
+{
+ Shell_t *shp = pw->p_shp;
+ /* save current terminal state */
+ tty_get(job.fd,&my_stty);
+ if(pw->p_flag&P_STTY)
+ {
+ /* restore terminal state for job */
+ tty_set(job.fd,TCSAFLUSH,&pw->p_stty);
+ }
+#ifdef SIGTSTP
+ if((pw->p_flag&P_STOPPED) || tcgetpgrp(job.fd) == shp->gd->pid)
+ tcsetpgrp(job.fd,pw->p_fgrp);
+ /* if job is stopped, resume it in the background */
+ job_unstop(pw);
+#endif /* SIGTSTP */
+}
+
+static void job_reset(register struct process *pw)
+{
+ /* save the terminal state for current job */
+#ifdef SIGTSTP
+ job_fgrp(pw,tcgetpgrp(job.fd));
+ if(tcsetpgrp(job.fd,job.mypid) !=0)
+ return;
+#endif /* SIGTSTP */
+ /* force the following tty_get() to do a tcgetattr() unless fg */
+ if(!(pw->p_flag&P_FG))
+ tty_set(-1, 0, NIL(struct termios*));
+ if(pw && (pw->p_flag&P_SIGNALLED) && pw->p_exit!=SIGHUP)
+ {
+ if(tty_get(job.fd,&pw->p_stty) == 0)
+ pw->p_flag |= P_STTY;
+ /* restore terminal state for job */
+ tty_set(job.fd,TCSAFLUSH,&my_stty);
+ }
+ beenhere = 0;
+}
+#endif /* JOBS */
+
+/*
+ * wait built-in command
+ */
+
+void job_bwait(char **jobs)
+{
+ register char *jp;
+ register struct process *pw;
+ register pid_t pid;
+ if(*jobs==0)
+ job_wait((pid_t)-1);
+ else while(jp = *jobs++)
+ {
+#ifdef JOBS
+ if(*jp == '%')
+ {
+ job_lock();
+ pw = job_bystring(jp);
+ job_unlock();
+ if(pw)
+ pid = pw->p_pid;
+ else
+ return;
+ }
+# if SHOPT_COSHELL
+ else if(isalpha(*jp))
+ {
+ job_cowalk(NULL,0,jp);
+ return;
+ }
+# endif /* SHOPT_COSHELL */
+ else
+#endif /* JOBS */
+ pid = pid_fromstring(jp);
+ job_wait(-pid);
+ }
+}
+
+#ifdef JOBS
+/*
+ * execute function <fun> for each job
+ */
+
+int job_walk(Sfio_t *file,int (*fun)(struct process*,int),int arg,char *joblist[])
+{
+ register struct process *pw;
+ register int r = 0;
+ register char *jobid, **jobs=joblist;
+ register struct process *px;
+ job_string = 0;
+ outfile = file;
+ by_number = 0;
+ job_lock();
+ pw = job.pwlist;
+#if SHOPT_COSHELL
+ job_waitsafe(SIGCHLD);
+#endif /* SHOPT_COSHELL */
+ if(jobs==0)
+ {
+ /* do all jobs */
+ for(;pw;pw=px)
+ {
+ px = pw->p_nxtjob;
+ if(pw->p_env != sh.jobenv)
+ continue;
+ if((*fun)(pw,arg))
+ r = 2;
+ }
+ }
+ else if(*jobs==0) /* current job */
+ {
+ /* skip over non-stop jobs */
+ while(pw && (pw->p_env!=sh.jobenv || pw->p_pgrp==0))
+ pw = pw->p_nxtjob;
+ if((*fun)(pw,arg))
+ r = 2;
+ }
+ else while(jobid = *jobs++)
+ {
+ job_string = jobid;
+ if(*jobid==0)
+ errormsg(SH_DICT,ERROR_exit(1),e_jobusage,job_string);
+#if SHOPT_COSHELL
+ if(isalpha(*jobid))
+ {
+ r = job_cowalk(fun,arg,jobid);
+ by_number = 0;
+ job_unlock();
+ return(r);
+ }
+#endif /* SHOPT_COSHELL */
+ if(*jobid == '%')
+ pw = job_bystring(jobid);
+ else
+ {
+ int pid = pid_fromstring(jobid);
+ if(!(pw = job_bypid(pid)))
+ {
+ pw = &dummy;
+ pw->p_shp = sh_getinterp();
+ pw->p_pid = pid;
+ pw->p_pgrp = pid;
+ }
+ by_number = 1;
+ }
+ if((*fun)(pw,arg))
+ r = 2;
+ by_number = 0;
+ }
+ job_unlock();
+ return(r);
+}
+
+/*
+ * send signal <sig> to background process group if not disowned
+ */
+int job_terminate(register struct process *pw,register int sig)
+{
+ if(pw->p_pgrp && !(pw->p_flag&P_DISOWN))
+ job_kill(pw,sig);
+ return(0);
+}
+
+/*
+ * list the given job
+ * flag JOB_LFLAG for long listing
+ * flag JOB_NFLAG for list only jobs marked for notification
+ * flag JOB_PFLAG for process id(s) only
+ */
+
+int job_list(struct process *pw,register int flag)
+{
+ Shell_t *shp = sh_getinterp();
+ register struct process *px = pw;
+ register int n;
+ register const char *msg;
+ register int msize;
+ if(!pw || pw->p_job<=0)
+ return(1);
+ if(pw->p_env != shp->jobenv)
+ return(0);
+ if((flag&JOB_NFLAG) && (!(px->p_flag&P_NOTIFY)||px->p_pgrp==0))
+ return(0);
+ if((flag&JOB_PFLAG))
+ {
+#if SHOPT_COSHELL
+ sfprintf(outfile,"%s\n",sh_pid2str(shp,px->p_pgrp?px->p_pgrp:px->p_pid));
+#else
+ sfprintf(outfile,"%d\n",px->p_pgrp?px->p_pgrp:px->p_pid);
+#endif /* SHOPT_COSHELL */
+ return(0);
+ }
+ if((px->p_flag&P_DONE) && job.waitall && !(flag&JOB_LFLAG))
+ return(0);
+ job_lock();
+ n = px->p_job;
+ if(px==job.pwlist)
+ msize = '+';
+ else if(px==job.pwlist->p_nxtjob)
+ msize = '-';
+ else
+ msize = ' ';
+ if(flag&JOB_NLFLAG)
+ sfputc(outfile,'\n');
+ sfprintf(outfile,"[%d] %c ",n, msize);
+ do
+ {
+ n = 0;
+ if(flag&JOB_LFLAG)
+#if SHOPT_COSHELL
+ sfprintf(outfile,"%s\t",sh_pid2str(shp,px->p_pid));
+#else
+ sfprintf(outfile,"%d\t",px->p_pid);
+#endif /* SHOPT_COSHELL */
+ if(px->p_flag&P_SIGNALLED)
+ msg = job_sigmsg((int)(px->p_exit));
+ else if(px->p_flag&P_NOTIFY)
+ {
+ msg = sh_translate(e_done);
+ n = px->p_exit;
+ }
+ else
+ msg = sh_translate(e_running);
+ px->p_flag &= ~P_NOTIFY;
+ sfputr(outfile,msg,-1);
+ msize = strlen(msg);
+ if(n)
+ {
+ sfprintf(outfile,"(%d)",(int)n);
+ msize += (3+(n>10)+(n>100));
+ }
+ if(px->p_flag&P_COREDUMP)
+ {
+ msg = sh_translate(e_coredump);
+ sfputr(outfile, msg, -1);
+ msize += strlen(msg);
+ }
+ sfnputc(outfile,' ',MAXMSG>msize?MAXMSG-msize:1);
+ if(flag&JOB_LFLAG)
+ px = px->p_nxtproc;
+ else
+ {
+ while(px=px->p_nxtproc)
+ px->p_flag &= ~P_NOTIFY;
+ px = 0;
+ }
+ if(!px)
+ hist_list(shgd->hist_ptr,outfile,pw->p_name,0,";");
+ else
+ sfputr(outfile, e_nlspace, -1);
+ }
+ while(px);
+ job_unlock();
+ return(0);
+}
+
+/*
+ * get the process group given the job number
+ * This routine returns the process group number or -1
+ */
+static struct process *job_bystring(register char *ajob)
+{
+ register struct process *pw=job.pwlist;
+ register int c;
+ if(*ajob++ != '%' || !pw)
+ return(NIL(struct process*));
+ c = *ajob;
+ if(isdigit(c))
+ pw = job_byjid((int)strtol(ajob, (char**)0, 10));
+ else if(c=='+' || c=='%')
+ ;
+ else if(c=='-')
+ {
+ if(pw)
+ pw = job.pwlist->p_nxtjob;
+ }
+ else
+ pw = job_byname(ajob);
+ if(pw && pw->p_flag)
+ return(pw);
+ return(NIL(struct process*));
+}
+
+/*
+ * Kill a job or process
+ */
+
+int job_kill(register struct process *pw,register int sig)
+{
+ Shell_t *shp = pw->p_shp;
+ register pid_t pid;
+ register int r;
+ const char *msg;
+#ifdef SIGTSTP
+ int stopsig = (sig==SIGSTOP||sig==SIGTSTP||sig==SIGTTIN||sig==SIGTTOU);
+#else
+# define stopsig 1
+#endif /* SIGTSTP */
+ job_lock();
+ errno = ECHILD;
+ if(pw==0)
+ goto error;
+ pid = pw->p_pid;
+#if SHOPT_COSHELL
+ if(pw->p_cojob)
+ r = cokill(pw->p_cojob->coshell,pw->p_cojob,sig);
+ else
+#endif /* SHOPT_COSHELL */
+ if(by_number)
+ {
+ if(pid==0 && job.jobcontrol)
+ r = job_walk(outfile, job_kill,sig, (char**)0);
+#ifdef SIGTSTP
+ if(sig==SIGSTOP && pid==shp->gd->pid && shp->gd->ppid==1)
+ {
+ /* can't stop login shell */
+ errno = EPERM;
+ r = -1;
+ }
+ else
+ {
+ if(pid>=0)
+ {
+ if((r = kill(pid,sig))>=0 && !stopsig)
+ {
+ if(pw->p_flag&P_STOPPED)
+ pw->p_flag &= ~(P_STOPPED|P_SIGNALLED);
+ if(sig)
+ kill(pid,SIGCONT);
+ }
+ }
+ else
+ {
+ if((r = killpg(-pid,sig))>=0 && !stopsig)
+ {
+ job_unstop(job_bypid(pw->p_pid));
+ if(sig)
+ killpg(-pid,SIGCONT);
+ }
+ }
+ }
+#else
+ if(pid>=0)
+ r = kill(pid,sig);
+ else
+ r = killpg(-pid,sig);
+#endif /* SIGTSTP */
+ }
+ else
+ {
+ if(pid = pw->p_pgrp)
+ {
+ r = killpg(pid,sig);
+#ifdef SIGTSTP
+ if(r>=0 && (sig==SIGHUP||sig==SIGTERM || sig==SIGCONT))
+ job_unstop(pw);
+#endif /* SIGTSTP */
+ if(r>=0)
+ sh_delay(.05);
+ }
+ while(pw && pw->p_pgrp==0 && (r=kill(pw->p_pid,sig))>=0)
+ {
+#ifdef SIGTSTP
+ if(sig==SIGHUP || sig==SIGTERM)
+ kill(pw->p_pid,SIGCONT);
+#endif /* SIGTSTP */
+ pw = pw->p_nxtproc;
+ }
+ }
+ if(r<0 && job_string)
+ {
+ error:
+ if(pw && by_number)
+ msg = sh_translate(e_no_proc);
+ else
+ msg = sh_translate(e_no_job);
+ if(errno == EPERM)
+ msg = sh_translate(e_access);
+ sfprintf(sfstderr,"kill: %s: %s\n",job_string, msg);
+ r = 2;
+ }
+ sh_delay(.001);
+ job_unlock();
+ return(r);
+}
+
+/*
+ * Get process structure from first letters of jobname
+ *
+ */
+
+static struct process *job_byname(char *name)
+{
+ register struct process *pw = job.pwlist;
+ register struct process *pz = 0;
+ register int *flag = 0;
+ register char *cp = name;
+ int offset;
+ if(!shgd->hist_ptr)
+ return(NIL(struct process*));
+ if(*cp=='?')
+ cp++,flag= &offset;
+ for(;pw;pw=pw->p_nxtjob)
+ {
+ if(hist_match(shgd->hist_ptr,pw->p_name,cp,flag)>=0)
+ {
+ if(pz)
+ errormsg(SH_DICT,ERROR_exit(1),e_jobusage,name-1);
+ pz = pw;
+ }
+ }
+ return(pz);
+}
+
+#else
+# define job_set(x)
+# define job_reset(x)
+#endif /* JOBS */
+
+
+
+/*
+ * Initialize the process posting array
+ */
+
+void job_clear(void)
+{
+ Shell_t *shp = sh_getinterp();
+ register struct process *pw, *px;
+ register struct process *pwnext;
+ register int j = BYTE(shp->gd->lim.child_max);
+ register struct jobsave *jp,*jpnext;
+ job_lock();
+ for(pw=job.pwlist; pw; pw=pwnext)
+ {
+ pwnext = pw->p_nxtjob;
+ while(px=pw)
+ {
+ pw = pw->p_nxtproc;
+ free((void*)px);
+ }
+ }
+ for(jp=bck.list; jp;jp=jpnext)
+ {
+ jpnext = jp->next;
+ free((void*)jp);
+ }
+ bck.list = 0;
+ if(njob_savelist < NJOB_SAVELIST)
+ init_savelist();
+ job.pwlist = NIL(struct process*);
+ job.numpost=0;
+#ifdef SHOPT_BGX
+ job.numbjob = 0;
+#endif /* SHOPT_BGX */
+ job.waitall = 0;
+ job.curpgid = 0;
+ job.toclear = 0;
+ if(!job.freejobs)
+ job.freejobs = (unsigned char*)malloc((unsigned)(j+1));
+ while(j >=0)
+ job.freejobs[j--] = 0;
+ job_unlock();
+}
+
+/*
+ * put the process <pid> on the process list and return the job number
+ * if non-zero, <join> is the process id of the job to join
+ */
+
+int job_post(Shell_t *shp,pid_t pid, pid_t join)
+{
+ register struct process *pw;
+ register History_t *hp = shp->gd->hist_ptr;
+#ifdef SHOPT_BGX
+ int val,bg=0;
+#else
+ int val;
+#endif
+ shp->jobenv = shp->curenv;
+ if(job.toclear)
+ {
+ job_clear();
+ return(0);
+ }
+ job_lock();
+#ifdef SHOPT_BGX
+ if(join==1)
+ {
+ join = 0;
+ bg = P_BG;
+ job.numbjob++;
+ }
+#endif /* SHOPT_BGX */
+ if(njob_savelist < NJOB_SAVELIST)
+ init_savelist();
+ if(pw = job_bypid(pid))
+ job_unpost(pw,0);
+ if(join)
+ {
+ if(pw=job_bypid(join))
+ val = pw->p_job;
+ else
+ val = job.curjobid;
+ /* if job to join is not first move it to front */
+ if(val && (pw=job_byjid(val)) != job.pwlist)
+ {
+ job_unlink(pw);
+ pw->p_nxtjob = job.pwlist;
+ job.pwlist = pw;
+ }
+ }
+ if(pw=freelist)
+ freelist = pw->p_nxtjob;
+ else
+ pw = new_of(struct process,0);
+ pw->p_flag = 0;
+ job.numpost++;
+ if(join && job.pwlist)
+ {
+ /* join existing current job */
+ pw->p_nxtjob = job.pwlist->p_nxtjob;
+ pw->p_nxtproc = job.pwlist;
+ pw->p_job = job.pwlist->p_job;
+ }
+ else
+ {
+ /* create a new job */
+ while((pw->p_job = job_alloc()) < 0)
+ job_wait((pid_t)1);
+ pw->p_nxtjob = job.pwlist;
+ pw->p_nxtproc = 0;
+ }
+ pw->p_exitval = job.exitval;
+#if SHOPT_COSHELL
+ pw->p_cojob = 0;
+ if(shp->coshell && (pid&COPID_BIT))
+ {
+ pw->p_cojob = ((struct cosh*)shp->coshell)->cojob;
+ job.curpgid = sh_isstate(SH_MONITOR)?pid:0;
+ }
+#endif /* SHOPT_COSHELL */
+ job.pwlist = pw;
+ pw->p_shp = shp;
+ pw->p_env = shp->curenv;
+ pw->p_pid = pid;
+ if(!shp->outpipe || shp->cpid==pid)
+ pw->p_flag = P_EXITSAVE;
+ pw->p_exitmin = shp->xargexit;
+ pw->p_exit = 0;
+ if(sh_isstate(SH_MONITOR))
+ {
+ if(killpg(job.curpgid,0)<0 && errno==ESRCH)
+ job.curpgid = pid;
+ pw->p_fgrp = job.curpgid;
+ }
+ else
+ pw->p_fgrp = 0;
+ pw->p_pgrp = pw->p_fgrp;
+#ifdef DEBUG
+ sfprintf(sfstderr,"ksh: job line %4d: post pid=%d critical=%d job=%d pid=%d pgid=%d savesig=%d join=%d\n",__LINE__,getpid(),job.in_critical,pw->p_job,
+ pw->p_pid,pw->p_pgrp,job.savesig,join);
+ sfsync(sfstderr);
+#endif /* DEBUG */
+#ifdef JOBS
+ if(hp && !sh_isstate(SH_PROFILE))
+ pw->p_name=hist_tell(shgd->hist_ptr,(int)hp->histind-1);
+ else
+ pw->p_name = -1;
+#endif /* JOBS */
+ if ((val = job_chksave(pid))>=0 && !jobfork)
+ {
+ pw->p_exit = val;
+ if(pw->p_exit==SH_STOPSIG)
+ {
+ pw->p_flag |= (P_SIGNALLED|P_STOPPED);
+ pw->p_exit = 0;
+ }
+ else if(pw->p_exit >= SH_EXITSIG)
+ {
+ pw->p_flag |= P_DONE|P_SIGNALLED;
+ pw->p_exit &= SH_EXITMASK;
+ }
+ else
+ pw->p_flag |= (P_DONE|P_NOTIFY);
+ }
+#ifdef SHOPT_BGX
+ if(bg)
+ {
+ if(pw->p_flag&P_DONE)
+ job.numbjob--;
+ else
+ pw->p_flag |= P_BG;
+ }
+#endif /* SHOPT_BGX */
+ lastpid = 0;
+ job_unlock();
+ return(pw->p_job);
+}
+
+/*
+ * Returns a process structure give a process id
+ */
+
+static struct process *job_bypid(pid_t pid)
+{
+ register struct process *pw, *px;
+ for(pw=job.pwlist; pw; pw=pw->p_nxtjob)
+ for(px=pw; px; px=px->p_nxtproc)
+ {
+ if(px->p_pid==pid)
+ return(px);
+ }
+ return(NIL(struct process*));
+}
+
+/*
+ * return a pointer to a job given the job id
+ */
+
+static struct process *job_byjid(int jobid)
+{
+ register struct process *pw;
+ for(pw=job.pwlist;pw; pw = pw->p_nxtjob)
+ {
+ if(pw->p_job==jobid)
+ break;
+ }
+ return(pw);
+}
+
+/*
+ * print a signal message
+ */
+static void job_prmsg(register struct process *pw)
+{
+ if(pw->p_exit!=SIGINT && pw->p_exit!=SIGPIPE)
+ {
+ register const char *msg, *dump;
+ msg = job_sigmsg((int)(pw->p_exit));
+ msg = sh_translate(msg);
+ if(pw->p_flag&P_COREDUMP)
+ dump = sh_translate(e_coredump);
+ else
+ dump = "";
+ if(sh_isstate(SH_INTERACTIVE))
+ sfprintf(sfstderr,"%s%s\n",msg,dump);
+ else
+ errormsg(SH_DICT,2,"%d: %s%s",pw->p_pid,msg,dump);
+ }
+}
+
+/*
+ * Wait for process pid to complete
+ * If pid < -1, then wait can be interrupted, -pid is waited for (wait builtin)
+ * pid=0 to unpost all done processes
+ * pid=1 to wait for at least one process to complete
+ * pid=-1 to wait for all runing processes
+ */
+
+int job_wait(register pid_t pid)
+{
+ Shell_t *shp = sh_getinterp();
+ register struct process *pw=0,*px;
+ register int jobid = 0;
+ int nochild = 1;
+ char intr = 0;
+ if(pid < 0)
+ {
+ pid = -pid;
+ intr = 1;
+ }
+ job_lock();
+ if(pid==0)
+ {
+ if(!job.waitall || !job.curjobid || !(pw = job_byjid(job.curjobid)))
+ {
+ job_unlock();
+ goto done;
+ }
+ jobid = pw->p_job;
+ job.curjobid = 0;
+ if(!(pw->p_flag&(P_DONE|P_STOPPED)))
+ job_reap(job.savesig);
+ }
+ if(pid > 1)
+ {
+ if(pid==shp->spid)
+ shp->spid = 0;
+ if(!(pw=job_bypid(pid)))
+ {
+ /* check to see whether job status has been saved */
+ if((shp->exitval = job_chksave(pid)) < 0)
+ shp->exitval = ERROR_NOENT;
+ exitset();
+ job_unlock();
+ return(nochild);
+ }
+ else if(intr && pw->p_env!=shp->curenv)
+ {
+ shp->exitval = ERROR_NOENT;
+ job_unlock();
+ return(nochild);
+ }
+ jobid = pw->p_job;
+ if(!intr)
+ pw->p_flag &= ~P_EXITSAVE;
+ if(pw->p_pgrp && job.parent!= (pid_t)-1)
+ job_set(job_byjid(jobid));
+ }
+ pwfg = pw;
+#ifdef DEBUG
+ sfprintf(sfstderr,"ksh: job line %4d: wait pid=%d critical=%d job=%d pid=%d\n",__LINE__,getpid(),job.in_critical,jobid,pid);
+ if(pw)
+ sfprintf(sfstderr,"ksh: job line %4d: wait pid=%d critical=%d flags=%o\n",__LINE__,getpid(),job.in_critical,pw->p_flag);
+#endif /* DEBUG*/
+ errno = 0;
+ if(shp->coutpipe>=0 && lastpid && shp->cpid==lastpid)
+ {
+ sh_close(shp->coutpipe);
+ sh_close(shp->cpipe[1]);
+ shp->cpipe[1] = shp->coutpipe = -1;
+ }
+ while(1)
+ {
+ if(job.waitsafe)
+ {
+ for(px=job.pwlist;px; px = px->p_nxtjob)
+ {
+ if(px!=pw && (px->p_flag&P_NOTIFY))
+ {
+ if(sh_isoption(SH_NOTIFY))
+ {
+ outfile = sfstderr;
+ job_list(px,JOB_NFLAG|JOB_NLFLAG);
+ sfsync(sfstderr);
+ }
+ else if(!sh_isoption(SH_INTERACTIVE) && (px->p_flag&P_SIGNALLED))
+ {
+ job_prmsg(px);
+ px->p_flag &= ~P_NOTIFY;
+ }
+ }
+ }
+ }
+ if(pw && (pw->p_flag&(P_DONE|P_STOPPED)))
+ {
+#ifdef SIGTSTP
+ if(pw->p_flag&P_STOPPED)
+ {
+ pw->p_flag |= P_EXITSAVE;
+ if(sh_isoption(SH_INTERACTIVE) && !sh_isstate(SH_FORKED))
+ {
+ if( pw->p_exit!=SIGTTIN && pw->p_exit!=SIGTTOU)
+ break;
+
+ killpg(pw->p_pgrp,SIGCONT);
+ }
+ else /* ignore stop when non-interactive */
+ pw->p_flag &= ~(P_NOTIFY|P_SIGNALLED|P_STOPPED|P_EXITSAVE);
+ }
+ else
+#endif /* SIGTSTP */
+ {
+ if(pw->p_flag&P_SIGNALLED)
+ {
+ pw->p_flag &= ~P_NOTIFY;
+ job_prmsg(pw);
+ }
+ else if(pw->p_flag&P_DONE)
+ pw->p_flag &= ~P_NOTIFY;
+ if(pw->p_job==jobid)
+ {
+ px = job_byjid(jobid);
+ /* last process in job */
+ if(px!=pw)
+ px = 0;
+ if(px)
+ {
+ shp->exitval=px->p_exit;
+ if(px->p_flag&P_SIGNALLED)
+ shp->exitval |= SH_EXITSIG;
+ if(intr)
+ px->p_flag &= ~P_EXITSAVE;
+ }
+ }
+ px = job_unpost(pw,1);
+ if(!px || !job.waitall)
+ break;
+ pw = px;
+ continue;
+ }
+ }
+ sfsync(sfstderr);
+ job.waitsafe = 0;
+ nochild = job_reap(job.savesig);
+ if(job.waitsafe)
+ continue;
+ if(nochild)
+ break;
+ if(shp->sigflag[SIGALRM]&SH_SIGTRAP)
+ sh_timetraps(shp);
+ if((intr && shp->trapnote) || (pid==1 && !intr))
+ break;
+ }
+ if(intr && shp->trapnote)
+ shp->exitval = 1;
+ pwfg = 0;
+ job_unlock();
+ if(pid==1)
+ return(nochild);
+ exitset();
+ if(pid==0)
+ goto done;
+ if(pw->p_pgrp)
+ {
+ job_reset(pw);
+ /* propogate keyboard interrupts to parent */
+ if((pw->p_flag&P_SIGNALLED) && pw->p_exit==SIGINT && !(shp->sigflag[SIGINT]&SH_SIGOFF))
+ sh_fault(SIGINT);
+#ifdef SIGTSTP
+ else if((pw->p_flag&P_STOPPED) && pw->p_exit==SIGTSTP)
+ {
+ job.parent = 0;
+ sh_fault(SIGTSTP);
+ }
+#endif /* SIGTSTP */
+ }
+ else
+ {
+ if(pw->p_pid == tcgetpgrp(JOBTTY))
+ {
+ if(pw->p_pgrp==0)
+ pw->p_pgrp = pw->p_pid;
+ job_reset(pw);
+ }
+ tty_set(-1, 0, NIL(struct termios*));
+ }
+done:
+ if(!job.waitall && sh_isoption(SH_PIPEFAIL))
+ return(nochild);
+ if(!shp->intrap)
+ {
+ job_lock();
+ for(pw=job.pwlist; pw; pw=px)
+ {
+ px = pw->p_nxtjob;
+ job_unpost(pw,0);
+ }
+ job_unlock();
+ }
+ return(nochild);
+}
+
+/*
+ * move job to foreground if bgflag == 'f'
+ * move job to background if bgflag == 'b'
+ * disown job if bgflag == 'd'
+ */
+
+int job_switch(register struct process *pw,int bgflag)
+{
+ register const char *msg;
+ job_lock();
+ if(!pw || !(pw=job_byjid((int)pw->p_job)))
+ {
+ job_unlock();
+ return(1);
+ }
+ if(bgflag=='d')
+ {
+ for(; pw; pw=pw->p_nxtproc)
+ pw->p_flag |= P_DISOWN;
+ job_unlock();
+ return(0);
+ }
+#ifdef SIGTSTP
+ if(bgflag=='b')
+ {
+ sfprintf(outfile,"[%d]\t",(int)pw->p_job);
+ sh.bckpid = pw->p_pid;
+#ifdef SHOPT_BGX
+ pw->p_flag |= P_BG;
+#endif
+ msg = "&";
+ }
+ else
+ {
+ job_unlink(pw);
+ pw->p_nxtjob = job.pwlist;
+ job.pwlist = pw;
+ msg = "";
+ }
+ hist_list(shgd->hist_ptr,outfile,pw->p_name,'&',";");
+ sfputr(outfile,msg,'\n');
+ sfsync(outfile);
+ if(bgflag=='f')
+ {
+ if(!(pw=job_unpost(pw,1)))
+ {
+ job_unlock();
+ return(1);
+ }
+ job.waitall = 1;
+ pw->p_flag |= P_FG;
+#ifdef SHOPT_BGX
+ pw->p_flag &= ~P_BG;
+#endif
+ job_wait(pw->p_pid);
+ job.waitall = 0;
+ }
+ else if(pw->p_flag&P_STOPPED)
+ job_unstop(pw);
+#endif /* SIGTSTP */
+ job_unlock();
+ return(0);
+}
+
+
+#ifdef SIGTSTP
+/*
+ * Set the foreground group associated with a job
+ */
+
+static void job_fgrp(register struct process *pw, int newgrp)
+{
+ for(; pw; pw=pw->p_nxtproc)
+ pw->p_fgrp = newgrp;
+}
+
+/*
+ * turn off STOP state of a process group and send CONT signals
+ */
+
+static void job_unstop(register struct process *px)
+{
+ register struct process *pw;
+ register int num = 0;
+ for(pw=px ;pw ;pw=pw->p_nxtproc)
+ {
+ if(pw->p_flag&P_STOPPED)
+ {
+ num++;
+ pw->p_flag &= ~(P_STOPPED|P_SIGNALLED|P_NOTIFY);
+ }
+ }
+ if(num!=0)
+ {
+ if(px->p_fgrp != px->p_pgrp)
+ killpg(px->p_fgrp,SIGCONT);
+ killpg(px->p_pgrp,SIGCONT);
+ }
+}
+#endif /* SIGTSTP */
+
+/*
+ * remove a job from table
+ * If all the processes have not completed, unpost first non-completed process
+ * Otherwise the job is removed and job_unpost returns NULL.
+ * pwlist is reset if the first job is removed
+ * if <notify> is non-zero, then jobs with pending notifications are unposted
+ */
+
+static struct process *job_unpost(register struct process *pwtop,int notify)
+{
+ register struct process *pw;
+ /* make sure all processes are done */
+#ifdef DEBUG
+ sfprintf(sfstderr,"ksh: job line %4d: drop pid=%d critical=%d pid=%d env=%d\n",__LINE__,getpid(),job.in_critical,pwtop->p_pid,pwtop->p_env);
+ sfsync(sfstderr);
+#endif /* DEBUG */
+ pwtop = pw = job_byjid((int)pwtop->p_job);
+#ifdef SHOPT_BGX
+ if(pw->p_flag&P_BG)
+ return(pw);
+#endif /* SHOPT_BGX */
+ for(; pw && (pw->p_flag&P_DONE)&&(notify||!(pw->p_flag&P_NOTIFY)||pw->p_env); pw=pw->p_nxtproc);
+ if(pw)
+ return(pw);
+ if(pwtop->p_job == job.curjobid)
+ return(0);
+ /* all processes complete, unpost job */
+ job_unlink(pwtop);
+ for(pw=pwtop; pw; pw=pw->p_nxtproc)
+ {
+ if(pw && pw->p_exitval)
+ *pw->p_exitval = pw->p_exit;
+ /* save the exit status for background jobs */
+ if((pw->p_flag&P_EXITSAVE) || pw->p_pid==sh.spid)
+ {
+ struct jobsave *jp;
+ /* save status for future wait */
+ if(jp = jobsave_create(pw->p_pid))
+ {
+ jp->exitval = pw->p_exit;
+ if(pw->p_flag&P_SIGNALLED)
+ jp->exitval |= SH_EXITSIG;
+ }
+ pw->p_flag &= ~P_EXITSAVE;
+ }
+ pw->p_flag &= ~P_DONE;
+ job.numpost--;
+ pw->p_nxtjob = freelist;
+ freelist = pw;
+ }
+ pwtop->p_pid = 0;
+#ifdef DEBUG
+ sfprintf(sfstderr,"ksh: job line %4d: free pid=%d critical=%d job=%d\n",__LINE__,getpid(),job.in_critical,pwtop->p_job);
+ sfsync(sfstderr);
+#endif /* DEBUG */
+ job_free((int)pwtop->p_job);
+ return((struct process*)0);
+}
+
+/*
+ * unlink a job form the job list
+ */
+static void job_unlink(register struct process *pw)
+{
+ register struct process *px;
+ if(pw==job.pwlist)
+ {
+ job.pwlist = pw->p_nxtjob;
+ job.curpgid = 0;
+ return;
+ }
+ for(px=job.pwlist;px;px=px->p_nxtjob)
+ if(px->p_nxtjob == pw)
+ {
+ px->p_nxtjob = pw->p_nxtjob;
+ return;
+ }
+}
+
+/*
+ * get an unused job number
+ * freejobs is a bit vector, 0 is unused
+ */
+
+static int job_alloc(void)
+{
+ register int j=0;
+ register unsigned mask = 1;
+ register unsigned char *freeword;
+ register int jmax = BYTE(shgd->lim.child_max);
+ /* skip to first word with a free slot */
+ for(j=0;job.freejobs[j] == UCHAR_MAX; j++);
+ if(j >= jmax)
+ {
+ register struct process *pw;
+ for(j=1; j < shgd->lim.child_max; j++)
+ {
+ if((pw=job_byjid(j))&& !job_unpost(pw,0))
+ break;
+ }
+ j /= CHAR_BIT;
+ if(j >= jmax)
+ return(-1);
+ }
+ freeword = &job.freejobs[j];
+ j *= CHAR_BIT;
+ for(j++;mask&(*freeword);j++,mask <<=1);
+ *freeword |= mask;
+ return(j);
+}
+
+/*
+ * return a job number
+ */
+
+static void job_free(register int n)
+{
+ register int j = (--n)/CHAR_BIT;
+ register unsigned mask;
+ n -= j*CHAR_BIT;
+ mask = 1 << n;
+ job.freejobs[j] &= ~mask;
+}
+
+static char *job_sigmsg(int sig)
+{
+ static char signo[40];
+#ifdef apollo
+ /*
+ * This code handles the formatting for the apollo specific signal
+ * SIGAPOLLO.
+ */
+ extern char *apollo_error(void);
+
+ if ( sig == SIGAPOLLO )
+ return( apollo_error() );
+#endif /* apollo */
+ if(sig<=shgd->sigmax && shgd->sigmsg[sig])
+ return(shgd->sigmsg[sig]);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ if(sig>=sh.gd->sigruntime[SH_SIGRTMIN] && sig<=sh.gd->sigruntime[SH_SIGRTMAX])
+ {
+ static char sigrt[20];
+ if(sig>sh.gd->sigruntime[SH_SIGRTMIN]+(sh.gd->sigruntime[SH_SIGRTMAX]-sig<=sh.gd->sigruntime[SH_SIGRTMIN])/2)
+ sfsprintf(sigrt,sizeof(sigrt),"SIGRTMAX-%d",sh.gd->sigruntime[SH_SIGRTMAX]-sig);
+ else
+ sfsprintf(sigrt,sizeof(sigrt),"SIGRTMIN+%d",sig-sh.gd->sigruntime[SH_SIGRTMIN]);
+ return(sigrt);
+ }
+#endif
+ sfsprintf(signo,sizeof(signo),sh_translate(e_signo),sig);
+ return(signo);
+}
+
+/*
+ * see whether exit status has been saved and delete it
+ * if pid==0, then oldest saved process is deleted
+ * If pid is not found a -1 is returned.
+ */
+static int job_chksave(register pid_t pid)
+{
+ register struct jobsave *jp = bck.list, *jpold=0;
+ register int r= -1;
+ register int count=bck.count;
+ struct back_save *bp= &bck;
+again:
+ while(jp && count-->0)
+ {
+ if(jp->pid==pid)
+ break;
+ if(pid==0 && !jp->next)
+ break;
+ jpold = jp;
+ jp = jp->next;
+ }
+ if(!jp && pid && (bp=bp->prev))
+ {
+ count = bp->count;
+ jp = bp->list;
+ goto again;
+ }
+ if(jp)
+ {
+ r = 0;
+ if(pid)
+ r = jp->exitval;
+ if(jpold)
+ jpold->next = jp->next;
+ else
+ bp->list = jp->next;
+ bp->count--;
+ if(njob_savelist < NJOB_SAVELIST)
+ {
+ njob_savelist++;
+ jp->next = job_savelist;
+ job_savelist = jp;
+ }
+ else
+ free((void*)jp);
+ }
+ return(r);
+}
+
+void *job_subsave(void)
+{
+ struct back_save *bp = new_of(struct back_save,0);
+ job_lock();
+ *bp = bck;
+ bp->prev = bck.prev;
+ bck.count = 0;
+ bck.list = 0;
+ bck.prev = bp;
+ job_unlock();
+ return((void*)bp);
+}
+
+void job_subrestore(void* ptr)
+{
+ register struct jobsave *jp, *jpnext;
+ register struct back_save *bp = (struct back_save*)ptr;
+ register struct process *pw, *px, *pwnext;
+ struct jobsave *end=NULL;
+ job_lock();
+ for(jp=bck.list; jp; jp=jp->next)
+ {
+ if (!jp->next)
+ end = jp;
+ }
+ if(end)
+ end->next = bp->list;
+ else
+ bck.list = bp->list;
+ bck.count += bp->count;
+ bck.prev = bp->prev;
+ while(bck.count > shgd->lim.child_max)
+ job_chksave(0);
+ for(pw=job.pwlist; pw; pw=pwnext)
+ {
+ pwnext = pw->p_nxtjob;
+ if(pw->p_env != sh.curenv || pw->p_pid==sh.pipepid)
+ continue;
+ for(px=pw; px; px=px->p_nxtproc)
+ px->p_flag |= P_DONE;
+ job_unpost(pw,0);
+ }
+
+ free((void*)bp);
+ job_unlock();
+}
+
+int sh_waitsafe(void)
+{
+ return(job.waitsafe);
+}
+
+void job_fork(pid_t parent)
+{
+#ifdef DEBUG
+ sfprintf(sfstderr,"ksh: job line %4d: fork pid=%d critical=%d parent=%d\n",__LINE__,getpid(),job.in_critical,parent);
+#endif /* DEBUG */
+ switch (parent)
+ {
+ case -1:
+ job_lock();
+ jobfork++;
+ break;
+ case 0:
+ jobfork=0;
+ job_unlock();
+ job.waitsafe = 0;
+ job.in_critical = 0;
+ break;
+ default:
+ jobfork=0;
+ job_unlock();
+ break;
+ }
+}
+