14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*8462SApril.Chin@Sun.COM *          Copyright (c) 1982-2008 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
7*8462SApril.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  *   Create and manage subshells avoiding forks when possible
234887Schin  *
244887Schin  *   David Korn
254887Schin  *   AT&T Labs
264887Schin  *
274887Schin  */
284887Schin 
294887Schin #include	"defs.h"
304887Schin #include	<ls.h>
314887Schin #include	"io.h"
324887Schin #include	"fault.h"
334887Schin #include	"shnodes.h"
344887Schin #include	"shlex.h"
354887Schin #include	"jobs.h"
364887Schin #include	"variables.h"
374887Schin #include	"path.h"
384887Schin 
394887Schin #ifndef PIPE_BUF
404887Schin #   define PIPE_BUF	512
414887Schin #endif
424887Schin 
434887Schin /*
444887Schin  * Note that the following structure must be the same
454887Schin  * size as the Dtlink_t structure
464887Schin  */
474887Schin struct Link
484887Schin {
494887Schin 	struct Link	*next;
50*8462SApril.Chin@Sun.COM 	Namval_t	*child;
51*8462SApril.Chin@Sun.COM 	Dt_t		*dict;
524887Schin 	Namval_t	*node;
534887Schin };
544887Schin 
554887Schin /*
564887Schin  * The following structure is used for command substitution and (...)
574887Schin  */
584887Schin static struct subshell
594887Schin {
60*8462SApril.Chin@Sun.COM 	Shell_t		*shp;	/* shell interpreter */
614887Schin 	struct subshell	*prev;	/* previous subshell data */
624887Schin 	struct subshell	*pipe;	/* subshell where output goes to pipe on fork */
634887Schin 	Dt_t		*var;	/* variable table at time of subshell */
644887Schin 	struct Link	*svar;	/* save shell variable table */
654887Schin 	Dt_t		*sfun;	/* function scope for subshell */
664887Schin 	Dt_t		*salias;/* alias scope for subshell */
674887Schin 	Pathcomp_t	*pathlist; /* for PATH variable */
684887Schin #if (ERROR_VERSION >= 20030214L)
694887Schin 	struct Error_context_s *errcontext;
704887Schin #else
714887Schin 	struct errorcontext *errcontext;
724887Schin #endif
734887Schin 	Shopt_t		options;/* save shell options */
744887Schin 	pid_t		subpid;	/* child process id */
754887Schin 	Sfio_t*	saveout;/*saved standard output */
764887Schin 	char		*pwd;	/* present working directory */
774887Schin 	const char	*shpwd;	/* saved pointer to sh.pwd */
784887Schin 	void		*jobs;	/* save job info */
794887Schin 	mode_t		mask;	/* saved umask */
804887Schin 	short		tmpfd;	/* saved tmp file descriptor */
814887Schin 	short		pipefd;	/* read fd if pipe is created */
824887Schin 	char		jobcontrol;
834887Schin 	char		monitor;
844887Schin 	unsigned char	fdstatus;
854887Schin 	int		fdsaved; /* bit make for saved files */
86*8462SApril.Chin@Sun.COM 	int		sig;	/* signal for $$ */
87*8462SApril.Chin@Sun.COM 	pid_t		bckpid;
88*8462SApril.Chin@Sun.COM 	pid_t		cpid;
89*8462SApril.Chin@Sun.COM 	int		coutpipe;
90*8462SApril.Chin@Sun.COM 	int		cpipe;
91*8462SApril.Chin@Sun.COM 	int		nofork;
924887Schin } *subshell_data;
934887Schin 
944887Schin static int subenv;
954887Schin 
964887Schin /*
974887Schin  * This routine will turn the sftmp() file into a real /tmp file or pipe
984887Schin  */
99*8462SApril.Chin@Sun.COM void	sh_subtmpfile(int pflag)
1004887Schin {
101*8462SApril.Chin@Sun.COM 	Shell_t *shp = &sh;
102*8462SApril.Chin@Sun.COM 	int fds[2];
103*8462SApril.Chin@Sun.COM 	Sfoff_t off;
1044887Schin 	if(sfset(sfstdout,0,0)&SF_STRING)
1054887Schin 	{
1064887Schin 		register int fd;
107*8462SApril.Chin@Sun.COM 		register struct checkpt	*pp = (struct checkpt*)shp->jmplist;
1084887Schin 		register struct subshell *sp = subshell_data->pipe;
1094887Schin 		/* save file descriptor 1 if open */
1104887Schin 		if((sp->tmpfd = fd = fcntl(1,F_DUPFD,10)) >= 0)
1114887Schin 		{
1124887Schin 			fcntl(fd,F_SETFD,FD_CLOEXEC);
113*8462SApril.Chin@Sun.COM 			shp->fdstatus[fd] = shp->fdstatus[1]|IOCLEX;
1144887Schin 			close(1);
1154887Schin 		}
1164887Schin 		else if(errno!=EBADF)
1174887Schin 			errormsg(SH_DICT,ERROR_system(1),e_toomany);
118*8462SApril.Chin@Sun.COM 		if(!pflag)
1194887Schin 		{
120*8462SApril.Chin@Sun.COM 			sfdisc(sfstdout,SF_POPDISC);
121*8462SApril.Chin@Sun.COM 			if((fd=sffileno(sfstdout))>=0)
1224887Schin 			{
123*8462SApril.Chin@Sun.COM 				sh.fdstatus[fd] = IOREAD|IOWRITE;
124*8462SApril.Chin@Sun.COM 				sfsync(sfstdout);
125*8462SApril.Chin@Sun.COM 				if(fd==1)
126*8462SApril.Chin@Sun.COM 					fcntl(1,F_SETFD,0);
127*8462SApril.Chin@Sun.COM 				else
128*8462SApril.Chin@Sun.COM 				{
129*8462SApril.Chin@Sun.COM 					sfsetfd(sfstdout,1);
130*8462SApril.Chin@Sun.COM 					sh.fdstatus[1] = sh.fdstatus[fd];
131*8462SApril.Chin@Sun.COM 					sh.fdstatus[fd] = IOCLOSE;
132*8462SApril.Chin@Sun.COM 				}
133*8462SApril.Chin@Sun.COM 				goto skip;
1344887Schin 			}
1354887Schin 		}
136*8462SApril.Chin@Sun.COM 		sh_pipe(fds);
137*8462SApril.Chin@Sun.COM 		sp->pipefd = fds[0];
138*8462SApril.Chin@Sun.COM 		sh_fcntl(sp->pipefd,F_SETFD,FD_CLOEXEC);
139*8462SApril.Chin@Sun.COM 		/* write the data to the pipe */
140*8462SApril.Chin@Sun.COM 		if(off = sftell(sfstdout))
141*8462SApril.Chin@Sun.COM 		{
142*8462SApril.Chin@Sun.COM 			write(fds[1],sfsetbuf(sfstdout,(Void_t*)sfstdout,0),(size_t)off);
143*8462SApril.Chin@Sun.COM 			sfpurge(sfstdout);
144*8462SApril.Chin@Sun.COM 		}
145*8462SApril.Chin@Sun.COM 		sfclose(sfstdout);
146*8462SApril.Chin@Sun.COM 		if((sh_fcntl(fds[1],F_DUPFD, 1)) != 1)
147*8462SApril.Chin@Sun.COM 			errormsg(SH_DICT,ERROR_system(1),e_file+4);
148*8462SApril.Chin@Sun.COM 		sh_close(fds[1]);
149*8462SApril.Chin@Sun.COM 	skip:
150*8462SApril.Chin@Sun.COM 		sh_iostream(shp,1);
1514887Schin 		sfset(sfstdout,SF_SHARE|SF_PUBLIC,1);
152*8462SApril.Chin@Sun.COM 		sfpool(sfstdout,shp->outpool,SF_WRITE);
1534887Schin 		if(pp && pp->olist  && pp->olist->strm == sfstdout)
1544887Schin 			pp->olist->strm = 0;
1554887Schin 	}
1564887Schin }
1574887Schin 
1584887Schin /*
1594887Schin  * This routine creates a temp file if necessary and creates a subshell.
1604887Schin  * The parent routine longjmps back to sh_subshell()
1614887Schin  * The child continues possibly with its standard output replaced by temp file
1624887Schin  */
1634887Schin void sh_subfork(void)
1644887Schin {
1654887Schin 	register struct subshell *sp = subshell_data;
166*8462SApril.Chin@Sun.COM 	Shell_t	*shp = sp->shp;
167*8462SApril.Chin@Sun.COM 	int	curenv = shp->curenv;
1684887Schin 	pid_t pid;
1694887Schin 	/* see whether inside $(...) */
1704887Schin 	if(sp->pipe)
171*8462SApril.Chin@Sun.COM 		sh_subtmpfile(1);
172*8462SApril.Chin@Sun.COM 	shp->curenv = 0;
1734887Schin 	if(pid = sh_fork(0,NIL(int*)))
1744887Schin 	{
175*8462SApril.Chin@Sun.COM 		shp->curenv = curenv;
1764887Schin 		/* this is the parent part of the fork */
1774887Schin 		if(sp->subpid==0)
1784887Schin 			sp->subpid = pid;
179*8462SApril.Chin@Sun.COM 		siglongjmp(*shp->jmplist,SH_JMPSUB);
1804887Schin 	}
1814887Schin 	else
1824887Schin 	{
1834887Schin 		/* this is the child part of the fork */
1844887Schin 		/* setting subpid to 1 causes subshell to exit when reached */
1854887Schin 		sh_onstate(SH_FORKED);
1864887Schin 		sh_onstate(SH_NOLOG);
1874887Schin 		sh_offstate(SH_MONITOR);
1884887Schin 		subshell_data = 0;
189*8462SApril.Chin@Sun.COM 		shp->subshell = 0;
190*8462SApril.Chin@Sun.COM 		SH_SUBSHELLNOD->nvalue.s = 0;
1914887Schin 		sp->subpid=0;
1924887Schin 	}
1934887Schin }
1944887Schin 
195*8462SApril.Chin@Sun.COM int nv_subsaved(register Namval_t *np)
196*8462SApril.Chin@Sun.COM {
197*8462SApril.Chin@Sun.COM 	register struct subshell	*sp;
198*8462SApril.Chin@Sun.COM 	register struct Link		*lp;
199*8462SApril.Chin@Sun.COM 	for(sp = (struct subshell*)subshell_data; sp; sp=sp->prev)
200*8462SApril.Chin@Sun.COM 	{
201*8462SApril.Chin@Sun.COM 		for(lp=sp->svar; lp; lp = lp->next)
202*8462SApril.Chin@Sun.COM 		{
203*8462SApril.Chin@Sun.COM 			if(lp->node==np)
204*8462SApril.Chin@Sun.COM 				return(1);
205*8462SApril.Chin@Sun.COM 		}
206*8462SApril.Chin@Sun.COM 	}
207*8462SApril.Chin@Sun.COM 	return(0);
208*8462SApril.Chin@Sun.COM }
209*8462SApril.Chin@Sun.COM 
2104887Schin /*
2114887Schin  * This routine will make a copy of the given node in the
2124887Schin  * layer created by the most recent subshell_fork if the
2134887Schin  * node hasn't already been copied
2144887Schin  */
2154887Schin Namval_t *sh_assignok(register Namval_t *np,int add)
2164887Schin {
217*8462SApril.Chin@Sun.COM 	register Namval_t	*mp;
218*8462SApril.Chin@Sun.COM 	register struct Link	*lp;
2194887Schin 	register struct subshell *sp = (struct subshell*)subshell_data;
220*8462SApril.Chin@Sun.COM 	struct Ufunction	*rp;
221*8462SApril.Chin@Sun.COM 	Shell_t			*shp = sp->shp;
222*8462SApril.Chin@Sun.COM 	Dt_t			*dp;
223*8462SApril.Chin@Sun.COM 	Namval_t		*mpnext;
224*8462SApril.Chin@Sun.COM 	Namarr_t		*ap;
225*8462SApril.Chin@Sun.COM 	int			save;
2264887Schin 	/* don't bother with this */
2274887Schin 	if(!sp->shpwd || (nv_isnull(np) && !add))
2284887Schin 		return(np);
2294887Schin 	/* don't bother to save if in newer scope */
230*8462SApril.Chin@Sun.COM 	if(!(rp=shp->st.real_fun)  || !(dp=rp->sdict))
231*8462SApril.Chin@Sun.COM 		dp = sp->var;
232*8462SApril.Chin@Sun.COM 	if(np->nvenv && !nv_isattr(np,NV_MINIMAL|NV_EXPORT) && shp->last_root)
233*8462SApril.Chin@Sun.COM 		dp = shp->last_root;
234*8462SApril.Chin@Sun.COM 	if((mp=nv_search((char*)np,dp,HASH_BUCKET))!=np)
235*8462SApril.Chin@Sun.COM 	{
236*8462SApril.Chin@Sun.COM 		if(mp || !np->nvfun || np->nvfun->subshell>=sh.subshell)
237*8462SApril.Chin@Sun.COM 			return(np);
238*8462SApril.Chin@Sun.COM 	}
239*8462SApril.Chin@Sun.COM 	if((ap=nv_arrayptr(np)) && (mp=nv_opensub(np)))
240*8462SApril.Chin@Sun.COM 	{
241*8462SApril.Chin@Sun.COM 		shp->last_root = ap->table;
242*8462SApril.Chin@Sun.COM 		sh_assignok(mp,add);
243*8462SApril.Chin@Sun.COM 		if(!add || array_assoc(ap))
244*8462SApril.Chin@Sun.COM 			return(np);
245*8462SApril.Chin@Sun.COM 	}
2464887Schin 	for(lp=subshell_data->svar; lp; lp = lp->next)
2474887Schin 	{
2484887Schin 		if(lp->node==np)
2494887Schin 			return(np);
2504887Schin 	}
251*8462SApril.Chin@Sun.COM 	/* first two pointers use linkage from np */
252*8462SApril.Chin@Sun.COM 	lp = (struct Link*)malloc(sizeof(*np)+2*sizeof(void*));
253*8462SApril.Chin@Sun.COM 	memset(lp,0, sizeof(*mp)+2*sizeof(void*));
2544887Schin 	lp->node = np;
255*8462SApril.Chin@Sun.COM 	if(!add &&  nv_isvtree(np))
256*8462SApril.Chin@Sun.COM 	{
257*8462SApril.Chin@Sun.COM 		Namval_t	fake;
258*8462SApril.Chin@Sun.COM 		Dt_t		*walk, *root=shp->var_tree;
259*8462SApril.Chin@Sun.COM 		char		*name = nv_name(np);
260*8462SApril.Chin@Sun.COM 		int		len = strlen(name);
261*8462SApril.Chin@Sun.COM 		fake.nvname = name;
262*8462SApril.Chin@Sun.COM 		mpnext = dtnext(root,&fake);
263*8462SApril.Chin@Sun.COM 		dp = root->walk?root->walk:root;
264*8462SApril.Chin@Sun.COM 		while(mp=mpnext)
265*8462SApril.Chin@Sun.COM 		{
266*8462SApril.Chin@Sun.COM 			walk = root->walk?root->walk:root;
267*8462SApril.Chin@Sun.COM 			mpnext = dtnext(root,mp);
268*8462SApril.Chin@Sun.COM 			if(memcmp(name,mp->nvname,len) || mp->nvname[len]!='.')
269*8462SApril.Chin@Sun.COM 				break;
270*8462SApril.Chin@Sun.COM 			nv_delete(mp,walk,NV_NOFREE);
271*8462SApril.Chin@Sun.COM 			*((Namval_t**)mp) = lp->child;
272*8462SApril.Chin@Sun.COM 			lp->child = mp;
273*8462SApril.Chin@Sun.COM 
274*8462SApril.Chin@Sun.COM 		}
275*8462SApril.Chin@Sun.COM 	}
276*8462SApril.Chin@Sun.COM 	lp->dict = dp;
277*8462SApril.Chin@Sun.COM 	mp = (Namval_t*)&lp->dict;
2784887Schin 	lp->next = subshell_data->svar;
2794887Schin 	subshell_data->svar = lp;
280*8462SApril.Chin@Sun.COM 	save = shp->subshell;
281*8462SApril.Chin@Sun.COM 	shp->subshell = 0;
282*8462SApril.Chin@Sun.COM 	mp->nvname = np->nvname;
283*8462SApril.Chin@Sun.COM 	nv_clone(np,mp,(add?(nv_isnull(np)?0:NV_NOFREE)|NV_ARRAY:NV_MOVE));
284*8462SApril.Chin@Sun.COM 	shp->subshell = save;
2854887Schin 	return(np);
2864887Schin }
2874887Schin 
2884887Schin /*
2894887Schin  * restore the variables
2904887Schin  */
2914887Schin static void nv_restore(struct subshell *sp)
2924887Schin {
2934887Schin 	register struct Link *lp, *lq;
2944887Schin 	register Namval_t *mp, *np;
2954887Schin 	const char *save = sp->shpwd;
296*8462SApril.Chin@Sun.COM 	Namval_t	*mpnext;
2974887Schin 	sp->shpwd = 0;	/* make sure sh_assignok doesn't save with nv_unset() */
2984887Schin 	for(lp=sp->svar; lp; lp=lq)
2994887Schin 	{
300*8462SApril.Chin@Sun.COM 		np = (Namval_t*)&lp->dict;
301*8462SApril.Chin@Sun.COM 		lq = lp->next;
3024887Schin 		mp = lp->node;
303*8462SApril.Chin@Sun.COM 		if(!mp->nvname)
304*8462SApril.Chin@Sun.COM 			continue;
3054887Schin 		if(nv_isarray(mp))
3064887Schin 			 nv_putsub(mp,NIL(char*),ARRAY_SCAN);
3074887Schin 		_nv_unset(mp,NV_RDONLY);
308*8462SApril.Chin@Sun.COM 		if(nv_isarray(np))
309*8462SApril.Chin@Sun.COM 		{
310*8462SApril.Chin@Sun.COM 			nv_clone(np,mp,NV_MOVE);
311*8462SApril.Chin@Sun.COM 			goto skip;
312*8462SApril.Chin@Sun.COM 		}
3134887Schin 		nv_setsize(mp,nv_size(np));
3144887Schin 		if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(np,NV_EXPORT))
3154887Schin 			mp->nvenv = np->nvenv;
3164887Schin 		mp->nvfun = np->nvfun;
3174887Schin 		mp->nvflag = np->nvflag;
318*8462SApril.Chin@Sun.COM 		if(nv_cover(mp))
3194887Schin 			nv_putval(mp, np->nvalue.cp,0);
3204887Schin 		else
3214887Schin 			mp->nvalue.cp = np->nvalue.cp;
3224887Schin 		np->nvfun = 0;
3234887Schin 		if(nv_isattr(mp,NV_EXPORT))
3244887Schin 		{
3254887Schin 			char *name = nv_name(mp);
3264887Schin 			sh_envput(sh.env,mp);
3274887Schin 			if(*name=='_' && strcmp(name,"_AST_FEATURES")==0)
3284887Schin 				astconf(NiL, NiL, NiL);
3294887Schin 		}
3304887Schin 		else if(nv_isattr(np,NV_EXPORT))
3314887Schin 			env_delete(sh.env,nv_name(mp));
332*8462SApril.Chin@Sun.COM 	skip:
333*8462SApril.Chin@Sun.COM 		for(mp=lp->child; mp; mp=mpnext)
334*8462SApril.Chin@Sun.COM 		{
335*8462SApril.Chin@Sun.COM 			mpnext = *((Namval_t**)mp);
336*8462SApril.Chin@Sun.COM 			dtinsert(lp->dict,mp);
337*8462SApril.Chin@Sun.COM 		}
338*8462SApril.Chin@Sun.COM 		free((void*)lp);
339*8462SApril.Chin@Sun.COM 		sp->svar = lq;
3404887Schin 	}
3414887Schin 	sp->shpwd=save;
3424887Schin }
3434887Schin 
3444887Schin /*
3454887Schin  * return pointer to alias tree
3464887Schin  * create new one if in a subshell and one doesn't exist and create is non-zero
3474887Schin  */
3484887Schin Dt_t *sh_subaliastree(int create)
3494887Schin {
3504887Schin 	register struct subshell *sp = subshell_data;
3514887Schin 	if(!sp || sh.curenv==0)
3524887Schin 		return(sh.alias_tree);
3534887Schin 	if(!sp->salias && create)
3544887Schin 	{
3554887Schin 		sp->salias = dtopen(&_Nvdisc,Dtoset);
3564887Schin 		dtview(sp->salias,sh.alias_tree);
3574887Schin 		sh.alias_tree = sp->salias;
3584887Schin 	}
3594887Schin 	return(sp->salias);
3604887Schin }
3614887Schin 
3624887Schin /*
3634887Schin  * return pointer to function tree
3644887Schin  * create new one if in a subshell and one doesn't exist and create is non-zero
3654887Schin  */
3664887Schin Dt_t *sh_subfuntree(int create)
3674887Schin {
3684887Schin 	register struct subshell *sp = subshell_data;
3694887Schin 	if(!sp || sh.curenv==0)
3704887Schin 		return(sh.fun_tree);
3714887Schin 	if(!sp->sfun && create)
3724887Schin 	{
3734887Schin 		sp->sfun = dtopen(&_Nvdisc,Dtoset);
3744887Schin 		dtview(sp->sfun,sh.fun_tree);
3754887Schin 		sh.fun_tree = sp->sfun;
3764887Schin 	}
377*8462SApril.Chin@Sun.COM 	return(sh.fun_tree);
3784887Schin }
3794887Schin 
380*8462SApril.Chin@Sun.COM static void table_unset(register Dt_t *root,int fun)
3814887Schin {
3824887Schin 	register Namval_t *np,*nq;
383*8462SApril.Chin@Sun.COM 	int flag;
3844887Schin 	for(np=(Namval_t*)dtfirst(root);np;np=nq)
3854887Schin 	{
3864887Schin 		nq = (Namval_t*)dtnext(root,np);
387*8462SApril.Chin@Sun.COM 		flag=0;
388*8462SApril.Chin@Sun.COM 		if(fun && np->nvalue.rp->fname && *np->nvalue.rp->fname=='/')
389*8462SApril.Chin@Sun.COM 		{
390*8462SApril.Chin@Sun.COM 			np->nvalue.rp->fdict = 0;
391*8462SApril.Chin@Sun.COM 			flag = NV_NOFREE;
392*8462SApril.Chin@Sun.COM 		}
393*8462SApril.Chin@Sun.COM 		else
394*8462SApril.Chin@Sun.COM 			_nv_unset(np,NV_RDONLY);
395*8462SApril.Chin@Sun.COM 		nv_delete(np,root,flag|NV_FUNCTION);
3964887Schin 	}
3974887Schin }
3984887Schin 
3994887Schin int sh_subsavefd(register int fd)
4004887Schin {
4014887Schin 	register struct subshell *sp = subshell_data;
4024887Schin 	register int old=0;
4034887Schin 	if(sp)
4044887Schin 	{
4054887Schin 		old = !(sp->fdsaved&(1<<(fd-1)));
4064887Schin 		sp->fdsaved |= (1<<(fd-1));
4074887Schin 	}
4084887Schin 	return(old);
4094887Schin }
4104887Schin 
411*8462SApril.Chin@Sun.COM void sh_subjobcheck(pid_t pid)
412*8462SApril.Chin@Sun.COM {
413*8462SApril.Chin@Sun.COM 	register struct subshell *sp = subshell_data;
414*8462SApril.Chin@Sun.COM 	while(sp)
415*8462SApril.Chin@Sun.COM 	{
416*8462SApril.Chin@Sun.COM 		if(sp->cpid==pid)
417*8462SApril.Chin@Sun.COM 		{
418*8462SApril.Chin@Sun.COM 			sh_close(sp->coutpipe);
419*8462SApril.Chin@Sun.COM 			sh_close(sp->cpipe);
420*8462SApril.Chin@Sun.COM 			sp->coutpipe = sp->cpipe = -1;
421*8462SApril.Chin@Sun.COM 			return;
422*8462SApril.Chin@Sun.COM 		}
423*8462SApril.Chin@Sun.COM 		sp = sp->prev;
424*8462SApril.Chin@Sun.COM 	}
425*8462SApril.Chin@Sun.COM }
426*8462SApril.Chin@Sun.COM 
4274887Schin /*
4284887Schin  * Run command tree <t> in a virtual sub-shell
4294887Schin  * If comsub is not null, then output will be placed in temp file (or buffer)
4304887Schin  * If comsub is not null, the return value will be a stream consisting of
4314887Schin  * output of command <t>.  Otherwise, NULL will be returned.
4324887Schin  */
4334887Schin 
4344887Schin Sfio_t *sh_subshell(Shnode_t *t, int flags, int comsub)
4354887Schin {
4364887Schin 	Shell_t *shp = &sh;
4374887Schin 	struct subshell sub_data;
4384887Schin 	register struct subshell *sp = &sub_data;
439*8462SApril.Chin@Sun.COM 	int jmpval,nsig=0;
4404887Schin 	int savecurenv = shp->curenv;
441*8462SApril.Chin@Sun.COM 	int savejobpgid = job.curpgid;
4424887Schin 	int16_t subshell;
4434887Schin 	char *savsig;
4444887Schin 	Sfio_t *iop=0;
4454887Schin 	struct checkpt buff;
4464887Schin 	struct sh_scoped savst;
4474887Schin 	struct dolnod   *argsav=0;
4484887Schin 	memset((char*)sp, 0, sizeof(*sp));
4494887Schin 	sfsync(shp->outpool);
450*8462SApril.Chin@Sun.COM 	argsav = sh_arguse(shp);
4514887Schin 	if(shp->curenv==0)
4524887Schin 	{
4534887Schin 		subshell_data=0;
4544887Schin 		subenv = 0;
4554887Schin 	}
4564887Schin 	shp->curenv = ++subenv;
457*8462SApril.Chin@Sun.COM 	job.curpgid = 0;
4584887Schin 	savst = shp->st;
4594887Schin 	sh_pushcontext(&buff,SH_JMPSUB);
4604887Schin 	subshell = shp->subshell+1;
461*8462SApril.Chin@Sun.COM 	SH_SUBSHELLNOD->nvalue.s = subshell;
4624887Schin 	shp->subshell = subshell;
4634887Schin 	sp->prev = subshell_data;
464*8462SApril.Chin@Sun.COM 	sp->shp = shp;
465*8462SApril.Chin@Sun.COM 	sp->sig = 0;
4664887Schin 	subshell_data = sp;
4674887Schin 	sp->errcontext = &buff.err;
4684887Schin 	sp->var = shp->var_tree;
4694887Schin 	sp->options = shp->options;
4704887Schin 	sp->jobs = job_subsave();
4714887Schin 	/* make sure initialization has occurred */
4724887Schin 	if(!shp->pathlist)
4734887Schin 		path_get(".");
4744887Schin 	sp->pathlist = path_dup((Pathcomp_t*)shp->pathlist);
4754887Schin 	if(!shp->pwd)
4764887Schin 		path_pwd(0);
4774887Schin 	sp->bckpid = shp->bckpid;
478*8462SApril.Chin@Sun.COM 	if(comsub)
479*8462SApril.Chin@Sun.COM 		sh_stats(STAT_COMSUB);
480*8462SApril.Chin@Sun.COM 	if(!comsub || (comsub==1 && !sh_isoption(SH_SUBSHARE)))
4814887Schin 	{
4824887Schin 		sp->shpwd = shp->pwd;
4834887Schin 		sp->pwd = (shp->pwd?strdup(shp->pwd):0);
4844887Schin 		sp->mask = shp->mask;
485*8462SApril.Chin@Sun.COM 		sh_stats(STAT_SUBSHELL);
4864887Schin 		/* save trap table */
4874887Schin 		shp->st.otrapcom = 0;
4884887Schin 		if((nsig=shp->st.trapmax*sizeof(char*))>0 || shp->st.trapcom[0])
4894887Schin 		{
4904887Schin 			nsig += sizeof(char*);
4914887Schin 			memcpy(savsig=malloc(nsig),(char*)&shp->st.trapcom[0],nsig);
4924887Schin 			/* this nonsense needed for $(trap) */
4934887Schin 			shp->st.otrapcom = (char**)savsig;
4944887Schin 		}
495*8462SApril.Chin@Sun.COM 		sp->cpid = shp->cpid;
496*8462SApril.Chin@Sun.COM 		sp->coutpipe = shp->coutpipe;
497*8462SApril.Chin@Sun.COM 		sp->cpipe = shp->cpipe[1];
498*8462SApril.Chin@Sun.COM 		shp->coutpipe = shp->cpipe[1] = -1;
499*8462SApril.Chin@Sun.COM 		shp->cpid = 0;
5004887Schin 		sh_sigreset(0);
5014887Schin 	}
5024887Schin 	jmpval = sigsetjmp(buff.buff,0);
5034887Schin 	if(jmpval==0)
5044887Schin 	{
5054887Schin 		if(comsub)
5064887Schin 		{
5074887Schin 			/* disable job control */
508*8462SApril.Chin@Sun.COM 			shp->spid = 0;
5094887Schin 			sp->jobcontrol = job.jobcontrol;
5104887Schin 			sp->monitor = (sh_isstate(SH_MONITOR)!=0);
5114887Schin 			job.jobcontrol=0;
5124887Schin 			sh_offstate(SH_MONITOR);
5134887Schin 			sp->pipe = sp;
5144887Schin 			/* save sfstdout and status */
5154887Schin 			sp->saveout = sfswap(sfstdout,NIL(Sfio_t*));
5164887Schin 			sp->fdstatus = shp->fdstatus[1];
5174887Schin 			sp->tmpfd = -1;
5184887Schin 			sp->pipefd = -1;
5194887Schin 			/* use sftmp() file for standard output */
5204887Schin 			if(!(iop = sftmp(PIPE_BUF)))
5214887Schin 			{
5224887Schin 				sfswap(sp->saveout,sfstdout);
5234887Schin 				errormsg(SH_DICT,ERROR_system(1),e_tmpcreate);
5244887Schin 			}
5254887Schin 			sfswap(iop,sfstdout);
5264887Schin 			sfset(sfstdout,SF_READ,0);
5274887Schin 			shp->fdstatus[1] = IOWRITE;
528*8462SApril.Chin@Sun.COM 			if(!(sp->nofork = sh_state(SH_NOFORK)))
529*8462SApril.Chin@Sun.COM 				sh_onstate(SH_NOFORK);
530*8462SApril.Chin@Sun.COM 			flags |= sh_state(SH_NOFORK);
5314887Schin 		}
5324887Schin 		else if(sp->prev)
5334887Schin 		{
5344887Schin 			sp->pipe = sp->prev->pipe;
5354887Schin 			flags &= ~sh_state(SH_NOFORK);
5364887Schin 		}
5374887Schin 		sh_exec(t,flags);
5384887Schin 	}
539*8462SApril.Chin@Sun.COM 	if(comsub!=2 && jmpval!=SH_JMPSUB && shp->st.trapcom[0] && shp->subshell)
5404887Schin 	{
5414887Schin 		/* trap on EXIT not handled by child */
5424887Schin 		char *trap=shp->st.trapcom[0];
5434887Schin 		shp->st.trapcom[0] = 0;	/* prevent recursion */
5444887Schin 		shp->oldexit = shp->exitval;
5454887Schin 		sh_trap(trap,0);
5464887Schin 		free(trap);
5474887Schin 	}
5484887Schin 	sh_popcontext(&buff);
5494887Schin 	if(shp->subshell==0)	/* must be child process */
5504887Schin 	{
5514887Schin 		subshell_data = sp->prev;
5524887Schin 		if(jmpval==SH_JMPSCRIPT)
5534887Schin 			siglongjmp(*shp->jmplist,jmpval);
554*8462SApril.Chin@Sun.COM 		sh_done(shp,0);
5554887Schin 	}
5564887Schin 	if(comsub)
5574887Schin 	{
5584887Schin 		/* re-enable job control */
559*8462SApril.Chin@Sun.COM 		if(!sp->nofork)
560*8462SApril.Chin@Sun.COM 			sh_offstate(SH_NOFORK);
5614887Schin 		job.jobcontrol = sp->jobcontrol;
5624887Schin 		if(sp->monitor)
5634887Schin 			sh_onstate(SH_MONITOR);
5644887Schin 		if(sp->pipefd>=0)
5654887Schin 		{
5664887Schin 			/* sftmp() file has been returned into pipe */
567*8462SApril.Chin@Sun.COM 			iop = sh_iostream(shp,sp->pipefd);
5684887Schin 			sfclose(sfstdout);
5694887Schin 		}
5704887Schin 		else
5714887Schin 		{
5724887Schin 			/* move tmp file to iop and restore sfstdout */
5734887Schin 			iop = sfswap(sfstdout,NIL(Sfio_t*));
5744887Schin 			if(!iop)
5754887Schin 			{
5764887Schin 				/* maybe locked try again */
5774887Schin 				sfclrlock(sfstdout);
5784887Schin 				iop = sfswap(sfstdout,NIL(Sfio_t*));
5794887Schin 			}
5804887Schin 			if(iop && sffileno(iop)==1)
5814887Schin 			{
5824887Schin 				int fd=sfsetfd(iop,3);
5834887Schin 				if(fd<0)
5844887Schin 					errormsg(SH_DICT,ERROR_system(1),e_toomany);
5854887Schin 				shp->sftable[fd] = iop;
5864887Schin 				fcntl(fd,F_SETFD,FD_CLOEXEC);
5874887Schin 				shp->fdstatus[fd] = (shp->fdstatus[1]|IOCLEX);
5884887Schin 				shp->fdstatus[1] = IOCLOSE;
5894887Schin 			}
5904887Schin 			sfset(iop,SF_READ,1);
5914887Schin 		}
5924887Schin 		sfswap(sp->saveout,sfstdout);
5934887Schin 		/*  check if standard output was preserved */
5944887Schin 		if(sp->tmpfd>=0)
5954887Schin 		{
5964887Schin 			close(1);
5974887Schin 			fcntl(sp->tmpfd,F_DUPFD,1);
5984887Schin 			sh_close(sp->tmpfd);
5994887Schin 		}
6004887Schin 		shp->fdstatus[1] = sp->fdstatus;
6014887Schin 	}
6024887Schin 	if(sp->subpid)
603*8462SApril.Chin@Sun.COM 	{
604*8462SApril.Chin@Sun.COM 		if(shp->exitval > SH_EXITSIG)
605*8462SApril.Chin@Sun.COM 			sp->sig = (shp->exitval&SH_EXITMASK);
606*8462SApril.Chin@Sun.COM 		shp->exitval = 0;
607*8462SApril.Chin@Sun.COM 		if(comsub)
608*8462SApril.Chin@Sun.COM 			shp->spid = sp->subpid;
609*8462SApril.Chin@Sun.COM 		else
610*8462SApril.Chin@Sun.COM 			job_wait(sp->subpid);
611*8462SApril.Chin@Sun.COM 	}
612*8462SApril.Chin@Sun.COM 	if(comsub && iop && sp->pipefd<0)
6134887Schin 		sfseek(iop,(off_t)0,SEEK_SET);
6144887Schin 	path_delete((Pathcomp_t*)shp->pathlist);
6154887Schin 	shp->pathlist = (void*)sp->pathlist;
6164887Schin 	job_subrestore(sp->jobs);
6174887Schin 	shp->jobenv = savecurenv;
618*8462SApril.Chin@Sun.COM 	job.curpgid = savejobpgid;
6194887Schin 	shp->bckpid = sp->bckpid;
6204887Schin 	if(sp->shpwd)	/* restore environment if saved */
6214887Schin 	{
622*8462SApril.Chin@Sun.COM 		int n;
6234887Schin 		shp->options = sp->options;
6244887Schin 		nv_restore(sp);
6254887Schin 		if(sp->salias)
6264887Schin 		{
6274887Schin 			shp->alias_tree = dtview(sp->salias,0);
628*8462SApril.Chin@Sun.COM 			table_unset(sp->salias,0);
6294887Schin 			dtclose(sp->salias);
6304887Schin 		}
6314887Schin 		if(sp->sfun)
6324887Schin 		{
6334887Schin 			shp->fun_tree = dtview(sp->sfun,0);
634*8462SApril.Chin@Sun.COM 			table_unset(sp->sfun,1);
6354887Schin 			dtclose(sp->sfun);
6364887Schin 		}
637*8462SApril.Chin@Sun.COM 		n = shp->st.trapmax-savst.trapmax;
6384887Schin 		sh_sigreset(1);
639*8462SApril.Chin@Sun.COM 		if(n>0)
640*8462SApril.Chin@Sun.COM 			memset(&shp->st.trapcom[savst.trapmax],0,n*sizeof(char*));
6414887Schin 		shp->st = savst;
6424887Schin 		shp->curenv = savecurenv;
6434887Schin 		if(nsig)
6444887Schin 		{
6454887Schin 			memcpy((char*)&shp->st.trapcom[0],savsig,nsig);
6464887Schin 			free((void*)savsig);
6474887Schin 		}
6484887Schin 		shp->options = sp->options;
6494887Schin 		if(!shp->pwd || strcmp(sp->pwd,shp->pwd))
6504887Schin 		{
6514887Schin 			/* restore PWDNOD */
652*8462SApril.Chin@Sun.COM 			Namval_t *pwdnod = sh_scoped(shp,PWDNOD);
6534887Schin 			if(shp->pwd)
6544887Schin 			{
6554887Schin 				chdir(shp->pwd=sp->pwd);
6564887Schin 				path_newdir(shp->pathlist);
6574887Schin 			}
6584887Schin 			if(nv_isattr(pwdnod,NV_NOFREE))
6594887Schin 				pwdnod->nvalue.cp = (const char*)sp->pwd;
6604887Schin 		}
6614887Schin 		else if(sp->shpwd != shp->pwd)
6624887Schin 		{
6634887Schin 			shp->pwd = sp->pwd;
6644887Schin 			if(PWDNOD->nvalue.cp==sp->shpwd)
6654887Schin 				PWDNOD->nvalue.cp = sp->pwd;
6664887Schin 		}
6674887Schin 		else
6684887Schin 			free((void*)sp->pwd);
6694887Schin 		if(sp->mask!=shp->mask)
670*8462SApril.Chin@Sun.COM 			umask(shp->mask=sp->mask);
671*8462SApril.Chin@Sun.COM 		if(shp->coutpipe>=0)
672*8462SApril.Chin@Sun.COM 		{
673*8462SApril.Chin@Sun.COM 			sh_close(shp->coutpipe);
674*8462SApril.Chin@Sun.COM 			sh_close(shp->cpipe[1]);
675*8462SApril.Chin@Sun.COM 		}
676*8462SApril.Chin@Sun.COM 		shp->cpid = sp->cpid;
677*8462SApril.Chin@Sun.COM 		shp->cpipe[1] = sp->cpipe;
678*8462SApril.Chin@Sun.COM 		shp->coutpipe = sp->coutpipe;
6794887Schin 	}
680*8462SApril.Chin@Sun.COM 	if(shp->subshell)
681*8462SApril.Chin@Sun.COM 		SH_SUBSHELLNOD->nvalue.s = --shp->subshell;
682*8462SApril.Chin@Sun.COM 	if(sp->sig)
683*8462SApril.Chin@Sun.COM 	{
684*8462SApril.Chin@Sun.COM 		if(sp->prev)
685*8462SApril.Chin@Sun.COM 			sp->prev->sig = sp->sig;
686*8462SApril.Chin@Sun.COM 		else
687*8462SApril.Chin@Sun.COM 		{
688*8462SApril.Chin@Sun.COM 			sh_fault(sp->sig);
689*8462SApril.Chin@Sun.COM 			sh_chktrap();
690*8462SApril.Chin@Sun.COM 		}
691*8462SApril.Chin@Sun.COM 	}
692*8462SApril.Chin@Sun.COM 	subshell = shp->subshell;
6934887Schin 	subshell_data = sp->prev;
694*8462SApril.Chin@Sun.COM 	sh_argfree(shp,argsav,0);
6954887Schin 	shp->trapnote = 0;
6964887Schin 	if(shp->topfd != buff.topfd)
697*8462SApril.Chin@Sun.COM 		sh_iorestore(shp,buff.topfd|IOSUBSHELL,jmpval);
6984887Schin 	if(shp->exitval > SH_EXITSIG)
6994887Schin 	{
7004887Schin 		int sig = shp->exitval&SH_EXITMASK;
7014887Schin 		if(sig==SIGINT || sig== SIGQUIT)
7024887Schin 			sh_fault(sig);
7034887Schin 	}
7044887Schin 	return(iop);
7054887Schin }
706