2da2e3ebchin*                                                                      *
3da2e3ebchin*               This software is part of the ast package               *
43e14f97Roger A. Faulkner*          Copyright (c) 1982-2010 AT&T Intellectual Property          *
5da2e3ebchin*                      and is licensed under the                       *
6da2e3ebchin*                  Common Public License, Version 1.0                  *
77c2fbfbApril Chin*                    by AT&T Intellectual Property                     *
8da2e3ebchin*                                                                      *
9da2e3ebchin*                A copy of the License is available at                 *
10da2e3ebchin*            http://www.opensource.org/licenses/cpl1.0.txt             *
11da2e3ebchin*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12da2e3ebchin*                                                                      *
13da2e3ebchin*              Information and Software Systems Research               *
14da2e3ebchin*                            AT&T Research                             *
15da2e3ebchin*                           Florham Park NJ                            *
16da2e3ebchin*                                                                      *
17da2e3ebchin*                  David Korn <dgk@research.att.com>                   *
18da2e3ebchin*                                                                      *
20da2e3ebchin#pragma prototyped
22da2e3ebchin *   Create and manage subshells avoiding forks when possible
23da2e3ebchin *
24da2e3ebchin *   David Korn
25da2e3ebchin *   AT&T Labs
26da2e3ebchin *
27da2e3ebchin */
29da2e3ebchin#include	"defs.h"
30da2e3ebchin#include	<ls.h>
31da2e3ebchin#include	"io.h"
32da2e3ebchin#include	"fault.h"
33da2e3ebchin#include	"shnodes.h"
34da2e3ebchin#include	"shlex.h"
35da2e3ebchin#include	"jobs.h"
36da2e3ebchin#include	"variables.h"
37da2e3ebchin#include	"path.h"
39da2e3ebchin#ifndef PIPE_BUF
40da2e3ebchin#   define PIPE_BUF	512
44da2e3ebchin * Note that the following structure must be the same
45da2e3ebchin * size as the Dtlink_t structure
46da2e3ebchin */
47da2e3ebchinstruct Link
49da2e3ebchin	struct Link	*next;
507c2fbfbApril Chin	Namval_t	*child;
517c2fbfbApril Chin	Dt_t		*dict;
52da2e3ebchin	Namval_t	*node;
56da2e3ebchin * The following structure is used for command substitution and (...)
57da2e3ebchin */
58da2e3ebchinstatic struct subshell
607c2fbfbApril Chin	Shell_t		*shp;	/* shell interpreter */
61da2e3ebchin	struct subshell	*prev;	/* previous subshell data */
62da2e3ebchin	struct subshell	*pipe;	/* subshell where output goes to pipe on fork */
63da2e3ebchin	Dt_t		*var;	/* variable table at time of subshell */
64da2e3ebchin	struct Link	*svar;	/* save shell variable table */
65da2e3ebchin	Dt_t		*sfun;	/* function scope for subshell */
66da2e3ebchin	Dt_t		*salias;/* alias scope for subshell */
67da2e3ebchin	Pathcomp_t	*pathlist; /* for PATH variable */
68da2e3ebchin#if (ERROR_VERSION >= 20030214L)
69da2e3ebchin	struct Error_context_s *errcontext;
71da2e3ebchin	struct errorcontext *errcontext;
73da2e3ebchin	Shopt_t		options;/* save shell options */
74da2e3ebchin	pid_t		subpid;	/* child process id */
75da2e3ebchin	Sfio_t*	saveout;/*saved standard output */
76da2e3ebchin	char		*pwd;	/* present working directory */
77da2e3ebchin	const char	*shpwd;	/* saved pointer to sh.pwd */
78da2e3ebchin	void		*jobs;	/* save job info */
79da2e3ebchin	mode_t		mask;	/* saved umask */
80da2e3ebchin	short		tmpfd;	/* saved tmp file descriptor */
81da2e3ebchin	short		pipefd;	/* read fd if pipe is created */
82da2e3ebchin	char		jobcontrol;
83da2e3ebchin	char		monitor;
84da2e3ebchin	unsigned char	fdstatus;
85da2e3ebchin	int		fdsaved; /* bit make for saved files */
867c2fbfbApril Chin	int		sig;	/* signal for $$ */
877c2fbfbApril Chin	pid_t		bckpid;
887c2fbfbApril Chin	pid_t		cpid;
897c2fbfbApril Chin	int		coutpipe;
907c2fbfbApril Chin	int		cpipe;
917c2fbfbApril Chin	int		nofork;
9281af778Casper H.S. Dik	char		subshare;
93da2e3ebchin} *subshell_data;
95da2e3ebchinstatic int subenv;
98da2e3ebchin * This routine will turn the sftmp() file into a real /tmp file or pipe
99da2e3ebchin */
1007c2fbfbApril Chinvoid	sh_subtmpfile(int pflag)
1027c2fbfbApril Chin	Shell_t *shp = &sh;
1037c2fbfbApril Chin	int fds[2];
1047c2fbfbApril Chin	Sfoff_t off;
10534f9b3eRoland Mainz	register struct checkpt	*pp = (struct checkpt*)shp->jmplist;
10634f9b3eRoland Mainz	register struct subshell *sp = subshell_data->pipe;
107da2e3ebchin	if(sfset(sfstdout,0,0)&SF_STRING)
108da2e3ebchin	{
109da2e3ebchin		register int fd;
110da2e3ebchin		/* save file descriptor 1 if open */
111da2e3ebchin		if((sp->tmpfd = fd = fcntl(1,F_DUPFD,10)) >= 0)
112da2e3ebchin		{
113da2e3ebchin			fcntl(fd,F_SETFD,FD_CLOEXEC);
1147c2fbfbApril Chin			shp->fdstatus[fd] = shp->fdstatus[1]|IOCLEX;
115da2e3ebchin			close(1);
11634f9b3eRoland Mainz			shp->fdstatus[1] = IOCLOSE;
117da2e3ebchin		}
118da2e3ebchin		else if(errno!=EBADF)
11934f9b3eRoland Mainz		{
12034f9b3eRoland Mainz			((struct checkpt*)shp->jmplist)->mode = SH_JMPERREXIT;
12134f9b3eRoland Mainz			shp->toomany = 1;
122da2e3ebchin			errormsg(SH_DICT,ERROR_system(1),e_toomany);
12334f9b3eRoland Mainz		}
12434f9b3eRoland Mainz		if(shp->subshare || !pflag)
125da2e3ebchin		{
1267c2fbfbApril Chin			sfdisc(sfstdout,SF_POPDISC);
1277c2fbfbApril Chin			if((fd=sffileno(sfstdout))>=0)
128da2e3ebchin			{
12934f9b3eRoland Mainz				shp->fdstatus[fd] = IOREAD|IOWRITE;
1307c2fbfbApril Chin				sfsync(sfstdout);
1317c2fbfbApril Chin				if(fd==1)
1327c2fbfbApril Chin					fcntl(1,F_SETFD,0);
1337c2fbfbApril Chin				else
1347c2fbfbApril Chin				{
1357c2fbfbApril Chin					sfsetfd(sfstdout,1);
13634f9b3eRoland Mainz					shp->fdstatus[1] = shp->fdstatus[fd];
13734f9b3eRoland Mainz					shp->fdstatus[fd] = IOCLOSE;
1387c2fbfbApril Chin				}
1397c2fbfbApril Chin				goto skip;
140da2e3ebchin			}
141da2e3ebchin		}
14234f9b3eRoland Mainz	}
14334f9b3eRoland Mainz	if(sp && (shp->fdstatus[1]==IOCLOSE || (!shp->subshare && !(shp->fdstatus[1]&IONOSEEK))))
14434f9b3eRoland Mainz	{
14534f9b3eRoland Mainz		struct stat statb,statx;
14634f9b3eRoland Mainz		int fd;
1477c2fbfbApril Chin		sh_pipe(fds);
1487c2fbfbApril Chin		sp->pipefd = fds[0];
1497c2fbfbApril Chin		sh_fcntl(sp->pipefd,F_SETFD,FD_CLOEXEC);
1507c2fbfbApril Chin		/* write the data to the pipe */
1517c2fbfbApril Chin		if(off = sftell(sfstdout))
1527c2fbfbApril Chin		{
1537c2fbfbApril Chin			write(fds[1],sfsetbuf(sfstdout,(Void_t*)sfstdout,0),(size_t)off);
1547c2fbfbApril Chin			sfpurge(sfstdout);
1557c2fbfbApril Chin		}
15634f9b3eRoland Mainz		if((sfset(sfstdout,0,0)&SF_STRING) || fstat(1,&statb)<0)
15734f9b3eRoland Mainz			statb.st_ino = 0;
1587c2fbfbApril Chin		sfclose(sfstdout);
1597c2fbfbApril Chin		if((sh_fcntl(fds[1],F_DUPFD, 1)) != 1)
16034f9b3eRoland Mainz			errormsg(SH_DICT,ERROR_system(1),e_redirect);
1617c2fbfbApril Chin		sh_close(fds[1]);
16234f9b3eRoland Mainz		if(statb.st_ino) for(fd=0; fd < 10; fd++)
16334f9b3eRoland Mainz		{
16434f9b3eRoland Mainz			if(fd==1 || ((shp->fdstatus[fd]&(IONOSEEK|IOSEEK|IOWRITE))!=(IOSEEK|IOWRITE)) || fstat(fd,&statx)<0)
16534f9b3eRoland Mainz				continue;
16634f9b3eRoland Mainz			if(statb.st_ino==statx.st_ino && statb.st_dev==statx.st_dev)
16734f9b3eRoland Mainz			{
16834f9b3eRoland Mainz				sh_close(fd);
16934f9b3eRoland Mainz				fcntl(1,F_DUPFD, fd);
17034f9b3eRoland Mainz			}
17134f9b3eRoland Mainz		}
1727c2fbfbApril Chin	skip:
1737c2fbfbApril Chin		sh_iostream(shp,1);
174da2e3ebchin		sfset(sfstdout,SF_SHARE|SF_PUBLIC,1);
1757c2fbfbApril Chin		sfpool(sfstdout,shp->outpool,SF_WRITE);
176da2e3ebchin		if(pp && pp->olist  && pp->olist->strm == sfstdout)
177da2e3ebchin			pp->olist->strm = 0;
178da2e3ebchin	}
18134f9b3eRoland Mainz
183da2e3ebchin * This routine creates a temp file if necessary and creates a subshell.
184da2e3ebchin * The parent routine longjmps back to sh_subshell()
185da2e3ebchin * The child continues possibly with its standard output replaced by temp file
186da2e3ebchin */
187da2e3ebchinvoid sh_subfork(void)
189da2e3ebchin	register struct subshell *sp = subshell_data;
1907c2fbfbApril Chin	Shell_t	*shp = sp->shp;
1917c2fbfbApril Chin	int	curenv = shp->curenv;
192da2e3ebchin	pid_t pid;
19334f9b3eRoland Mainz	char *trap = shp->st.trapcom[0];
19434f9b3eRoland Mainz	if(trap)
19534f9b3eRoland Mainz		trap = strdup(trap);
196da2e3ebchin	/* see whether inside $(...) */
197da2e3ebchin	if(sp->pipe)
1987c2fbfbApril Chin		sh_subtmpfile(1);
1997c2fbfbApril Chin	shp->curenv = 0;
20034f9b3eRoland Mainz	if(pid = sh_fork(FSHOWME,NIL(int*)))
201da2e3ebchin	{
2027c2fbfbApril Chin		shp->curenv = curenv;
203da2e3ebchin		/* this is the parent part of the fork */
204da2e3ebchin		if(sp->subpid==0)
205da2e3ebchin			sp->subpid = pid;
20634f9b3eRoland Mainz		if(trap)
20734f9b3eRoland Mainz			free((void*)trap);
2087c2fbfbApril Chin		siglongjmp(*shp->jmplist,SH_JMPSUB);
209da2e3ebchin	}
210da2e3ebchin	else
211da2e3ebchin	{
212da2e3ebchin		/* this is the child part of the fork */
213da2e3ebchin		/* setting subpid to 1 causes subshell to exit when reached */
214da2e3ebchin		sh_onstate(SH_FORKED);
215da2e3ebchin		sh_onstate(SH_NOLOG);
2163e14f97Roger A. Faulkner		sh_offoption(SH_MONITOR);
217da2e3ebchin		sh_offstate(SH_MONITOR);
218da2e3ebchin		subshell_data = 0;
2197c2fbfbApril Chin		shp->subshell = 0;
2207c2fbfbApril Chin		SH_SUBSHELLNOD->nvalue.s = 0;
221da2e3ebchin		sp->subpid=0;
22234f9b3eRoland Mainz		shp->st.trapcom[0] = trap;
223da2e3ebchin	}
2267c2fbfbApril Chinint nv_subsaved(register Namval_t *np)
2277c2fbfbApril Chin{
2287c2fbfbApril Chin	register struct subshell	*sp;
2297c2fbfbApril Chin	register struct Link		*lp;
2307c2fbfbApril Chin	for(sp = (struct subshell*)subshell_data; sp; sp=sp->prev)
2317c2fbfbApril Chin	{
2327c2fbfbApril Chin		for(lp=sp->svar; lp; lp = lp->next)
2337c2fbfbApril Chin		{
2347c2fbfbApril Chin			if(lp->node==np)
2357c2fbfbApril Chin				return(1);
2367c2fbfbApril Chin		}
2377c2fbfbApril Chin	}
2387c2fbfbApril Chin	return(0);
2397c2fbfbApril Chin}
2407c2fbfbApril Chin
242da2e3ebchin * This routine will make a copy of the given node in the
243da2e3ebchin * layer created by the most recent subshell_fork if the
244da2e3ebchin * node hasn't already been copied
245da2e3ebchin */
246da2e3ebchinNamval_t *sh_assignok(register Namval_t *np,int add)
2487c2fbfbApril Chin	register Namval_t	*mp;
2497c2fbfbApril Chin	register struct Link	*lp;
250da2e3ebchin	register struct subshell *sp = (struct subshell*)subshell_data;
2517c2fbfbApril Chin	struct Ufunction	*rp;
2527c2fbfbApril Chin	Shell_t			*shp = sp->shp;
2537c2fbfbApril Chin	Dt_t			*dp;
2547c2fbfbApril Chin	Namval_t		*mpnext;
2557c2fbfbApril Chin	Namarr_t		*ap;
2567c2fbfbApril Chin	int			save;
257da2e3ebchin	/* don't bother with this */
25834f9b3eRoland Mainz	if(!sp->shpwd || (nv_isnull(np) && !add) || np==SH_LEVELNOD)
259da2e3ebchin		return(np);
260da2e3ebchin	/* don't bother to save if in newer scope */
2617c2fbfbApril Chin	if(!(rp=shp->st.real_fun)  || !(dp=rp->sdict))
2627c2fbfbApril Chin		dp = sp->var;
2637c2fbfbApril Chin	if(np->nvenv && !nv_isattr(np,NV_MINIMAL|NV_EXPORT) && shp->last_root)
2647c2fbfbApril Chin		dp = shp->last_root;
2657c2fbfbApril Chin	if((mp=nv_search((char*)np,dp,HASH_BUCKET))!=np)
2667c2fbfbApril Chin	{
2677c2fbfbApril Chin		if(mp || !np->nvfun || np->nvfun->subshell>=sh.subshell)
2687c2fbfbApril Chin			return(np);
2697c2fbfbApril Chin	}
2707c2fbfbApril Chin	if((ap=nv_arrayptr(np)) && (mp=nv_opensub(np)))
2717c2fbfbApril Chin	{
2727c2fbfbApril Chin		shp->last_root = ap->table;
2737c2fbfbApril Chin		sh_assignok(mp,add);
2747c2fbfbApril Chin		if(!add || array_assoc(ap))
2757c2fbfbApril Chin			return(np);
2767c2fbfbApril Chin	}
277da2e3ebchin	for(lp=subshell_data->svar; lp; lp = lp->next)
278da2e3ebchin	{
279da2e3ebchin		if(lp->node==np)
280da2e3ebchin			return(np);
281da2e3ebchin	}
2827c2fbfbApril Chin	/* first two pointers use linkage from np */
2837c2fbfbApril Chin	lp = (struct Link*)malloc(sizeof(*np)+2*sizeof(void*));
2847c2fbfbApril Chin	memset(lp,0, sizeof(*mp)+2*sizeof(void*));
285da2e3ebchin	lp->node = np;
2867c2fbfbApril Chin	if(!add &&  nv_isvtree(np))
2877c2fbfbApril Chin	{
2887c2fbfbApril Chin		Namval_t	fake;
2897c2fbfbApril Chin		Dt_t		*walk, *root=shp->var_tree;
2907c2fbfbApril Chin		char		*name = nv_name(np);
2917c2fbfbApril Chin		int		len = strlen(name);
2927c2fbfbApril Chin		fake.nvname = name;
2937c2fbfbApril Chin		mpnext = dtnext(root,&fake);
2947c2fbfbApril Chin		dp = root->walk?root->walk:root;
2957c2fbfbApril Chin		while(mp=mpnext)
2967c2fbfbApril Chin		{
2977c2fbfbApril Chin			walk = root->walk?root->walk:root;
2987c2fbfbApril Chin			mpnext = dtnext(root,mp);
2997c2fbfbApril Chin			if(memcmp(name,mp->nvname,len) || mp->nvname[len]!='.')
3007c2fbfbApril Chin				break;
3017c2fbfbApril Chin			nv_delete(mp,walk,NV_NOFREE);
3027c2fbfbApril Chin			*((Namval_t**)mp) = lp->child;
3037c2fbfbApril Chin			lp->child = mp;
3047c2fbfbApril Chin
3057c2fbfbApril Chin		}
3067c2fbfbApril Chin	}
3077c2fbfbApril Chin	lp->dict = dp;
3087c2fbfbApril Chin	mp = (Namval_t*)&lp->dict;
309da2e3ebchin	lp->next = subshell_data->svar;
310da2e3ebchin	subshell_data->svar = lp;
3117c2fbfbApril Chin	save = shp->subshell;
3127c2fbfbApril Chin	shp->subshell = 0;
3137c2fbfbApril Chin	mp->nvname = np->nvname;
3147c2fbfbApril Chin	nv_clone(np,mp,(add?(nv_isnull(np)?0:NV_NOFREE)|NV_ARRAY:NV_MOVE));
3157c2fbfbApril Chin	shp->subshell = save;
316da2e3ebchin	return(np);
320da2e3ebchin * restore the variables
321da2e3ebchin */
322da2e3ebchinstatic void nv_restore(struct subshell *sp)
324da2e3ebchin	register struct Link *lp, *<