14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*10898Sroland.mainz@nrubsig.org *          Copyright (c) 1982-2009 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
78462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                  David Korn <dgk@research.att.com>                   *
184887Schin *                                                                      *
194887Schin ***********************************************************************/
204887Schin #pragma prototyped
214887Schin /*
224887Schin  *  Job control for UNIX Shell
234887Schin  *
244887Schin  *   David Korn
254887Schin  *   AT&T Labs
264887Schin  *
274887Schin  *  Written October, 1982
284887Schin  *  Rewritten April, 1988
294887Schin  *  Revised January, 1992
304887Schin  */
314887Schin 
324887Schin #include	"defs.h"
334887Schin #include	<wait.h>
344887Schin #include	"io.h"
354887Schin #include	"jobs.h"
364887Schin #include	"history.h"
374887Schin 
384887Schin #if !defined(WCONTINUED) || !defined(WIFCONTINUED)
394887Schin #   undef  WCONTINUED
404887Schin #   define WCONTINUED	0
414887Schin #   undef  WIFCONTINUED
424887Schin #   define WIFCONTINUED(wstat)	(0)
434887Schin #endif
444887Schin 
454887Schin #define	NJOB_SAVELIST	4
464887Schin 
474887Schin /*
484887Schin  * temporary hack to get W* macros to work
494887Schin  */
504887Schin #undef wait
514887Schin #define wait    ______wait
524887Schin /*
534887Schin  * This struct saves a link list of processes that have non-zero exit
544887Schin  * status, have had $! saved, but haven't been waited for
554887Schin  */
564887Schin struct jobsave
574887Schin {
584887Schin 	struct jobsave	*next;
594887Schin 	pid_t		pid;
604887Schin 	unsigned short	exitval;
614887Schin };
624887Schin 
634887Schin static struct jobsave *job_savelist;
644887Schin static int njob_savelist;
658462SApril.Chin@Sun.COM static struct process *pwfg;
664887Schin 
674887Schin static void init_savelist(void)
684887Schin {
694887Schin 	register struct jobsave *jp;
704887Schin 	while(njob_savelist < NJOB_SAVELIST)
714887Schin 	{
724887Schin 		jp = newof(0,struct jobsave,1,0);
734887Schin 		jp->next = job_savelist;
744887Schin 		job_savelist = jp;
754887Schin 		njob_savelist++;
764887Schin 	}
774887Schin }
784887Schin 
794887Schin struct back_save
804887Schin {
814887Schin 	int		count;
824887Schin 	struct jobsave	*list;
834887Schin };
844887Schin 
854887Schin #define BYTE(n)		(((n)+CHAR_BIT-1)/CHAR_BIT)
864887Schin #define MAXMSG	25
874887Schin #define SH_STOPSIG	(SH_EXITSIG<<1)
884887Schin 
894887Schin #ifdef VSUSP
904887Schin #   ifndef CNSUSP
914887Schin #	ifdef _POSIX_VDISABLE
924887Schin #	   define CNSUSP	_POSIX_VDISABLE
934887Schin #	else
944887Schin #	   define CNSUSP	0
954887Schin #	endif /* _POSIX_VDISABLE */
964887Schin #   endif /* CNSUSP */
974887Schin #   ifndef CSWTCH
984887Schin #	ifdef CSUSP
994887Schin #	    define CSWTCH	CSUSP
1004887Schin #	else
1014887Schin #	    define CSWTCH	('z'&037)
1024887Schin #	endif /* CSUSP */
1034887Schin #   endif /* CSWTCH */
1044887Schin #endif /* VSUSP */
1054887Schin 
1064887Schin /* Process states */
1074887Schin #define P_EXITSAVE	01
1084887Schin #define P_STOPPED	02
1094887Schin #define P_NOTIFY	04
1104887Schin #define P_SIGNALLED	010
1114887Schin #define P_STTY		020
1124887Schin #define P_DONE		040
1134887Schin #define P_COREDUMP	0100
1144887Schin #define P_DISOWN	0200
1154887Schin #define P_FG		0400
116*10898Sroland.mainz@nrubsig.org #ifdef SHOPT_BGX
117*10898Sroland.mainz@nrubsig.org #define P_BG		01000
118*10898Sroland.mainz@nrubsig.org #endif /* SHOPT_BGX */
1194887Schin 
1204887Schin static int		job_chksave(pid_t);
1214887Schin static struct process	*job_bypid(pid_t);
1224887Schin static struct process	*job_byjid(int);
1234887Schin static char		*job_sigmsg(int);
1244887Schin static int		job_alloc(void);
1254887Schin static void		job_free(int);
1264887Schin static struct process	*job_unpost(struct process*,int);
1274887Schin static void		job_unlink(struct process*);
1284887Schin static void		job_prmsg(struct process*);
1294887Schin static struct process	*freelist;
1304887Schin static char		beenhere;
1314887Schin static char		possible;
1324887Schin static struct process	dummy;
1334887Schin static char		by_number;
1344887Schin static Sfio_t		*outfile;
1354887Schin static pid_t		lastpid;
1364887Schin static struct back_save	bck;
1374887Schin 
1384887Schin #ifdef JOBS
1394887Schin     static void			job_set(struct process*);
1404887Schin     static void			job_reset(struct process*);
1414887Schin     static void			job_waitsafe(int);
1424887Schin     static struct process	*job_byname(char*);
1434887Schin     static struct process	*job_bystring(char*);
1444887Schin     static struct termios	my_stty;  /* terminal state for shell */
1454887Schin     static char			*job_string;
1464887Schin #else
1474887Schin     extern const char		e_coredump[];
1484887Schin #endif /* JOBS */
1494887Schin 
1504887Schin #ifdef SIGTSTP
1514887Schin     static void		job_unstop(struct process*);
1524887Schin     static void		job_fgrp(struct process*, int);
1534887Schin #   ifndef _lib_tcgetpgrp
1544887Schin #	ifdef TIOCGPGRP
1554887Schin 	   static int _i_;
1564887Schin #	   define tcgetpgrp(a) (ioctl(a, TIOCGPGRP, &_i_)>=0?_i_:-1)
1574887Schin #	endif /* TIOCGPGRP */
1584887Schin 	int tcsetpgrp(int fd,pid_t pgrp)
1594887Schin 	{
1604887Schin 		int pgid = pgrp;
1614887Schin #		ifdef TIOCGPGRP
1624887Schin 			return(ioctl(fd, TIOCSPGRP, &pgid));
1634887Schin #		else
1644887Schin 			return(-1);
1654887Schin #		endif /* TIOCGPGRP */
1664887Schin 	}
1674887Schin #   endif /* _lib_tcgetpgrp */
1684887Schin #else
1694887Schin #   define job_unstop(pw)
1704887Schin #   undef CNSUSP
1714887Schin #endif /* SIGTSTP */
1724887Schin 
1734887Schin #ifndef OTTYDISC
1744887Schin #   undef NTTYDISC
1754887Schin #endif /* OTTYDISC */
1764887Schin 
1774887Schin #ifdef JOBS
1784887Schin 
1794887Schin typedef int (*Waitevent_f)(int,long,int);
1804887Schin 
181*10898Sroland.mainz@nrubsig.org #ifdef SHOPT_BGX
182*10898Sroland.mainz@nrubsig.org void job_chldtrap(Shell_t *shp, const char *trap, int unpost)
183*10898Sroland.mainz@nrubsig.org {
184*10898Sroland.mainz@nrubsig.org 	register struct process *pw,*pwnext;
185*10898Sroland.mainz@nrubsig.org 	pid_t bckpid;
186*10898Sroland.mainz@nrubsig.org 	int oldexit;
187*10898Sroland.mainz@nrubsig.org 	job_lock();
188*10898Sroland.mainz@nrubsig.org 	shp->sigflag[SIGCHLD] &= ~SH_SIGTRAP;
189*10898Sroland.mainz@nrubsig.org 	for(pw=job.pwlist;pw;pw=pwnext)
190*10898Sroland.mainz@nrubsig.org 	{
191*10898Sroland.mainz@nrubsig.org 		pwnext = pw->p_nxtjob;
192*10898Sroland.mainz@nrubsig.org 		if((pw->p_flag&(P_BG|P_DONE)) != (P_BG|P_DONE))
193*10898Sroland.mainz@nrubsig.org 			continue;
194*10898Sroland.mainz@nrubsig.org 		pw->p_flag &= ~P_BG;
195*10898Sroland.mainz@nrubsig.org 		bckpid = shp->bckpid;
196*10898Sroland.mainz@nrubsig.org 		oldexit = shp->savexit;
197*10898Sroland.mainz@nrubsig.org 		shp->bckpid = pw->p_pid;
198*10898Sroland.mainz@nrubsig.org 		shp->savexit = pw->p_exit;
199*10898Sroland.mainz@nrubsig.org 		if(pw->p_flag&P_SIGNALLED)
200*10898Sroland.mainz@nrubsig.org 			shp->savexit |= SH_EXITSIG;
201*10898Sroland.mainz@nrubsig.org 		sh_trap(trap,0);
202*10898Sroland.mainz@nrubsig.org 		shp->savexit = oldexit;
203*10898Sroland.mainz@nrubsig.org 		shp->bckpid = bckpid;
204*10898Sroland.mainz@nrubsig.org 		if(unpost)
205*10898Sroland.mainz@nrubsig.org 			job_unpost(pw,0);
206*10898Sroland.mainz@nrubsig.org 	}
207*10898Sroland.mainz@nrubsig.org 	job_unlock();
208*10898Sroland.mainz@nrubsig.org }
209*10898Sroland.mainz@nrubsig.org #endif /* SHOPT_BGX */
210*10898Sroland.mainz@nrubsig.org 
211*10898Sroland.mainz@nrubsig.org /*
212*10898Sroland.mainz@nrubsig.org  * return next on link list of jobsave free list
213*10898Sroland.mainz@nrubsig.org  */
214*10898Sroland.mainz@nrubsig.org static struct jobsave *jobsave_create(pid_t pid)
215*10898Sroland.mainz@nrubsig.org {
216*10898Sroland.mainz@nrubsig.org 	register struct jobsave *jp = job_savelist;
217*10898Sroland.mainz@nrubsig.org 	job_chksave(pid);
218*10898Sroland.mainz@nrubsig.org 	if(++bck.count > sh.lim.child_max)
219*10898Sroland.mainz@nrubsig.org 		job_chksave(0);
220*10898Sroland.mainz@nrubsig.org 	if(jp)
221*10898Sroland.mainz@nrubsig.org 	{
222*10898Sroland.mainz@nrubsig.org 		njob_savelist--;
223*10898Sroland.mainz@nrubsig.org 		job_savelist = jp->next;
224*10898Sroland.mainz@nrubsig.org 	}
225*10898Sroland.mainz@nrubsig.org 	else
226*10898Sroland.mainz@nrubsig.org 		jp = newof(0,struct jobsave,1,0);
227*10898Sroland.mainz@nrubsig.org 	if(jp)
228*10898Sroland.mainz@nrubsig.org 	{
229*10898Sroland.mainz@nrubsig.org 		jp->pid = pid;
230*10898Sroland.mainz@nrubsig.org 		jp->next = bck.list;
231*10898Sroland.mainz@nrubsig.org 		bck.list = jp;
232*10898Sroland.mainz@nrubsig.org 		jp->exitval = 0;
233*10898Sroland.mainz@nrubsig.org 	}
234*10898Sroland.mainz@nrubsig.org 	return(jp);
235*10898Sroland.mainz@nrubsig.org }
236*10898Sroland.mainz@nrubsig.org 
2374887Schin /*
2384887Schin  * Reap one job
2394887Schin  * When called with sig==0, it does a blocking wait
2404887Schin  */
2414887Schin int job_reap(register int sig)
2424887Schin {
2434887Schin 	register pid_t pid;
2444887Schin 	register struct process *pw;
2454887Schin 	struct process *px;
2464887Schin 	register int flags;
2474887Schin 	struct jobsave *jp;
2484887Schin 	int nochild=0, oerrno, wstat;
2494887Schin 	Waitevent_f waitevent = sh.waitevent;
2504887Schin 	static int wcontinued = WCONTINUED;
2518462SApril.Chin@Sun.COM 	if (vmbusy())
2528462SApril.Chin@Sun.COM 	{
2538462SApril.Chin@Sun.COM 		errormsg(SH_DICT,ERROR_warn(0),"vmbusy() inside job_reap() -- should not happen");
2548462SApril.Chin@Sun.COM 		if (getenv("_AST_KSH_VMBUSY_ABORT"))
2558462SApril.Chin@Sun.COM 			abort();
2568462SApril.Chin@Sun.COM 	}
2574887Schin #ifdef DEBUG
2584887Schin 	if(sfprintf(sfstderr,"ksh: job line %4d: reap pid=%d critical=%d signal=%d\n",__LINE__,getpid(),job.in_critical,sig) <=0)
2594887Schin 		write(2,"waitsafe\n",9);
2604887Schin 	sfsync(sfstderr);
2614887Schin #endif /* DEBUG */
2624887Schin 	job.savesig = 0;
2634887Schin 	if(sig)
2644887Schin 		flags = WNOHANG|WUNTRACED|wcontinued;
2654887Schin 	else
2664887Schin 		flags = WUNTRACED|wcontinued;
2674887Schin 	sh.waitevent = 0;
2684887Schin 	oerrno = errno;
2694887Schin 	while(1)
2704887Schin 	{
2718462SApril.Chin@Sun.COM 		if(!(flags&WNOHANG) && !sh.intrap && job.pwlist)
2724887Schin 		{
2738462SApril.Chin@Sun.COM 			sh_onstate(SH_TTYWAIT);
2748462SApril.Chin@Sun.COM 			if(waitevent && (*waitevent)(-1,-1L,0))
2754887Schin 				flags |= WNOHANG;
2764887Schin 		}
2774887Schin 		pid = waitpid((pid_t)-1,&wstat,flags);
2788462SApril.Chin@Sun.COM 		sh_offstate(SH_TTYWAIT);
2794887Schin 
2804887Schin 		/*
2814887Schin 		 * some systems (linux 2.6) may return EINVAL
2824887Schin 		 * when there are no continued children
2834887Schin 		 */
2844887Schin 
2854887Schin 		if (pid<0 && errno==EINVAL && (flags&WCONTINUED))
2864887Schin 			pid = waitpid((pid_t)-1,&wstat,flags&=~WCONTINUED);
2874887Schin 		sh_sigcheck();
2884887Schin 		if(sig && pid<0 && errno==EINTR)
2894887Schin 			continue;
2904887Schin 		if(pid<=0)
2914887Schin 			break;
2924887Schin 		flags |= WNOHANG;
2934887Schin 		job.waitsafe++;
2944887Schin 		jp = 0;
2958462SApril.Chin@Sun.COM 		lastpid = pid;
2964887Schin 		if(!(pw=job_bypid(pid)))
2974887Schin 		{
2984887Schin #ifdef DEBUG
2994887Schin 			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);
3004887Schin #endif /* DEBUG */
3018462SApril.Chin@Sun.COM 			if (WIFCONTINUED(wstat) && wcontinued)
3028462SApril.Chin@Sun.COM 				continue;
3034887Schin 			pw = &dummy;
3044887Schin 			pw->p_exit = 0;
3054887Schin 			pw->p_pgrp = 0;
3068462SApril.Chin@Sun.COM 			pw->p_exitmin = 0;
3074887Schin 			if(job.toclear)
3084887Schin 				job_clear();
309*10898Sroland.mainz@nrubsig.org 			jp = jobsave_create(pid);
3104887Schin 			pw->p_flag = 0;
3114887Schin 			lastpid = pw->p_pid = pid;
3124887Schin 			px = 0;
3134887Schin 			if(jp && WIFSTOPPED(wstat))
3144887Schin 			{
3154887Schin 				jp->exitval = SH_STOPSIG;
3164887Schin 				continue;
3174887Schin 			}
3184887Schin 		}
3194887Schin #ifdef SIGTSTP
3204887Schin 		else
3214887Schin 			px=job_byjid(pw->p_job);
3224887Schin 		if(WIFSTOPPED(wstat))
3234887Schin 		{
3244887Schin 			if(px)
3254887Schin 			{
3264887Schin 				/* move to top of job list */
3274887Schin 				job_unlink(px);
3284887Schin 				px->p_nxtjob = job.pwlist;
3294887Schin 				job.pwlist = px;
3304887Schin 			}
3318462SApril.Chin@Sun.COM 			pw->p_flag |= (P_NOTIFY|P_SIGNALLED|P_STOPPED);
3324887Schin 			pw->p_exit = WSTOPSIG(wstat);
3334887Schin 			if(pw->p_pgrp && pw->p_pgrp==job.curpgid && sh_isstate(SH_STOPOK))
3344887Schin 				sh_fault(pw->p_exit);
3354887Schin 			continue;
3364887Schin 		}
3374887Schin 		else if (WIFCONTINUED(wstat) && wcontinued)
3384887Schin 			pw->p_flag &= ~(P_NOTIFY|P_SIGNALLED|P_STOPPED);
3394887Schin 		else
3404887Schin #endif /* SIGTSTP */
3414887Schin 		{
3424887Schin 			/* check for coprocess completion */
3434887Schin 			if(pid==sh.cpid)
3444887Schin 			{
3454887Schin 				sh_close(sh.coutpipe);
3464887Schin 				sh_close(sh.cpipe[1]);
3474887Schin 				sh.cpipe[1] = -1;
3484887Schin 				sh.coutpipe = -1;
3494887Schin 			}
3508462SApril.Chin@Sun.COM 			else if(sh.subshell)
3518462SApril.Chin@Sun.COM 				sh_subjobcheck(pid);
3528462SApril.Chin@Sun.COM 
3538462SApril.Chin@Sun.COM 			pw->p_flag &= ~(P_STOPPED|P_SIGNALLED);
3544887Schin 			if (WIFSIGNALED(wstat))
3554887Schin 			{
3564887Schin 				pw->p_flag |= (P_DONE|P_NOTIFY|P_SIGNALLED);
3574887Schin 				if (WTERMCORE(wstat))
3584887Schin 					pw->p_flag |= P_COREDUMP;
3594887Schin 				pw->p_exit = WTERMSIG(wstat);
3604887Schin 				/* if process in current jobs terminates from
3614887Schin 				 * an interrupt, propogate to parent shell
3624887Schin 				 */
3634887Schin 				if(pw->p_pgrp && pw->p_pgrp==job.curpgid && pw->p_exit==SIGINT && sh_isstate(SH_STOPOK))
3644887Schin 				{
3654887Schin 					pw->p_flag &= ~P_NOTIFY;
3664887Schin 					sh_offstate(SH_STOPOK);
3674887Schin 					sh_fault(SIGINT);
3684887Schin 					sh_onstate(SH_STOPOK);
3694887Schin 				}
3704887Schin 			}
3714887Schin 			else
3724887Schin 			{
3734887Schin 				pw->p_flag |= (P_DONE|P_NOTIFY);
3748462SApril.Chin@Sun.COM 				pw->p_exit =  pw->p_exitmin;
3758462SApril.Chin@Sun.COM 				if(WEXITSTATUS(wstat) > pw->p_exitmin)
3764887Schin 					pw->p_exit = WEXITSTATUS(wstat);
3774887Schin 			}
378*10898Sroland.mainz@nrubsig.org #ifdef SHOPT_BGX
379*10898Sroland.mainz@nrubsig.org 			if((pw->p_flag&P_DONE) && (pw->p_flag&P_BG))
380*10898Sroland.mainz@nrubsig.org 			{
381*10898Sroland.mainz@nrubsig.org 				job.numbjob--;
382*10898Sroland.mainz@nrubsig.org 				if(sh.st.trapcom[SIGCHLD])
383*10898Sroland.mainz@nrubsig.org 				{
384*10898Sroland.mainz@nrubsig.org 					sh.sigflag[SIGCHLD] |= SH_SIGTRAP;
385*10898Sroland.mainz@nrubsig.org 					if(sig==0)
386*10898Sroland.mainz@nrubsig.org 						job_chldtrap(&sh,sh.st.trapcom[SIGCHLD],0);
387*10898Sroland.mainz@nrubsig.org 					else
388*10898Sroland.mainz@nrubsig.org 						sh.trapnote |= SH_SIGTRAP;
389*10898Sroland.mainz@nrubsig.org 				}
390*10898Sroland.mainz@nrubsig.org 				else
391*10898Sroland.mainz@nrubsig.org 					pw->p_flag &= ~P_BG;
392*10898Sroland.mainz@nrubsig.org 			}
393*10898Sroland.mainz@nrubsig.org #endif /* SHOPT_BGX */
3944887Schin 			if(pw->p_pgrp==0)
3954887Schin 				pw->p_flag &= ~P_NOTIFY;
3964887Schin 		}
3974887Schin 		if(jp && pw== &dummy)
3984887Schin 		{
3994887Schin 			jp->exitval = pw->p_exit;
4004887Schin 			if(pw->p_flag&P_SIGNALLED)
4014887Schin 				jp->exitval |= SH_EXITSIG;
4024887Schin 		}
4034887Schin #ifdef DEBUG
4044887Schin 		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);
4054887Schin 		sfsync(sfstderr);
4064887Schin #endif /* DEBUG*/
4074887Schin 		/* only top-level process in job should have notify set */
4084887Schin 		if(px && pw != px)
4094887Schin 			pw->p_flag &= ~P_NOTIFY;
4108462SApril.Chin@Sun.COM 		if(pid==pw->p_fgrp && pid==tcgetpgrp(JOBTTY))
4118462SApril.Chin@Sun.COM 		{
4128462SApril.Chin@Sun.COM 			px = job_byjid((int)pw->p_job);
4138462SApril.Chin@Sun.COM 			for(; px && (px->p_flag&P_DONE); px=px->p_nxtproc);
4148462SApril.Chin@Sun.COM 			if(!px)
4158462SApril.Chin@Sun.COM 				tcsetpgrp(JOBTTY,job.mypid);
4168462SApril.Chin@Sun.COM 		}
417*10898Sroland.mainz@nrubsig.org #ifndef SHOPT_BGX
4188462SApril.Chin@Sun.COM 		if(!sh.intrap && sh.st.trapcom[SIGCHLD] && pid>0 && (pwfg!=job_bypid(pid)))
4198462SApril.Chin@Sun.COM 		{
4208462SApril.Chin@Sun.COM 			sh.sigflag[SIGCHLD] |= SH_SIGTRAP;
4218462SApril.Chin@Sun.COM 			sh.trapnote |= SH_SIGTRAP;
4228462SApril.Chin@Sun.COM 		}
423*10898Sroland.mainz@nrubsig.org #endif
4244887Schin 	}
4254887Schin 	if(errno==ECHILD)
4264887Schin 	{
4274887Schin 		errno = oerrno;
428*10898Sroland.mainz@nrubsig.org #ifdef SHOPT_BGX
429*10898Sroland.mainz@nrubsig.org 		job.numbjob = 0;
430*10898Sroland.mainz@nrubsig.org #endif /* SHOPT_BGX */
4314887Schin 		nochild = 1;
4324887Schin 	}
4334887Schin 	sh.waitevent = waitevent;
4344887Schin 	if(sh_isoption(SH_NOTIFY) && sh_isstate(SH_TTYWAIT))
4354887Schin 	{
4364887Schin 		outfile = sfstderr;
4374887Schin 		job_list(pw,JOB_NFLAG|JOB_NLFLAG);
4384887Schin 		job_unpost(pw,1);
4394887Schin 		sfsync(sfstderr);
4404887Schin 	}
4414887Schin 	if(sig)
4424887Schin 		signal(sig, job_waitsafe);
4434887Schin 	return(nochild);
4444887Schin }
4454887Schin 
4464887Schin /*
4474887Schin  * This is the SIGCLD interrupt routine
4484887Schin  */
4494887Schin static void job_waitsafe(int sig)
4504887Schin {
4518462SApril.Chin@Sun.COM 	if(job.in_critical || vmbusy())
4524887Schin 	{
4534887Schin 		job.savesig = sig;
4544887Schin 		job.waitsafe++;
4554887Schin 	}
4564887Schin 	else
4574887Schin 		job_reap(sig);
4584887Schin }
4594887Schin 
4604887Schin /*
4614887Schin  * initialize job control if possible
4624887Schin  * if lflag is set the switching driver message will not print
4634887Schin  */
4648462SApril.Chin@Sun.COM void job_init(Shell_t *shp, int lflag)
4654887Schin {
4668462SApril.Chin@Sun.COM 	register int ntry=0;
4674887Schin 	job.fd = JOBTTY;
4684887Schin 	signal(SIGCHLD,job_waitsafe);
4694887Schin #   if defined(SIGCLD) && (SIGCLD!=SIGCHLD)
4704887Schin 	signal(SIGCLD,job_waitsafe);
4714887Schin #   endif
4724887Schin 	if(njob_savelist < NJOB_SAVELIST)
4734887Schin 		init_savelist();
4744887Schin 	if(!sh_isoption(SH_INTERACTIVE))
4754887Schin 		return;
4764887Schin 	/* use new line discipline when available */
4774887Schin #ifdef NTTYDISC
4784887Schin #   ifdef FIOLOOKLD
4794887Schin 	if((job.linedisc = ioctl(JOBTTY, FIOLOOKLD, 0)) <0)
4804887Schin #   else
4814887Schin 	if(ioctl(JOBTTY,TIOCGETD,&job.linedisc) !=0)
4824887Schin #   endif /* FIOLOOKLD */
4834887Schin 		return;
4844887Schin 	if(job.linedisc!=NTTYDISC && job.linedisc!=OTTYDISC)
4854887Schin 	{
4864887Schin 		/* no job control when running with MPX */
4874887Schin #   if SHOPT_VSH
4884887Schin 		sh_onoption(SH_VIRAW);
4894887Schin #   endif /* SHOPT_VSH */
4904887Schin 		return;
4914887Schin 	}
4924887Schin 	if(job.linedisc==NTTYDISC)
4934887Schin 		job.linedisc = -1;
4944887Schin #endif /* NTTYDISC */
4954887Schin 
4964887Schin 	job.mypgid = getpgrp();
4974887Schin 	/* some systems have job control, but not initialized */
4984887Schin 	if(job.mypgid<=0)
4994887Schin         {
5004887Schin 		/* Get a controlling terminal and set process group */
5014887Schin 		/* This should have already been done by rlogin */
5024887Schin                 register int fd;
5034887Schin                 register char *ttynam;
5044887Schin #ifndef SIGTSTP
5058462SApril.Chin@Sun.COM                 setpgid(0,shp->pid);
5064887Schin #endif /*SIGTSTP */
5074887Schin                 if(job.mypgid<0 || !(ttynam=ttyname(JOBTTY)))
5084887Schin                         return;
5094887Schin                 close(JOBTTY);
5104887Schin                 if((fd = open(ttynam,O_RDWR)) <0)
5114887Schin                         return;
5124887Schin                 if(fd!=JOBTTY)
5138462SApril.Chin@Sun.COM                         sh_iorenumber(shp,fd,JOBTTY);
5148462SApril.Chin@Sun.COM                 job.mypgid = shp->pid;
5154887Schin #ifdef SIGTSTP
5168462SApril.Chin@Sun.COM                 tcsetpgrp(JOBTTY,shp->pid);
5178462SApril.Chin@Sun.COM                 setpgid(0,shp->pid);
5184887Schin #endif /* SIGTSTP */
5194887Schin         }
5204887Schin #ifdef SIGTSTP
5214887Schin 	if(possible = (setpgid(0,job.mypgid)>=0) || errno==EPERM)
5224887Schin 	{
5234887Schin 		/* wait until we are in the foreground */
5244887Schin 		while((job.mytgid=tcgetpgrp(JOBTTY)) != job.mypgid)
5254887Schin 		{
5264887Schin 			if(job.mytgid == -1)
5274887Schin 				return;
5284887Schin 			/* Stop this shell until continued */
5294887Schin 			signal(SIGTTIN,SIG_DFL);
5308462SApril.Chin@Sun.COM 			kill(shp->pid,SIGTTIN);
5314887Schin 			/* resumes here after continue tries again */
5324887Schin 			if(ntry++ > IOMAXTRY)
5334887Schin 			{
5344887Schin 				errormsg(SH_DICT,0,e_no_start);
5354887Schin 				return;
5364887Schin 			}
5374887Schin 		}
5384887Schin 	}
5394887Schin #endif /* SIGTTIN */
5404887Schin 
5414887Schin #ifdef NTTYDISC
5424887Schin 	/* set the line discipline */
5434887Schin 	if(job.linedisc>=0)
5444887Schin 	{
5454887Schin 		int linedisc = NTTYDISC;
5464887Schin #   ifdef FIOPUSHLD
5474887Schin 		tty_get(JOBTTY,&my_stty);
5484887Schin 		if (ioctl(JOBTTY, FIOPOPLD, 0) < 0)
5494887Schin 			return;
5504887Schin 		if (ioctl(JOBTTY, FIOPUSHLD, &linedisc) < 0)
5514887Schin 		{
5524887Schin 			ioctl(JOBTTY, FIOPUSHLD, &job.linedisc);
5534887Schin 			return;
5544887Schin 		}
5554887Schin 		tty_set(JOBTTY,TCSANOW,&my_stty);
5564887Schin #   else
5574887Schin 		if(ioctl(JOBTTY,TIOCSETD,&linedisc) !=0)
5584887Schin 			return;
5594887Schin #   endif /* FIOPUSHLD */
5604887Schin 		if(lflag==0)
5614887Schin 			errormsg(SH_DICT,0,e_newtty);
5624887Schin 		else
5634887Schin 			job.linedisc = -1;
5644887Schin 	}
5654887Schin #endif /* NTTYDISC */
5664887Schin 	if(!possible)
5674887Schin 		return;
5684887Schin 
5694887Schin #ifdef SIGTSTP
5704887Schin 	/* make sure that we are a process group leader */
5718462SApril.Chin@Sun.COM 	setpgid(0,shp->pid);
5724887Schin #   if defined(SA_NOCLDWAIT) && defined(_lib_sigflag)
5734887Schin 	sigflag(SIGCHLD, SA_NOCLDSTOP|SA_NOCLDWAIT, 0);
5744887Schin #   endif /* SA_NOCLDWAIT */
5754887Schin 	signal(SIGTTIN,SIG_IGN);
5764887Schin 	signal(SIGTTOU,SIG_IGN);
5774887Schin 	/* The shell now handles ^Z */
5784887Schin 	signal(SIGTSTP,sh_fault);
5798462SApril.Chin@Sun.COM 	tcsetpgrp(JOBTTY,shp->pid);
5804887Schin #   ifdef CNSUSP
5814887Schin 	/* set the switch character */
5824887Schin 	tty_get(JOBTTY,&my_stty);
5834887Schin 	job.suspend = (unsigned)my_stty.c_cc[VSUSP];
5844887Schin 	if(job.suspend == (unsigned char)CNSUSP)
5854887Schin 	{
5864887Schin 		my_stty.c_cc[VSUSP] = CSWTCH;
5874887Schin 		tty_set(JOBTTY,TCSAFLUSH,&my_stty);
5884887Schin 	}
5894887Schin #   endif /* CNSUSP */
5904887Schin 	sh_onoption(SH_MONITOR);
5914887Schin 	job.jobcontrol++;
5928462SApril.Chin@Sun.COM 	job.mypid = shp->pid;
5934887Schin #endif /* SIGTSTP */
5944887Schin 	return;
5954887Schin }
5964887Schin 
5974887Schin 
5984887Schin /*
5994887Schin  * see if there are any stopped jobs
6004887Schin  * restore tty driver and pgrp
6014887Schin  */
6028462SApril.Chin@Sun.COM int job_close(Shell_t* shp)
6034887Schin {
6044887Schin 	register struct process *pw;
6054887Schin 	register int count = 0, running = 0;
6064887Schin 	if(possible && !job.jobcontrol)
6074887Schin 		return(0);
6084887Schin 	else if(!possible && (!sh_isstate(SH_MONITOR) || sh_isstate(SH_FORKED)))
6094887Schin 		return(0);
6104887Schin 	else if(getpid() != job.mypid)
6114887Schin 		return(0);
6124887Schin 	job_lock();
6134887Schin 	if(!tty_check(0))
6144887Schin 		beenhere++;
6154887Schin 	for(pw=job.pwlist;pw;pw=pw->p_nxtjob)
6164887Schin 	{
6174887Schin 		if(!(pw->p_flag&P_STOPPED))
6184887Schin 		{
6194887Schin 			if(!(pw->p_flag&P_DONE))
6204887Schin 				running++;
6214887Schin 			continue;
6224887Schin 		}
6234887Schin 		if(beenhere)
6244887Schin 			killpg(pw->p_pgrp,SIGTERM);
6254887Schin 		count++;
6264887Schin 	}
6274887Schin 	if(beenhere++ == 0 && job.pwlist)
6284887Schin 	{
6294887Schin 		if(count)
6304887Schin 		{
6314887Schin 			errormsg(SH_DICT,0,e_terminate);
6324887Schin 			return(-1);
6334887Schin 		}
6348462SApril.Chin@Sun.COM 		else if(running && shp->login_sh)
6354887Schin 		{
6364887Schin 			errormsg(SH_DICT,0,e_jobsrunning);
6374887Schin 			return(-1);
6384887Schin 		}
6394887Schin 	}
6404887Schin 	job_unlock();
6414887Schin #   ifdef SIGTSTP
6424887Schin 	if(possible && setpgid(0,job.mypgid)>=0)
6434887Schin 		tcsetpgrp(job.fd,job.mypgid);
6444887Schin #   endif /* SIGTSTP */
6454887Schin #   ifdef NTTYDISC
6464887Schin 	if(job.linedisc>=0)
6474887Schin 	{
6484887Schin 		/* restore old line discipline */
6494887Schin #	ifdef FIOPUSHLD
6504887Schin 		tty_get(job.fd,&my_stty);
6514887Schin 		if (ioctl(job.fd, FIOPOPLD, 0) < 0)
6524887Schin 			return(0);
6534887Schin 		if (ioctl(job.fd, FIOPUSHLD, &job.linedisc) < 0)
6544887Schin 		{
6554887Schin 			job.linedisc = NTTYDISC;
6564887Schin 			ioctl(job.fd, FIOPUSHLD, &job.linedisc);
6574887Schin 			return(0);
6584887Schin 		}
6594887Schin 		tty_set(job.fd,TCSAFLUSH,&my_stty);
6604887Schin #	else
6614887Schin 		if(ioctl(job.fd,TIOCSETD,&job.linedisc) !=0)
6624887Schin 			return(0);
6634887Schin #	endif /* FIOPUSHLD */
6644887Schin 		errormsg(SH_DICT,0,e_oldtty);
6654887Schin 	}
6664887Schin #   endif /* NTTYDISC */
6674887Schin #   ifdef CNSUSP
6684887Schin 	if(possible && job.suspend==CNSUSP)
6694887Schin 	{
6704887Schin 		tty_get(job.fd,&my_stty);
6714887Schin 		my_stty.c_cc[VSUSP] = CNSUSP;
6724887Schin 		tty_set(job.fd,TCSAFLUSH,&my_stty);
6734887Schin 	}
6744887Schin #   endif /* CNSUSP */
6754887Schin 	job.jobcontrol = 0;
6764887Schin 	return(0);
6774887Schin }
6784887Schin 
6794887Schin static void job_set(register struct process *pw)
6804887Schin {
6814887Schin 	/* save current terminal state */
6824887Schin 	tty_get(job.fd,&my_stty);
6834887Schin 	if(pw->p_flag&P_STTY)
6844887Schin 	{
6854887Schin 		/* restore terminal state for job */
6864887Schin 		tty_set(job.fd,TCSAFLUSH,&pw->p_stty);
6874887Schin 	}
6884887Schin #ifdef SIGTSTP
6894887Schin 	if((pw->p_flag&P_STOPPED) || tcgetpgrp(job.fd) == sh.pid)
6904887Schin 		tcsetpgrp(job.fd,pw->p_fgrp);
6914887Schin 	/* if job is stopped, resume it in the background */
6924887Schin 	job_unstop(pw);
6934887Schin #endif	/* SIGTSTP */
6944887Schin }
6954887Schin 
6964887Schin static void job_reset(register struct process *pw)
6974887Schin {
6984887Schin 	/* save the terminal state for current job */
6994887Schin #ifdef SIGTSTP
7004887Schin 	job_fgrp(pw,tcgetpgrp(job.fd));
7018462SApril.Chin@Sun.COM 	if(tcsetpgrp(job.fd,job.mypid) !=0)
7024887Schin 		return;
7034887Schin #endif	/* SIGTSTP */
7044887Schin 	/* force the following tty_get() to do a tcgetattr() unless fg */
7054887Schin 	if(!(pw->p_flag&P_FG))
7064887Schin 		tty_set(-1, 0, NIL(struct termios*));
7074887Schin 	if(pw && (pw->p_flag&P_SIGNALLED) && pw->p_exit!=SIGHUP)
7084887Schin 	{
7094887Schin 		if(tty_get(job.fd,&pw->p_stty) == 0)
7104887Schin 			pw->p_flag |= P_STTY;
7114887Schin 		/* restore terminal state for job */
7124887Schin 		tty_set(job.fd,TCSAFLUSH,&my_stty);
7134887Schin 	}
7144887Schin 	beenhere = 0;
7154887Schin }
7164887Schin #endif /* JOBS */
7174887Schin 
7184887Schin /*
7194887Schin  * wait built-in command
7204887Schin  */
7214887Schin 
7224887Schin void job_bwait(char **jobs)
7234887Schin {
7244887Schin 	register char *jp;
7254887Schin 	register struct process *pw;
7264887Schin 	register pid_t pid;
7274887Schin 	if(*jobs==0)
7284887Schin 		job_wait((pid_t)-1);
7294887Schin 	else while(jp = *jobs++)
7304887Schin 	{
7314887Schin #ifdef JOBS
7324887Schin 		if(*jp == '%')
7334887Schin 		{
7344887Schin 			job_lock();
7354887Schin 			pw = job_bystring(jp);
7364887Schin 			job_unlock();
7374887Schin 			if(pw)
7384887Schin 				pid = pw->p_pid;
7394887Schin 			else
7404887Schin 				return;
7414887Schin 		}
7424887Schin 		else
7434887Schin #endif /* JOBS */
7444887Schin 			pid = (int)strtol(jp, (char**)0, 10);
7454887Schin 		job_wait(-pid);
7464887Schin 	}
7474887Schin }
7484887Schin 
7494887Schin #ifdef JOBS
7504887Schin /*
7514887Schin  * execute function <fun> for each job
7524887Schin  */
7534887Schin 
7544887Schin int job_walk(Sfio_t *file,int (*fun)(struct process*,int),int arg,char *joblist[])
7554887Schin {
7564887Schin 	register struct process *pw;
7574887Schin 	register int r = 0;
7584887Schin 	register char *jobid, **jobs=joblist;
7594887Schin 	register struct process *px;
7604887Schin 	job_string = 0;
7614887Schin 	outfile = file;
7624887Schin 	by_number = 0;
7634887Schin 	job_lock();
7644887Schin 	pw = job.pwlist;
7654887Schin 	if(jobs==0)
7664887Schin 	{
7674887Schin 		/* do all jobs */
7684887Schin 		for(;pw;pw=px)
7694887Schin 		{
7704887Schin 			px = pw->p_nxtjob;
7714887Schin 			if(pw->p_env != sh.jobenv)
7724887Schin 				continue;
7734887Schin 			if((*fun)(pw,arg))
7744887Schin 				r = 2;
7754887Schin 		}
7764887Schin 	}
7774887Schin 	else if(*jobs==0)	/* current job */
7784887Schin 	{
7794887Schin 		/* skip over non-stop jobs */
7804887Schin 		while(pw && (pw->p_env!=sh.jobenv || pw->p_pgrp==0))
7814887Schin 			pw = pw->p_nxtjob;
7824887Schin 		if((*fun)(pw,arg))
7834887Schin 			r = 2;
7844887Schin 	}
7854887Schin 	else while(jobid = *jobs++)
7864887Schin 	{
7874887Schin 		job_string = jobid;
7884887Schin 		if(*jobid==0)
7894887Schin 			errormsg(SH_DICT,ERROR_exit(1),e_jobusage,job_string);
7904887Schin 		if(*jobid == '%')
7914887Schin 			pw = job_bystring(jobid);
7924887Schin 		else
7934887Schin 		{
7944887Schin 			int pid = (int)strtol(jobid, (char**)0, 10);
7954887Schin 			if(pid<0)
7964887Schin 				jobid++;
7974887Schin 			while(isdigit(*jobid))
7984887Schin 				jobid++;
7994887Schin 			if(*jobid)
8004887Schin 				errormsg(SH_DICT,ERROR_exit(1),e_jobusage,job_string);
8014887Schin 			if(!(pw = job_bypid(pid)))
8024887Schin 			{
8034887Schin 				pw = &dummy;
8044887Schin 				pw->p_pid = pid;
8054887Schin 				pw->p_pgrp = pid;
8064887Schin 			}
8074887Schin 			by_number = 1;
8084887Schin 		}
8094887Schin 		if((*fun)(pw,arg))
8104887Schin 			r = 2;
8114887Schin 		by_number = 0;
8124887Schin 	}
8134887Schin 	job_unlock();
8144887Schin 	return(r);
8154887Schin }
8164887Schin 
8174887Schin /*
8184887Schin  * send signal <sig> to background process group if not disowned
8194887Schin  */
8204887Schin int job_terminate(register struct process *pw,register int sig)
8214887Schin {
8224887Schin 	if(pw->p_pgrp && !(pw->p_flag&P_DISOWN))
8234887Schin 		job_kill(pw,sig);
8244887Schin 	return(0);
8254887Schin }
8264887Schin 
8274887Schin /*
8284887Schin  * list the given job
8294887Schin  * flag JOB_LFLAG for long listing
8304887Schin  * flag JOB_NFLAG for list only jobs marked for notification
8314887Schin  * flag JOB_PFLAG for process id(s) only
8324887Schin  */
8334887Schin 
8344887Schin int job_list(struct process *pw,register int flag)
8354887Schin {
8364887Schin 	register struct process *px = pw;
8374887Schin 	register int  n;
8384887Schin 	register const char *msg;
8394887Schin 	register int msize;
8404887Schin 	if(!pw || pw->p_job<=0)
8414887Schin 		return(1);
8424887Schin 	if(pw->p_env != sh.jobenv)
8434887Schin 		return(0);
8444887Schin 	if((flag&JOB_NFLAG) && (!(px->p_flag&P_NOTIFY)||px->p_pgrp==0))
8454887Schin 		return(0);
8464887Schin 	if((flag&JOB_PFLAG))
8474887Schin 	{
8484887Schin 		sfprintf(outfile,"%d\n",px->p_pgrp?px->p_pgrp:px->p_pid);
8494887Schin 		return(0);
8504887Schin 	}
8514887Schin 	if((px->p_flag&P_DONE) && job.waitall && !(flag&JOB_LFLAG))
8524887Schin 		return(0);
8534887Schin 	job_lock();
8544887Schin 	n = px->p_job;
8554887Schin 	if(px==job.pwlist)
8564887Schin 		msize = '+';
8574887Schin 	else if(px==job.pwlist->p_nxtjob)
8584887Schin 		msize = '-';
8594887Schin 	else
8604887Schin 		msize = ' ';
8614887Schin 	if(flag&JOB_NLFLAG)
8624887Schin 		sfputc(outfile,'\n');
8634887Schin 	sfprintf(outfile,"[%d] %c ",n, msize);
8644887Schin 	do
8654887Schin 	{
8664887Schin 		n = 0;
8674887Schin 		if(flag&JOB_LFLAG)
8684887Schin 			sfprintf(outfile,"%d\t",px->p_pid);
8694887Schin 		if(px->p_flag&P_SIGNALLED)
8704887Schin 			msg = job_sigmsg((int)(px->p_exit));
8714887Schin 		else if(px->p_flag&P_NOTIFY)
8724887Schin 		{
8734887Schin 			msg = sh_translate(e_done);
8744887Schin 			n = px->p_exit;
8754887Schin 		}
8764887Schin 		else
8774887Schin 			msg = sh_translate(e_running);
8784887Schin 		px->p_flag &= ~P_NOTIFY;
8794887Schin 		sfputr(outfile,msg,-1);
8804887Schin 		msize = strlen(msg);
8814887Schin 		if(n)
8824887Schin 		{
8834887Schin 			sfprintf(outfile,"(%d)",(int)n);
8844887Schin 			msize += (3+(n>10)+(n>100));
8854887Schin 		}
8864887Schin 		if(px->p_flag&P_COREDUMP)
8874887Schin 		{
8884887Schin 			msg = sh_translate(e_coredump);
8894887Schin 			sfputr(outfile, msg, -1);
8904887Schin 			msize += strlen(msg);
8914887Schin 		}
8924887Schin 		sfnputc(outfile,' ',MAXMSG>msize?MAXMSG-msize:1);
8934887Schin 		if(flag&JOB_LFLAG)
8944887Schin 			px = px->p_nxtproc;
8954887Schin 		else
8964887Schin 		{
8974887Schin 			while(px=px->p_nxtproc)
8984887Schin 				px->p_flag &= ~P_NOTIFY;
8994887Schin 			px = 0;
9004887Schin 		}
9014887Schin 		if(!px)
9024887Schin 			hist_list(sh.hist_ptr,outfile,pw->p_name,0,";");
9034887Schin 		else
9044887Schin 			sfputr(outfile, e_nlspace, -1);
9054887Schin 	}
9064887Schin 	while(px);
9074887Schin 	job_unlock();
9084887Schin 	return(0);
9094887Schin }
9104887Schin 
9114887Schin /*
9124887Schin  * get the process group given the job number
9134887Schin  * This routine returns the process group number or -1
9144887Schin  */
9154887Schin static struct process *job_bystring(register char *ajob)
9164887Schin {
9174887Schin 	register struct process *pw=job.pwlist;
9184887Schin 	register int c;
9194887Schin 	if(*ajob++ != '%' || !pw)
9204887Schin 		return(NIL(struct process*));
9214887Schin 	c = *ajob;
9224887Schin 	if(isdigit(c))
9234887Schin 		pw = job_byjid((int)strtol(ajob, (char**)0, 10));
9244887Schin 	else if(c=='+' || c=='%')
9254887Schin 		;
9264887Schin 	else if(c=='-')
9274887Schin 	{
9284887Schin 		if(pw)
9294887Schin 			pw = job.pwlist->p_nxtjob;
9304887Schin 	}
9314887Schin 	else
9324887Schin 		pw = job_byname(ajob);
9334887Schin 	if(pw && pw->p_flag)
9344887Schin 		return(pw);
9354887Schin 	return(NIL(struct process*));
9364887Schin }
9374887Schin 
9384887Schin /*
9394887Schin  * Kill a job or process
9404887Schin  */
9414887Schin 
9424887Schin int job_kill(register struct process *pw,register int sig)
9434887Schin {
9444887Schin 	register pid_t pid;
9454887Schin 	register int r;
9464887Schin 	const char *msg;
9474887Schin #ifdef SIGTSTP
9484887Schin 	int stopsig = (sig==SIGSTOP||sig==SIGTSTP||sig==SIGTTIN||sig==SIGTTOU);
9494887Schin #else
9504887Schin #	define stopsig	1
9514887Schin #endif	/* SIGTSTP */
9524887Schin 	job_lock();
9534887Schin 	errno = ECHILD;
9544887Schin 	if(pw==0)
9554887Schin 		goto error;
9564887Schin 	pid = pw->p_pid;
9574887Schin 	if(by_number)
9584887Schin 	{
9594887Schin 		if(pid==0 && job.jobcontrol)
9604887Schin 			r = job_walk(outfile, job_kill,sig, (char**)0);
9614887Schin #ifdef SIGTSTP
9624887Schin 		if(sig==SIGSTOP && pid==sh.pid && sh.ppid==1)
9634887Schin 		{
9644887Schin 			/* can't stop login shell */
9654887Schin 			errno = EPERM;
9664887Schin 			r = -1;
9674887Schin 		}
9684887Schin 		else
9694887Schin 		{
9704887Schin 			if(pid>=0)
9714887Schin 			{
9724887Schin 				if((r = kill(pid,sig))>=0 && !stopsig)
9734887Schin 				{
9744887Schin 					if(pw->p_flag&P_STOPPED)
9754887Schin 						pw->p_flag &= ~(P_STOPPED|P_SIGNALLED);
9764887Schin 					if(sig)
9774887Schin 						kill(pid,SIGCONT);
9784887Schin 				}
9794887Schin 			}
9804887Schin 			else
9814887Schin 			{
9824887Schin 				if((r = killpg(-pid,sig))>=0 && !stopsig)
9834887Schin 				{
9844887Schin 					job_unstop(job_bypid(pw->p_pid));
9854887Schin 					if(sig)
9864887Schin 						killpg(-pid,SIGCONT);
9874887Schin 				}
9884887Schin 			}
9894887Schin 		}
9904887Schin #else
9914887Schin 		if(pid>=0)
9924887Schin 			r = kill(pid,sig);
9934887Schin 		else
9944887Schin 			r = killpg(-pid,sig);
9954887Schin #endif	/* SIGTSTP */
9964887Schin 	}
9974887Schin 	else
9984887Schin 	{
9994887Schin 		if(pid = pw->p_pgrp)
10004887Schin 		{
10014887Schin 			r = killpg(pid,sig);
10024887Schin #ifdef SIGTSTP
10034887Schin 			if(r>=0 && (sig==SIGHUP||sig==SIGTERM || sig==SIGCONT))
10044887Schin 				job_unstop(pw);
10054887Schin #endif	/* SIGTSTP */
10064887Schin 			if(r>=0)
10074887Schin 				sh_delay(.05);
10084887Schin 		}
10094887Schin 		while(pw && pw->p_pgrp==0 && (r=kill(pw->p_pid,sig))>=0)
10104887Schin 		{
10114887Schin #ifdef SIGTSTP
10124887Schin 			if(sig==SIGHUP || sig==SIGTERM)
10134887Schin 				kill(pw->p_pid,SIGCONT);
10144887Schin #endif	/* SIGTSTP */
10154887Schin 			pw = pw->p_nxtproc;
10164887Schin 		}
10174887Schin 	}
10184887Schin 	if(r<0 && job_string)
10194887Schin 	{
10204887Schin 	error:
10214887Schin 		if(pw && by_number)
10224887Schin 			msg = sh_translate(e_no_proc);
10234887Schin 		else
10244887Schin 			msg = sh_translate(e_no_job);
10254887Schin 		if(errno == EPERM)
10264887Schin 			msg = sh_translate(e_access);
10274887Schin 		sfprintf(sfstderr,"kill: %s: %s\n",job_string, msg);
10284887Schin 		r = 2;
10294887Schin 	}
10304887Schin 	sh_delay(.001);
10314887Schin 	job_unlock();
10324887Schin 	return(r);
10334887Schin }
10344887Schin 
10354887Schin /*
10364887Schin  * Get process structure from first letters of jobname
10374887Schin  *
10384887Schin  */
10394887Schin 
10404887Schin static struct process *job_byname(char *name)
10414887Schin {
10424887Schin 	register struct process *pw = job.pwlist;
10434887Schin 	register struct process *pz = 0;
10444887Schin 	register int *flag = 0;
10454887Schin 	register char *cp = name;
10464887Schin 	int offset;
10474887Schin 	if(!sh.hist_ptr)
10484887Schin 		return(NIL(struct process*));
10494887Schin 	if(*cp=='?')
10504887Schin 		cp++,flag= &offset;
10514887Schin 	for(;pw;pw=pw->p_nxtjob)
10524887Schin 	{
10534887Schin 		if(hist_match(sh.hist_ptr,pw->p_name,cp,flag)>=0)
10544887Schin 		{
10554887Schin 			if(pz)
10564887Schin 				errormsg(SH_DICT,ERROR_exit(1),e_jobusage,name-1);
10574887Schin 			pz = pw;
10584887Schin 		}
10594887Schin 	}
10604887Schin 	return(pz);
10614887Schin }
10624887Schin 
10634887Schin #else
10644887Schin #   define job_set(x)
10654887Schin #   define job_reset(x)
10664887Schin #endif /* JOBS */
10674887Schin 
10684887Schin 
10694887Schin 
10704887Schin /*
10714887Schin  * Initialize the process posting array
10724887Schin  */
10734887Schin 
10744887Schin void	job_clear(void)
10754887Schin {
10764887Schin 	register struct process *pw, *px;
10774887Schin 	register struct process *pwnext;
10784887Schin 	register int j = BYTE(sh.lim.child_max);
10794887Schin 	register struct jobsave *jp,*jpnext;
10804887Schin 	job_lock();
10814887Schin 	for(pw=job.pwlist; pw; pw=pwnext)
10824887Schin 	{
10834887Schin 		pwnext = pw->p_nxtjob;
10844887Schin 		while(px=pw)
10854887Schin 		{
10864887Schin 			pw = pw->p_nxtproc;
10874887Schin 			free((void*)px);
10884887Schin 		}
10894887Schin 	}
10904887Schin 	for(jp=bck.list; jp;jp=jpnext)
10914887Schin 	{
10924887Schin 		jpnext = jp->next;
10934887Schin 		free((void*)jp);
10944887Schin 	}
10954887Schin 	bck.list = 0;
10964887Schin 	if(njob_savelist < NJOB_SAVELIST)
10974887Schin 		init_savelist();
10984887Schin 	job.pwlist = NIL(struct process*);
10994887Schin 	job.numpost=0;
1100*10898Sroland.mainz@nrubsig.org #ifdef SHOPT_BGX
1101*10898Sroland.mainz@nrubsig.org 	job.numbjob = 0;
1102*10898Sroland.mainz@nrubsig.org #endif /* SHOPT_BGX */
11034887Schin 	job.waitall = 0;
11044887Schin 	job.curpgid = 0;
11054887Schin 	job.toclear = 0;
11064887Schin 	if(!job.freejobs)
11074887Schin 		job.freejobs = (unsigned char*)malloc((unsigned)(j+1));
11084887Schin 	while(j >=0)
11094887Schin 		job.freejobs[j--]  = 0;
11104887Schin 	job_unlock();
11114887Schin }
11124887Schin 
11134887Schin /*
11144887Schin  * put the process <pid> on the process list and return the job number
11154887Schin  * if non-zero, <join> is the process id of the job to join
11164887Schin  */
11174887Schin 
11184887Schin int job_post(pid_t pid, pid_t join)
11194887Schin {
11204887Schin 	register struct process *pw;
11214887Schin 	register History_t *hp = sh.hist_ptr;
1122*10898Sroland.mainz@nrubsig.org #ifdef SHOPT_BGX
1123*10898Sroland.mainz@nrubsig.org 	int val,bg=0;
1124*10898Sroland.mainz@nrubsig.org #else
11258462SApril.Chin@Sun.COM 	int val;
1126*10898Sroland.mainz@nrubsig.org #endif
11274887Schin 	sh.jobenv = sh.curenv;
11284887Schin 	if(job.toclear)
11294887Schin 	{
11304887Schin 		job_clear();
11314887Schin 		return(0);
11324887Schin 	}
11334887Schin 	job_lock();
1134*10898Sroland.mainz@nrubsig.org #ifdef SHOPT_BGX
1135*10898Sroland.mainz@nrubsig.org 	if(join==1)
1136*10898Sroland.mainz@nrubsig.org 	{
1137*10898Sroland.mainz@nrubsig.org 		join = 0;
1138*10898Sroland.mainz@nrubsig.org 		bg = P_BG;
1139*10898Sroland.mainz@nrubsig.org 		job.numbjob++;
1140*10898Sroland.mainz@nrubsig.org 	}
1141*10898Sroland.mainz@nrubsig.org #endif /* SHOPT_BGX */
1142*10898Sroland.mainz@nrubsig.org 	if(njob_savelist < NJOB_SAVELIST)
1143*10898Sroland.mainz@nrubsig.org 		init_savelist();
11444887Schin 	if(pw = job_bypid(pid))
11454887Schin 		job_unpost(pw,0);
11464887Schin 	if(join && (pw=job_bypid(join)))
11474887Schin 	{
11484887Schin 		/* if job to join is not first move it to front */
11494887Schin 		if((pw=job_byjid(pw->p_job)) != job.pwlist)
11504887Schin 		{
11514887Schin 			job_unlink(pw);
11524887Schin 			pw->p_nxtjob = job.pwlist;
11534887Schin 			job.pwlist = pw;
11544887Schin 		}
11554887Schin 	}
11564887Schin 	if(pw=freelist)
11574887Schin 		freelist = pw->p_nxtjob;
11584887Schin 	else
11594887Schin 		pw = new_of(struct process,0);
1160*10898Sroland.mainz@nrubsig.org 	pw->p_flag = 0;
11614887Schin 	job.numpost++;
11624887Schin 	if(join && job.pwlist)
11634887Schin 	{
11644887Schin 		/* join existing current job */
11654887Schin 		pw->p_nxtjob = job.pwlist->p_nxtjob;
11664887Schin 		pw->p_nxtproc = job.pwlist;
11674887Schin 		pw->p_job = job.pwlist->p_job;
11684887Schin 	}
11694887Schin 	else
11704887Schin 	{
11714887Schin 		/* create a new job */
11724887Schin 		while((pw->p_job = job_alloc()) < 0)
11734887Schin 			job_wait((pid_t)1);
11744887Schin 		pw->p_nxtjob = job.pwlist;
11754887Schin 		pw->p_nxtproc = 0;
11764887Schin 	}
11774887Schin 	job.pwlist = pw;
11784887Schin 	pw->p_env = sh.curenv;
11794887Schin 	pw->p_pid = pid;
1180*10898Sroland.mainz@nrubsig.org 	if(!sh.outpipe || sh_isoption(SH_PIPEFAIL))
1181*10898Sroland.mainz@nrubsig.org 		pw->p_flag = P_EXITSAVE;
11828462SApril.Chin@Sun.COM 	pw->p_exitmin = sh.xargexit;
11838462SApril.Chin@Sun.COM 	pw->p_exit = 0;
11844887Schin 	if(sh_isstate(SH_MONITOR))
11854887Schin 	{
11864887Schin 		if(killpg(job.curpgid,0)<0 && errno==ESRCH)
11874887Schin 			job.curpgid = pid;
11884887Schin 		pw->p_fgrp = job.curpgid;
11894887Schin 	}
11904887Schin 	else
11914887Schin 		pw->p_fgrp = 0;
11924887Schin 	pw->p_pgrp = pw->p_fgrp;
11934887Schin #ifdef DEBUG
11944887Schin 	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,
11954887Schin 		pw->p_pid,pw->p_pgrp,job.savesig,join);
11964887Schin 	sfsync(sfstderr);
11974887Schin #endif /* DEBUG */
11984887Schin #ifdef JOBS
11994887Schin 	if(hp && !sh_isstate(SH_PROFILE))
12004887Schin 		pw->p_name=hist_tell(sh.hist_ptr,(int)hp->histind-1);
12014887Schin 	else
12024887Schin 		pw->p_name = -1;
12034887Schin #endif /* JOBS */
12048462SApril.Chin@Sun.COM 	if ((val = job_chksave(pid)) >= 0)
12054887Schin 	{
12068462SApril.Chin@Sun.COM 		pw->p_exit = val;
12074887Schin 		if(pw->p_exit==SH_STOPSIG)
12084887Schin 		{
12094887Schin 			pw->p_flag |= (P_SIGNALLED|P_STOPPED);
12104887Schin 			pw->p_exit = 0;
12114887Schin 		}
1212*10898Sroland.mainz@nrubsig.org 		else if(pw->p_exit >= SH_EXITSIG)
1213*10898Sroland.mainz@nrubsig.org 		{
1214*10898Sroland.mainz@nrubsig.org 			pw->p_flag |= P_DONE|P_SIGNALLED;
1215*10898Sroland.mainz@nrubsig.org 			pw->p_exit &= SH_EXITMASK;
1216*10898Sroland.mainz@nrubsig.org 		}
12174887Schin 		else
12184887Schin 			pw->p_flag |= (P_DONE|P_NOTIFY);
12194887Schin 	}
1220*10898Sroland.mainz@nrubsig.org #ifdef SHOPT_BGX
1221*10898Sroland.mainz@nrubsig.org 	if(bg && !(pw->p_flag&P_DONE))
1222*10898Sroland.mainz@nrubsig.org 		pw->p_flag |= P_BG;
1223*10898Sroland.mainz@nrubsig.org #endif /* SHOPT_BGX */
12244887Schin 	lastpid = 0;
12254887Schin 	job_unlock();
12264887Schin 	return(pw->p_job);
12274887Schin }
12284887Schin 
12294887Schin /*
12304887Schin  * Returns a process structure give a process id
12314887Schin  */
12324887Schin 
12334887Schin static struct process *job_bypid(pid_t pid)
12344887Schin {
12354887Schin 	register struct process  *pw, *px;
12364887Schin 	for(pw=job.pwlist; pw; pw=pw->p_nxtjob)
12374887Schin 		for(px=pw; px; px=px->p_nxtproc)
12384887Schin 		{
12394887Schin 			if(px->p_pid==pid)
12404887Schin 				return(px);
12414887Schin 		}
12424887Schin 	return(NIL(struct process*));
12434887Schin }
12444887Schin 
12454887Schin /*
12464887Schin  * return a pointer to a job given the job id
12474887Schin  */
12484887Schin 
12494887Schin static struct process *job_byjid(int jobid)
12504887Schin {
12514887Schin 	register struct process *pw;
12524887Schin 	for(pw=job.pwlist;pw; pw = pw->p_nxtjob)
12534887Schin 	{
12544887Schin 		if(pw->p_job==jobid)
12554887Schin 			break;
12564887Schin 	}
12574887Schin 	return(pw);
12584887Schin }
12594887Schin 
12604887Schin /*
12614887Schin  * print a signal message
12624887Schin  */
12634887Schin static void job_prmsg(register struct process *pw)
12644887Schin {
12654887Schin 	if(pw->p_exit!=SIGINT && pw->p_exit!=SIGPIPE)
12664887Schin 	{
12674887Schin 		register const char *msg, *dump;
12684887Schin 		msg = job_sigmsg((int)(pw->p_exit));
12694887Schin 		msg = sh_translate(msg);
12704887Schin 		if(pw->p_flag&P_COREDUMP)
12714887Schin 			dump =  sh_translate(e_coredump);
12724887Schin 		else
12734887Schin 			dump = "";
12744887Schin 		if(sh_isstate(SH_INTERACTIVE))
12754887Schin 			sfprintf(sfstderr,"%s%s\n",msg,dump);
12764887Schin 		else
12774887Schin 			errormsg(SH_DICT,2,"%d: %s%s",pw->p_pid,msg,dump);
12784887Schin 	}
12794887Schin }
12804887Schin 
12814887Schin /*
12824887Schin  * Wait for process pid to complete
12834887Schin  * If pid < -1, then wait can be interrupted, -pid is waited for (wait builtin)
12844887Schin  * pid=0 to unpost all done processes
12854887Schin  * pid=1 to wait for at least one process to complete
12864887Schin  * pid=-1 to wait for all runing processes
12874887Schin  */
12884887Schin 
12898462SApril.Chin@Sun.COM int	job_wait(register pid_t pid)
12904887Schin {
12914887Schin 	register struct process *pw=0,*px;
12924887Schin 	register int	jobid = 0;
12938462SApril.Chin@Sun.COM 	int		nochild = 1;
12944887Schin 	char		intr = 0;
12954887Schin 	if(pid <= 0)
12964887Schin 	{
12974887Schin 		if(pid==0)
12984887Schin 			goto done;
12994887Schin 		pid = -pid;
13004887Schin 		intr = 1;
13014887Schin 	}
13024887Schin 	job_lock();
13034887Schin 	if(pid > 1)
13044887Schin 	{
13058462SApril.Chin@Sun.COM 		if(pid==sh.spid)
13068462SApril.Chin@Sun.COM 			sh.spid = 0;
13074887Schin 		if(!(pw=job_bypid(pid)))
13084887Schin 		{
13094887Schin 			/* check to see whether job status has been saved */
13104887Schin 			if((sh.exitval = job_chksave(pid)) < 0)
13114887Schin 				sh.exitval = ERROR_NOENT;
13124887Schin 			exitset();
13134887Schin 			job_unlock();
13148462SApril.Chin@Sun.COM 			return(nochild);
13154887Schin 		}
13164887Schin 		else if(intr && pw->p_env!=sh.curenv)
13174887Schin 		{
13184887Schin 			sh.exitval = ERROR_NOENT;
13194887Schin 			job_unlock();
13208462SApril.Chin@Sun.COM 			return(nochild);
13214887Schin 		}
13224887Schin 		jobid = pw->p_job;
13234887Schin 		if(!intr)
13244887Schin 			pw->p_flag &= ~P_EXITSAVE;
13254887Schin 		if(pw->p_pgrp && job.parent!= (pid_t)-1)
13264887Schin 			job_set(job_byjid(jobid));
13274887Schin 	}
13288462SApril.Chin@Sun.COM 	pwfg = pw;
13294887Schin #ifdef DEBUG
13304887Schin 	sfprintf(sfstderr,"ksh: job line %4d: wait pid=%d critical=%d job=%d pid=%d\n",__LINE__,getpid(),job.in_critical,jobid,pid);
13314887Schin 	if(pw)
13324887Schin 		sfprintf(sfstderr,"ksh: job line %4d: wait pid=%d critical=%d flags=%o\n",__LINE__,getpid(),job.in_critical,pw->p_flag);
13334887Schin #endif /* DEBUG*/
13344887Schin 	errno = 0;
13358462SApril.Chin@Sun.COM 	if(sh.coutpipe>=0 && sh.cpid==lastpid)
13368462SApril.Chin@Sun.COM 	{
13378462SApril.Chin@Sun.COM 		sh_close(sh.coutpipe);
13388462SApril.Chin@Sun.COM 		sh_close(sh.cpipe[1]);
13398462SApril.Chin@Sun.COM 		sh.cpipe[1] = sh.coutpipe = -1;
13408462SApril.Chin@Sun.COM 	}
13414887Schin 	while(1)
13424887Schin 	{
13434887Schin 		if(job.waitsafe)
13444887Schin 		{
13454887Schin 			for(px=job.pwlist;px; px = px->p_nxtjob)
13464887Schin 			{
13474887Schin 				if(px!=pw && (px->p_flag&P_NOTIFY))
13484887Schin 				{
13494887Schin 					if(sh_isoption(SH_NOTIFY))
13504887Schin 					{
13514887Schin 						outfile = sfstderr;
13524887Schin 						job_list(px,JOB_NFLAG|JOB_NLFLAG);
13534887Schin 						sfsync(sfstderr);
13544887Schin 					}
13554887Schin 					else if(!sh_isoption(SH_INTERACTIVE) && (px->p_flag&P_SIGNALLED))
13564887Schin 					{
13574887Schin 						job_prmsg(px);
13584887Schin 						px->p_flag &= ~P_NOTIFY;
13594887Schin 					}
13604887Schin 				}
13614887Schin 			}
13624887Schin 		}
13634887Schin 		if(pw && (pw->p_flag&(P_DONE|P_STOPPED)))
13644887Schin 		{
13654887Schin #ifdef SIGTSTP
13664887Schin 			if(pw->p_flag&P_STOPPED)
13674887Schin 			{
13684887Schin 				pw->p_flag |= P_EXITSAVE;
13694887Schin 				if(sh_isoption(SH_INTERACTIVE) && !sh_isstate(SH_FORKED))
13704887Schin 				{
13714887Schin 					if( pw->p_exit!=SIGTTIN && pw->p_exit!=SIGTTOU)
13724887Schin 						break;
13734887Schin 
13744887Schin 					killpg(pw->p_pgrp,SIGCONT);
13754887Schin 				}
13764887Schin 				else /* ignore stop when non-interactive */
13774887Schin 					pw->p_flag &= ~(P_NOTIFY|P_SIGNALLED|P_STOPPED|P_EXITSAVE);
13784887Schin 			}
13794887Schin 			else
13804887Schin #endif /* SIGTSTP */
13814887Schin 			{
13824887Schin 				if(pw->p_flag&P_SIGNALLED)
13834887Schin 				{
13844887Schin 					pw->p_flag &= ~P_NOTIFY;
13854887Schin 					job_prmsg(pw);
13864887Schin 				}
13874887Schin 				else if(pw->p_flag&P_DONE)
13884887Schin 					pw->p_flag &= ~P_NOTIFY;
13894887Schin 				if(pw->p_job==jobid)
13904887Schin 				{
13914887Schin 					px = job_byjid(jobid);
13924887Schin 					/* last process in job */
13934887Schin 					if(sh_isoption(SH_PIPEFAIL))
13944887Schin 					{
13954887Schin 						/* last non-zero exit */
13964887Schin 						for(;px;px=px->p_nxtproc)
13974887Schin 						{
13984887Schin 							if(px->p_exit)
13994887Schin 								break;
14004887Schin 						}
14014887Schin 						if(!px)
14024887Schin 							px = pw;
14034887Schin 					}
14044887Schin 					else if(px!=pw)
14054887Schin 						px = 0;
14064887Schin 					if(px)
14074887Schin 					{
14084887Schin 						sh.exitval=px->p_exit;
14094887Schin 						if(px->p_flag&P_SIGNALLED)
14104887Schin 							sh.exitval |= SH_EXITSIG;
14114887Schin 						if(intr)
14124887Schin 							px->p_flag &= ~P_EXITSAVE;
14134887Schin 					}
14144887Schin 				}
1415*10898Sroland.mainz@nrubsig.org 				px = job_unpost(pw,1);
1416*10898Sroland.mainz@nrubsig.org 				if(!px || !sh_isoption(SH_PIPEFAIL))
14174887Schin 					break;
14184887Schin 				pw = px;
14194887Schin 				continue;
14204887Schin 			}
14214887Schin 		}
14224887Schin 		sfsync(sfstderr);
14234887Schin 		job.waitsafe = 0;
14244887Schin 		nochild = job_reap(job.savesig);
14254887Schin 		if(job.waitsafe)
14264887Schin 			continue;
14274887Schin 		if(nochild)
14284887Schin 			break;
14294887Schin 		if(sh.sigflag[SIGALRM]&SH_SIGTRAP)
14304887Schin 			sh_timetraps();
14314887Schin 		if((intr && sh.trapnote) || (pid==1 && !intr))
14324887Schin 			break;
14334887Schin 	}
14348462SApril.Chin@Sun.COM 	pwfg = 0;
14354887Schin 	job_unlock();
14364887Schin 	if(pid==1)
14378462SApril.Chin@Sun.COM 		return(nochild);
14384887Schin 	exitset();
14394887Schin 	if(pw->p_pgrp)
14404887Schin 	{
14414887Schin 		job_reset(pw);
14424887Schin 		/* propogate keyboard interrupts to parent */
14434887Schin 		if((pw->p_flag&P_SIGNALLED) && pw->p_exit==SIGINT && !(sh.sigflag[SIGINT]&SH_SIGOFF))
14444887Schin 			sh_fault(SIGINT);
14454887Schin #ifdef SIGTSTP
14464887Schin 		else if((pw->p_flag&P_STOPPED) && pw->p_exit==SIGTSTP)
14474887Schin 		{
14484887Schin 			job.parent = 0;
14494887Schin 			sh_fault(SIGTSTP);
14504887Schin 		}
14514887Schin #endif /* SIGTSTP */
14524887Schin 	}
14534887Schin 	else
14548462SApril.Chin@Sun.COM 	{
14558462SApril.Chin@Sun.COM 		if(pw->p_pid == tcgetpgrp(JOBTTY))
14568462SApril.Chin@Sun.COM 		{
14578462SApril.Chin@Sun.COM 			if(pw->p_pgrp==0)
14588462SApril.Chin@Sun.COM 				pw->p_pgrp = pw->p_pid;
14598462SApril.Chin@Sun.COM 			job_reset(pw);
14608462SApril.Chin@Sun.COM 		}
14614887Schin 		tty_set(-1, 0, NIL(struct termios*));
14628462SApril.Chin@Sun.COM 	}
14634887Schin done:
14644887Schin 	if(!job.waitall && sh_isoption(SH_PIPEFAIL))
14658462SApril.Chin@Sun.COM 		return(nochild);
14664887Schin 	if(!sh.intrap)
14674887Schin 	{
14684887Schin 		job_lock();
14694887Schin 		for(pw=job.pwlist; pw; pw=px)
14704887Schin 		{
14714887Schin 			px = pw->p_nxtjob;
14724887Schin 			job_unpost(pw,0);
14734887Schin 		}
14744887Schin 		job_unlock();
14754887Schin 	}
14768462SApril.Chin@Sun.COM 	return(nochild);
14774887Schin }
14784887Schin 
14794887Schin /*
14804887Schin  * move job to foreground if bgflag == 'f'
14814887Schin  * move job to background if bgflag == 'b'
14824887Schin  * disown job if bgflag == 'd'
14834887Schin  */
14844887Schin 
14854887Schin int job_switch(register struct process *pw,int bgflag)
14864887Schin {
14874887Schin 	register const char *msg;
14884887Schin 	job_lock();
14894887Schin 	if(!pw || !(pw=job_byjid((int)pw->p_job)))
14904887Schin 	{
14914887Schin 		job_unlock();
14924887Schin 		return(1);
14934887Schin 	}
14944887Schin 	if(bgflag=='d')
14954887Schin 	{
14964887Schin 		for(; pw; pw=pw->p_nxtproc)
14974887Schin 			pw->p_flag |= P_DISOWN;
14984887Schin 		job_unlock();
14994887Schin 		return(0);
15004887Schin 	}
15014887Schin #ifdef SIGTSTP
15024887Schin 	if(bgflag=='b')
15034887Schin 	{
15044887Schin 		sfprintf(outfile,"[%d]\t",(int)pw->p_job);
15054887Schin 		sh.bckpid = pw->p_pid;
1506*10898Sroland.mainz@nrubsig.org #ifdef SHOPT_BGX
1507*10898Sroland.mainz@nrubsig.org 		pw->p_flag |= P_BG;
1508*10898Sroland.mainz@nrubsig.org #endif
15094887Schin 		msg = "&";
15104887Schin 	}
15114887Schin 	else
15124887Schin 	{
15134887Schin 		job_unlink(pw);
15144887Schin 		pw->p_nxtjob = job.pwlist;
15154887Schin 		job.pwlist = pw;
15164887Schin 		msg = "";
15174887Schin 	}
15184887Schin 	hist_list(sh.hist_ptr,outfile,pw->p_name,'&',";");
15194887Schin 	sfputr(outfile,msg,'\n');
15204887Schin 	sfsync(outfile);
15214887Schin 	if(bgflag=='f')
15224887Schin 	{
15234887Schin 		if(!(pw=job_unpost(pw,1)))
15244887Schin 		{
15254887Schin 			job_unlock();
15264887Schin 			return(1);
15274887Schin 		}
15284887Schin 		job.waitall = 1;
15294887Schin 		pw->p_flag |= P_FG;
1530*10898Sroland.mainz@nrubsig.org #ifdef SHOPT_BGX
1531*10898Sroland.mainz@nrubsig.org 		pw->p_flag &= ~P_BG;
1532*10898Sroland.mainz@nrubsig.org #endif
15334887Schin 		job_wait(pw->p_pid);
15344887Schin 		job.waitall = 0;
15354887Schin 	}
15364887Schin 	else if(pw->p_flag&P_STOPPED)
15374887Schin 		job_unstop(pw);
15384887Schin #endif /* SIGTSTP */
15394887Schin 	job_unlock();
15404887Schin 	return(0);
15414887Schin }
15424887Schin 
15434887Schin 
15444887Schin #ifdef SIGTSTP
15454887Schin /*
15464887Schin  * Set the foreground group associated with a job
15474887Schin  */
15484887Schin 
15494887Schin static void job_fgrp(register struct process *pw, int newgrp)
15504887Schin {
15514887Schin 	for(; pw; pw=pw->p_nxtproc)
15524887Schin 		pw->p_fgrp = newgrp;
15534887Schin }
15544887Schin 
15554887Schin /*
15564887Schin  * turn off STOP state of a process group and send CONT signals
15574887Schin  */
15584887Schin 
15594887Schin static void job_unstop(register struct process *px)
15604887Schin {
15614887Schin 	register struct process *pw;
15624887Schin 	register int num = 0;
15634887Schin 	for(pw=px ;pw ;pw=pw->p_nxtproc)
15644887Schin 	{
15654887Schin 		if(pw->p_flag&P_STOPPED)
15664887Schin 		{
15674887Schin 			num++;
15684887Schin 			pw->p_flag &= ~(P_STOPPED|P_SIGNALLED|P_NOTIFY);
15694887Schin 		}
15704887Schin 	}
15714887Schin 	if(num!=0)
15724887Schin 	{
15734887Schin 		if(px->p_fgrp != px->p_pgrp)
15744887Schin 			killpg(px->p_fgrp,SIGCONT);
15754887Schin 		killpg(px->p_pgrp,SIGCONT);
15764887Schin 	}
15774887Schin }
15784887Schin #endif	/* SIGTSTP */
15794887Schin 
15804887Schin /*
15814887Schin  * remove a job from table
15824887Schin  * If all the processes have not completed, unpost first non-completed  process
15834887Schin  * Otherwise the job is removed and job_unpost returns NULL.
15844887Schin  * pwlist is reset if the first job is removed
15854887Schin  * if <notify> is non-zero, then jobs with pending notifications are unposted
15864887Schin  */
15874887Schin 
15884887Schin static struct process *job_unpost(register struct process *pwtop,int notify)
15894887Schin {
15904887Schin 	register struct process *pw;
15914887Schin 	/* make sure all processes are done */
15924887Schin #ifdef DEBUG
15934887Schin 	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);
15944887Schin 	sfsync(sfstderr);
15954887Schin #endif /* DEBUG */
15964887Schin 	pwtop = pw = job_byjid((int)pwtop->p_job);
1597*10898Sroland.mainz@nrubsig.org #ifdef SHOPT_BGX
1598*10898Sroland.mainz@nrubsig.org 	if(pw->p_flag&P_BG)
1599*10898Sroland.mainz@nrubsig.org 		return(pw);
1600*10898Sroland.mainz@nrubsig.org #endif /* SHOPT_BGX */
16014887Schin 	for(; pw && (pw->p_flag&P_DONE)&&(notify||!(pw->p_flag&P_NOTIFY)||pw->p_env); pw=pw->p_nxtproc);
16024887Schin 	if(pw)
16034887Schin 		return(pw);
16044887Schin 	/* all processes complete, unpost job */
16054887Schin 	job_unlink(pwtop);
16064887Schin 	for(pw=pwtop; pw; pw=pw->p_nxtproc)
16074887Schin 	{
16084887Schin 		/* save the exit status for background jobs */
16098462SApril.Chin@Sun.COM 		if((pw->p_flag&P_EXITSAVE) ||  pw->p_pid==sh.spid)
16104887Schin 		{
16114887Schin 			struct jobsave *jp;
16124887Schin 			/* save status for future wait */
16134887Schin 			if(jp = jobsave_create(pw->p_pid))
16144887Schin 			{
16154887Schin 				jp->exitval = pw->p_exit;
16164887Schin 				if(pw->p_flag&P_SIGNALLED)
16174887Schin 					jp->exitval |= SH_EXITSIG;
16184887Schin 			}
16194887Schin 			pw->p_flag &= ~P_EXITSAVE;
16204887Schin 		}
16214887Schin 		pw->p_flag &= ~P_DONE;
16224887Schin 		job.numpost--;
16234887Schin 		pw->p_nxtjob = freelist;
16244887Schin 		freelist = pw;
16254887Schin 	}
16264887Schin #ifdef DEBUG
16274887Schin 	sfprintf(sfstderr,"ksh: job line %4d: free pid=%d critical=%d job=%d\n",__LINE__,getpid(),job.in_critical,pwtop->p_job);
16284887Schin 	sfsync(sfstderr);
16294887Schin #endif /* DEBUG */
16304887Schin 	job_free((int)pwtop->p_job);
16314887Schin 	return((struct process*)0);
16324887Schin }
16334887Schin 
16344887Schin /*
16354887Schin  * unlink a job form the job list
16364887Schin  */
16374887Schin static void job_unlink(register struct process *pw)
16384887Schin {
16394887Schin 	register struct process *px;
16404887Schin 	if(pw==job.pwlist)
16414887Schin 	{
16424887Schin 		job.pwlist = pw->p_nxtjob;
16434887Schin 		job.curpgid = 0;
16444887Schin 		return;
16454887Schin 	}
16464887Schin 	for(px=job.pwlist;px;px=px->p_nxtjob)
16474887Schin 		if(px->p_nxtjob == pw)
16484887Schin 		{
16494887Schin 			px->p_nxtjob = pw->p_nxtjob;
16504887Schin 			return;
16514887Schin 		}
16524887Schin }
16534887Schin 
16544887Schin /*
16554887Schin  * get an unused job number
16564887Schin  * freejobs is a bit vector, 0 is unused
16574887Schin  */
16584887Schin 
16594887Schin static int job_alloc(void)
16604887Schin {
16614887Schin 	register int j=0;
16624887Schin 	register unsigned mask = 1;
16634887Schin 	register unsigned char *freeword;
16644887Schin 	register int jmax = BYTE(sh.lim.child_max);
16654887Schin 	/* skip to first word with a free slot */
16664887Schin 	for(j=0;job.freejobs[j] == UCHAR_MAX; j++);
16674887Schin 	if(j >= jmax)
16684887Schin 	{
16694887Schin 		register struct process *pw;
16704887Schin 		for(j=1; j < sh.lim.child_max; j++)
16714887Schin 		{
16724887Schin 			if((pw=job_byjid(j))&& !job_unpost(pw,0))
16734887Schin 				break;
16744887Schin 		}
16754887Schin 		j /= CHAR_BIT;
16764887Schin 		if(j >= jmax)
16774887Schin 			return(-1);
16784887Schin 	}
16794887Schin 	freeword = &job.freejobs[j];
16804887Schin 	j *= CHAR_BIT;
16814887Schin 	for(j++;mask&(*freeword);j++,mask <<=1);
16824887Schin 	*freeword  |= mask;
16834887Schin 	return(j);
16844887Schin }
16854887Schin 
16864887Schin /*
16874887Schin  * return a job number
16884887Schin  */
16894887Schin 
16904887Schin static void job_free(register int n)
16914887Schin {
16924887Schin 	register int j = (--n)/CHAR_BIT;
16934887Schin 	register unsigned mask;
16944887Schin 	n -= j*CHAR_BIT;
16954887Schin 	mask = 1 << n;
16964887Schin 	job.freejobs[j]  &= ~mask;
16974887Schin }
16984887Schin 
16994887Schin static char *job_sigmsg(int sig)
17004887Schin {
17014887Schin 	static char signo[40];
17024887Schin #ifdef apollo
17034887Schin 	/*
17044887Schin 	 * This code handles the formatting for the apollo specific signal
17054887Schin 	 * SIGAPOLLO.
17064887Schin 	 */
17074887Schin 	extern char *apollo_error(void);
17084887Schin 
17094887Schin 	if ( sig == SIGAPOLLO )
17104887Schin 		return( apollo_error() );
17114887Schin #endif /* apollo */
1712*10898Sroland.mainz@nrubsig.org 	if(sig<=sh.sigmax && sh.sigmsg[sig])
17134887Schin 		return(sh.sigmsg[sig]);
17144887Schin #if defined(SIGRTMIN) && defined(SIGRTMAX)
17158462SApril.Chin@Sun.COM 	if(sig>=sh.sigruntime[SH_SIGRTMIN] && sig<=sh.sigruntime[SH_SIGRTMAX])
17164887Schin 	{
17174887Schin 		static char sigrt[20];
17188462SApril.Chin@Sun.COM 		if(sig>sh.sigruntime[SH_SIGRTMIN]+(sh.sigruntime[SH_SIGRTMAX]-sig<=sh.sigruntime[SH_SIGRTMIN])/2)
17198462SApril.Chin@Sun.COM 			sfsprintf(sigrt,sizeof(sigrt),"SIGRTMAX-%d",sh.sigruntime[SH_SIGRTMAX]-sig);
17208462SApril.Chin@Sun.COM 		else
17218462SApril.Chin@Sun.COM 			sfsprintf(sigrt,sizeof(sigrt),"SIGRTMIN+%d",sig-sh.sigruntime[SH_SIGRTMIN]);
17224887Schin 		return(sigrt);
17234887Schin 	}
17244887Schin #endif
17254887Schin 	sfsprintf(signo,sizeof(signo),sh_translate(e_signo),sig);
17264887Schin 	return(signo);
17274887Schin }
17284887Schin 
17294887Schin /*
17304887Schin  * see whether exit status has been saved and delete it
17314887Schin  * if pid==0, then oldest saved process is deleted
17324887Schin  * If pid is not found a -1 is returned.
17334887Schin  */
17344887Schin static int job_chksave(register pid_t pid)
17354887Schin {
17364887Schin 	register struct jobsave *jp = bck.list, *jpold=0;
17374887Schin 	register int r= -1;
1738*10898Sroland.mainz@nrubsig.org 	register int count=bck.count;
1739*10898Sroland.mainz@nrubsig.org 	while(jp && count-->0)
17404887Schin 	{
17414887Schin 		if(jp->pid==pid)
17424887Schin 			break;
17434887Schin 		if(pid==0 && !jp->next)
17444887Schin 			break;
17454887Schin 		jpold = jp;
17464887Schin 		jp = jp->next;
17474887Schin 	}
17484887Schin 	if(jp)
17494887Schin 	{
17504887Schin 		r = 0;
17514887Schin 		if(pid)
17524887Schin 			r = jp->exitval;
17534887Schin 		if(jpold)
17544887Schin 			jpold->next = jp->next;
17554887Schin 		else
17564887Schin 			bck.list = jp->next;
17574887Schin 		bck.count--;
17584887Schin 		if(njob_savelist < NJOB_SAVELIST)
17594887Schin 		{
17604887Schin 			njob_savelist++;
17614887Schin 			jp->next = job_savelist;
17624887Schin 			job_savelist = jp;
17634887Schin 		}
17644887Schin 		else
17654887Schin 			free((void*)jp);
17664887Schin 	}
17674887Schin 	return(r);
17684887Schin }
17694887Schin 
17704887Schin void *job_subsave(void)
17714887Schin {
17724887Schin 	struct back_save *bp = new_of(struct back_save,0);
17734887Schin 	job_lock();
17744887Schin 	*bp = bck;
17754887Schin 	bck.count = 0;
17764887Schin 	bck.list = 0;
17774887Schin 	job_unlock();
17784887Schin 	return((void*)bp);
17794887Schin }
17804887Schin 
17814887Schin void job_subrestore(void* ptr)
17824887Schin {
17838462SApril.Chin@Sun.COM 	register struct jobsave *jp;
17844887Schin 	register struct back_save *bp = (struct back_save*)ptr;
17854887Schin 	register struct process *pw, *px, *pwnext;
17868462SApril.Chin@Sun.COM 	struct jobsave *jpnext;
17874887Schin 	job_lock();
17888462SApril.Chin@Sun.COM 	for(jp=bck.list; jp; jp=jpnext)
17898462SApril.Chin@Sun.COM 	{
17908462SApril.Chin@Sun.COM 		jpnext = jp->next;
17918462SApril.Chin@Sun.COM 		if(jp->pid==sh.spid)
17928462SApril.Chin@Sun.COM 		{
17938462SApril.Chin@Sun.COM 			jp->next = bp->list;
17948462SApril.Chin@Sun.COM 			bp->list = jp;
1795*10898Sroland.mainz@nrubsig.org 			bp->count++;
17968462SApril.Chin@Sun.COM 		}
17978462SApril.Chin@Sun.COM 		else
17988462SApril.Chin@Sun.COM 			job_chksave(jp->pid);
17998462SApril.Chin@Sun.COM 	}
18004887Schin 	for(pw=job.pwlist; pw; pw=pwnext)
18014887Schin 	{
18024887Schin 		pwnext = pw->p_nxtjob;
1803*10898Sroland.mainz@nrubsig.org 		if(pw->p_env != sh.curenv || pw->p_pid==sh.pipepid)
18044887Schin 			continue;
18054887Schin 		for(px=pw; px; px=px->p_nxtproc)
18064887Schin 			px->p_flag |= P_DONE;
18074887Schin 		job_unpost(pw,0);
18084887Schin 	}
18098462SApril.Chin@Sun.COM 
18108462SApril.Chin@Sun.COM 	/*
18118462SApril.Chin@Sun.COM 	 * queue up old lists for disposal by job_reap()
18128462SApril.Chin@Sun.COM 	 */
18138462SApril.Chin@Sun.COM 
18148462SApril.Chin@Sun.COM 	bck = *bp;
18158462SApril.Chin@Sun.COM 	free((void*)bp);
18164887Schin 	job_unlock();
18174887Schin }
18184887Schin 
18194887Schin int sh_waitsafe(void)
18204887Schin {
18214887Schin 	return(job.waitsafe);
18224887Schin }
18234887Schin 
18244887Schin void job_fork(pid_t parent)
18254887Schin {
18264887Schin #ifdef DEBUG
18274887Schin 	sfprintf(sfstderr,"ksh: job line %4d: fork pid=%d critical=%d parent=%d\n",__LINE__,getpid(),job.in_critical,parent);
18284887Schin #endif /* DEBUG */
18294887Schin 	switch (parent)
18304887Schin 	{
18314887Schin 	case -1:
18324887Schin 		job_lock();
18334887Schin 		break;
18344887Schin 	case 0:
18354887Schin 		job_unlock();
18364887Schin 		job.waitsafe = 0;
18374887Schin 		job.in_critical = 0;
18384887Schin 		break;
18394887Schin 	default:
18404887Schin 		job_unlock();
18414887Schin 		break;
18424887Schin 	}
18434887Schin }
1844