mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-02-24 23:14:14 +00:00
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.
1913 lines
41 KiB
C
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;
|
|
}
|
|
}
|
|
|