1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-02-24 23:14:14 +00:00
cde/src/cmd/ksh93/sh/jobs.c
Martijn Dekker 843b546c1a rm redundant getpid(2) syscalls (re: 9de65210)
Now that we have ${.sh.pid} a.k.a. shgd->current_pid, which is
updated using getpid() whenever forking a new process, there is no
need for anything else to ever call getpid(); we can use the stored
value instead. There were a lot of these syscalls kicking around,
some of them in performance-sensitive places.

The following lists only changes *other* than changing getpid() to
shgd->currentpid.

src/cmd/ksh93/include/defs.h:
- Comments: clarify what shgd->{pid,ppid,current_pid} are for.

src/cmd/ksh93/sh/main.c,
src/cmd/ksh93/sh/init.c:
- On reinit for a new script, update shgd->{pid,ppid,current_pid}
  in the sh_reinit() function itself instead of calling sh_reinit()
  from sh_main() and then updating those immediately after that
  call. It just makes more sense this way. Nothing else ever calls
  sh_reinit() so there are no side effects.

src/cmd/ksh93/sh/xec.c: _sh_fork():
- Update shgd->current_pid in the child early, so that the rest of
  the function can use it instead of calling getpid() again.
- Remove reassignment of SH_PIDNOD->nvalue.lp value pointer to
  shgd->current_pid (which makes ${.sh.pid} work in the shell).
  It's constant and was already set on init.
2020-09-23 04:19:02 +02:00

1913 lines
41 KiB
C

/***********************************************************************
* *
* 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);
}
/*
* 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;
int was_ttywait_on;
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__,shgd->current_pid,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;
was_ttywait_on = sh_isstate(SH_TTYWAIT); /* save tty wait state */
while(1)
{
if(!(flags&WNOHANG) && !sh.intrap && job.pwlist)
{
if(!was_ttywait_on)
sh_onstate(SH_TTYWAIT);
if(waitevent && (*waitevent)(-1,-1L,0))
flags |= WNOHANG;
}
pid = waitpid((pid_t)-1,&wstat,flags);
if(!was_ttywait_on)
sh_offstate(SH_TTYWAIT);
/*
* 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__,shgd->current_pid,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))
kill(shgd->current_pid,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, propagate 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);
kill(shgd->current_pid,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__,shgd->current_pid,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 && sh_isoption(SH_INTERACTIVE))
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;
int err = errno;
#ifndef SIGTSTP
setpgid(0,shp->gd->pid);
#endif /*SIGTSTP */
if(job.mypgid<0 || !(ttynam=ttyname(JOBTTY)))
return;
while(close(JOBTTY)<0 && errno==EINTR)
errno = err;
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 <= 0)
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(shgd->current_pid != 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 */
if(!shp->forked)
job_unstop(pw);
shp->forked = 0;
#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(sh_isoption(SH_INTERACTIVE) && 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;
}
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;
job_waitsafe(SIGCHLD);
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(*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))
{
sfprintf(outfile,"%d\n",px->p_pgrp?px->p_pgrp:px->p_pid);
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)
sfprintf(outfile,"%d\t",px->p_pid);
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;
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;
shp = pw->p_shp;
pid = pw->p_pid;
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,0);
}
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,0);
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;
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__,shgd->current_pid,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 running 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__,shgd->current_pid,job.in_critical,jobid,pid);
if(pw)
sfprintf(sfstderr,"ksh: job line %4d: wait pid=%d critical=%d flags=%o\n",__LINE__,shgd->current_pid,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;
tcsetpgrp(JOBTTY,pw->p_pgrp);
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);
/* propagate keyboard interrupts to parent */
if((pw->p_flag&P_SIGNALLED) && pw->p_exit==SIGINT && !(shp->sigflag[SIGINT]&SH_SIGOFF))
kill(shgd->current_pid,SIGINT);
#ifdef SIGTSTP
else if((pw->p_flag&P_STOPPED) && pw->p_exit==SIGTSTP)
{
job.parent = 0;
kill(shgd->current_pid,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=%u\n",__LINE__,shgd->current_pid,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__,shgd->current_pid,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];
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;
jpold = 0;
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)
{
/*
* We must make a lock first before doing anything else,
* otherwise GCC will remove the job locking mechanism
* as a result of compiler optimization.
*/
job_lock();
struct back_save *bp = new_of(struct back_save,0);
*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;
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__,shgd->current_pid,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:
job_chksave(parent);
jobfork=0;
job_unlock();
break;
}
}