1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin * *
3da2e3ebdSchin * This software is part of the ast package *
4*3e14f97fSRoger A. Faulkner * Copyright (c) 1982-2010 AT&T Intellectual Property *
5da2e3ebdSchin * and is licensed under the *
6da2e3ebdSchin * Common Public License, Version 1.0 *
77c2fbfb3SApril Chin * by AT&T Intellectual Property *
8da2e3ebdSchin * *
9da2e3ebdSchin * A copy of the License is available at *
10da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt *
11da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12da2e3ebdSchin * *
13da2e3ebdSchin * Information and Software Systems Research *
14da2e3ebdSchin * AT&T Research *
15da2e3ebdSchin * Florham Park NJ *
16da2e3ebdSchin * *
17da2e3ebdSchin * David Korn <dgk@research.att.com> *
18da2e3ebdSchin * *
19da2e3ebdSchin ***********************************************************************/
20da2e3ebdSchin #pragma prototyped
21da2e3ebdSchin /*
22da2e3ebdSchin * Create and manage subshells avoiding forks when possible
23da2e3ebdSchin *
24da2e3ebdSchin * David Korn
25da2e3ebdSchin * AT&T Labs
26da2e3ebdSchin *
27da2e3ebdSchin */
28da2e3ebdSchin
29da2e3ebdSchin #include "defs.h"
30da2e3ebdSchin #include <ls.h>
31da2e3ebdSchin #include "io.h"
32da2e3ebdSchin #include "fault.h"
33da2e3ebdSchin #include "shnodes.h"
34da2e3ebdSchin #include "shlex.h"
35da2e3ebdSchin #include "jobs.h"
36da2e3ebdSchin #include "variables.h"
37da2e3ebdSchin #include "path.h"
38da2e3ebdSchin
39da2e3ebdSchin #ifndef PIPE_BUF
40da2e3ebdSchin # define PIPE_BUF 512
41da2e3ebdSchin #endif
42da2e3ebdSchin
43da2e3ebdSchin /*
44da2e3ebdSchin * Note that the following structure must be the same
45da2e3ebdSchin * size as the Dtlink_t structure
46da2e3ebdSchin */
47da2e3ebdSchin struct Link
48da2e3ebdSchin {
49da2e3ebdSchin struct Link *next;
507c2fbfb3SApril Chin Namval_t *child;
517c2fbfb3SApril Chin Dt_t *dict;
52da2e3ebdSchin Namval_t *node;
53da2e3ebdSchin };
54da2e3ebdSchin
55da2e3ebdSchin /*
56da2e3ebdSchin * The following structure is used for command substitution and (...)
57da2e3ebdSchin */
58da2e3ebdSchin static struct subshell
59da2e3ebdSchin {
607c2fbfb3SApril Chin Shell_t *shp; /* shell interpreter */
61da2e3ebdSchin struct subshell *prev; /* previous subshell data */
62da2e3ebdSchin struct subshell *pipe; /* subshell where output goes to pipe on fork */
63da2e3ebdSchin Dt_t *var; /* variable table at time of subshell */
64da2e3ebdSchin struct Link *svar; /* save shell variable table */
65da2e3ebdSchin Dt_t *sfun; /* function scope for subshell */
66da2e3ebdSchin Dt_t *salias;/* alias scope for subshell */
67da2e3ebdSchin Pathcomp_t *pathlist; /* for PATH variable */
68da2e3ebdSchin #if (ERROR_VERSION >= 20030214L)
69da2e3ebdSchin struct Error_context_s *errcontext;
70da2e3ebdSchin #else
71da2e3ebdSchin struct errorcontext *errcontext;
72da2e3ebdSchin #endif
73da2e3ebdSchin Shopt_t options;/* save shell options */
74da2e3ebdSchin pid_t subpid; /* child process id */
75da2e3ebdSchin Sfio_t* saveout;/*saved standard output */
76da2e3ebdSchin char *pwd; /* present working directory */
77da2e3ebdSchin const char *shpwd; /* saved pointer to sh.pwd */
78da2e3ebdSchin void *jobs; /* save job info */
79da2e3ebdSchin mode_t mask; /* saved umask */
80da2e3ebdSchin short tmpfd; /* saved tmp file descriptor */
81da2e3ebdSchin short pipefd; /* read fd if pipe is created */
82da2e3ebdSchin char jobcontrol;
83da2e3ebdSchin char monitor;
84da2e3ebdSchin unsigned char fdstatus;
85da2e3ebdSchin int fdsaved; /* bit make for saved files */
867c2fbfb3SApril Chin int sig; /* signal for $$ */
877c2fbfb3SApril Chin pid_t bckpid;
887c2fbfb3SApril Chin pid_t cpid;
897c2fbfb3SApril Chin int coutpipe;
907c2fbfb3SApril Chin int cpipe;
917c2fbfb3SApril Chin int nofork;
9281af778eSCasper H.S. Dik char subshare;
93da2e3ebdSchin } *subshell_data;
94da2e3ebdSchin
95da2e3ebdSchin static int subenv;
96da2e3ebdSchin
97da2e3ebdSchin /*
98da2e3ebdSchin * This routine will turn the sftmp() file into a real /tmp file or pipe
99da2e3ebdSchin */
sh_subtmpfile(int pflag)1007c2fbfb3SApril Chin void sh_subtmpfile(int pflag)
101da2e3ebdSchin {
1027c2fbfb3SApril Chin Shell_t *shp = &sh;
1037c2fbfb3SApril Chin int fds[2];
1047c2fbfb3SApril Chin Sfoff_t off;
10534f9b3eeSRoland Mainz register struct checkpt *pp = (struct checkpt*)shp->jmplist;
10634f9b3eeSRoland Mainz register struct subshell *sp = subshell_data->pipe;
107da2e3ebdSchin if(sfset(sfstdout,0,0)&SF_STRING)
108da2e3ebdSchin {
109da2e3ebdSchin register int fd;
110da2e3ebdSchin /* save file descriptor 1 if open */
111da2e3ebdSchin if((sp->tmpfd = fd = fcntl(1,F_DUPFD,10)) >= 0)
112da2e3ebdSchin {
113da2e3ebdSchin fcntl(fd,F_SETFD,FD_CLOEXEC);
1147c2fbfb3SApril Chin shp->fdstatus[fd] = shp->fdstatus[1]|IOCLEX;
115da2e3ebdSchin close(1);
11634f9b3eeSRoland Mainz shp->fdstatus[1] = IOCLOSE;
117da2e3ebdSchin }
118da2e3ebdSchin else if(errno!=EBADF)
11934f9b3eeSRoland Mainz {
12034f9b3eeSRoland Mainz ((struct checkpt*)shp->jmplist)->mode = SH_JMPERREXIT;
12134f9b3eeSRoland Mainz shp->toomany = 1;
122da2e3ebdSchin errormsg(SH_DICT,ERROR_system(1),e_toomany);
12334f9b3eeSRoland Mainz }
12434f9b3eeSRoland Mainz if(shp->subshare || !pflag)
125da2e3ebdSchin {
1267c2fbfb3SApril Chin sfdisc(sfstdout,SF_POPDISC);
1277c2fbfb3SApril Chin if((fd=sffileno(sfstdout))>=0)
128da2e3ebdSchin {
12934f9b3eeSRoland Mainz shp->fdstatus[fd] = IOREAD|IOWRITE;
130da2e3ebdSchin sfsync(sfstdout);
131da2e3ebdSchin if(fd==1)
132da2e3ebdSchin fcntl(1,F_SETFD,0);
133da2e3ebdSchin else
134da2e3ebdSchin {
135da2e3ebdSchin sfsetfd(sfstdout,1);
13634f9b3eeSRoland Mainz shp->fdstatus[1] = shp->fdstatus[fd];
13734f9b3eeSRoland Mainz shp->fdstatus[fd] = IOCLOSE;
138da2e3ebdSchin }
1397c2fbfb3SApril Chin goto skip;
140da2e3ebdSchin }
1417c2fbfb3SApril Chin }
14234f9b3eeSRoland Mainz }
14334f9b3eeSRoland Mainz if(sp && (shp->fdstatus[1]==IOCLOSE || (!shp->subshare && !(shp->fdstatus[1]&IONOSEEK))))
14434f9b3eeSRoland Mainz {
14534f9b3eeSRoland Mainz struct stat statb,statx;
14634f9b3eeSRoland Mainz int fd;
1477c2fbfb3SApril Chin sh_pipe(fds);
1487c2fbfb3SApril Chin sp->pipefd = fds[0];
1497c2fbfb3SApril Chin sh_fcntl(sp->pipefd,F_SETFD,FD_CLOEXEC);
1507c2fbfb3SApril Chin /* write the data to the pipe */
1517c2fbfb3SApril Chin if(off = sftell(sfstdout))
1527c2fbfb3SApril Chin {
1537c2fbfb3SApril Chin write(fds[1],sfsetbuf(sfstdout,(Void_t*)sfstdout,0),(size_t)off);
1547c2fbfb3SApril Chin sfpurge(sfstdout);
1557c2fbfb3SApril Chin }
15634f9b3eeSRoland Mainz if((sfset(sfstdout,0,0)&SF_STRING) || fstat(1,&statb)<0)
15734f9b3eeSRoland Mainz statb.st_ino = 0;
1587c2fbfb3SApril Chin sfclose(sfstdout);
1597c2fbfb3SApril Chin if((sh_fcntl(fds[1],F_DUPFD, 1)) != 1)
16034f9b3eeSRoland Mainz errormsg(SH_DICT,ERROR_system(1),e_redirect);
1617c2fbfb3SApril Chin sh_close(fds[1]);
16234f9b3eeSRoland Mainz if(statb.st_ino) for(fd=0; fd < 10; fd++)
16334f9b3eeSRoland Mainz {
16434f9b3eeSRoland Mainz if(fd==1 || ((shp->fdstatus[fd]&(IONOSEEK|IOSEEK|IOWRITE))!=(IOSEEK|IOWRITE)) || fstat(fd,&statx)<0)
16534f9b3eeSRoland Mainz continue;
16634f9b3eeSRoland Mainz if(statb.st_ino==statx.st_ino && statb.st_dev==statx.st_dev)
16734f9b3eeSRoland Mainz {
16834f9b3eeSRoland Mainz sh_close(fd);
16934f9b3eeSRoland Mainz fcntl(1,F_DUPFD, fd);
17034f9b3eeSRoland Mainz }
17134f9b3eeSRoland Mainz }
1727c2fbfb3SApril Chin skip:
1737c2fbfb3SApril Chin sh_iostream(shp,1);
174da2e3ebdSchin sfset(sfstdout,SF_SHARE|SF_PUBLIC,1);
1757c2fbfb3SApril Chin sfpool(sfstdout,shp->outpool,SF_WRITE);
176da2e3ebdSchin if(pp && pp->olist && pp->olist->strm == sfstdout)
177da2e3ebdSchin pp->olist->strm = 0;
178da2e3ebdSchin }
179da2e3ebdSchin }
180da2e3ebdSchin
18134f9b3eeSRoland Mainz
182da2e3ebdSchin /*
183da2e3ebdSchin * This routine creates a temp file if necessary and creates a subshell.
184da2e3ebdSchin * The parent routine longjmps back to sh_subshell()
185da2e3ebdSchin * The child continues possibly with its standard output replaced by temp file
186da2e3ebdSchin */
sh_subfork(void)187da2e3ebdSchin void sh_subfork(void)
188da2e3ebdSchin {
189da2e3ebdSchin register struct subshell *sp = subshell_data;
1907c2fbfb3SApril Chin Shell_t *shp = sp->shp;
1917c2fbfb3SApril Chin int curenv = shp->curenv;
192da2e3ebdSchin pid_t pid;
19334f9b3eeSRoland Mainz char *trap = shp->st.trapcom[0];
19434f9b3eeSRoland Mainz if(trap)
19534f9b3eeSRoland Mainz trap = strdup(trap);
196da2e3ebdSchin /* see whether inside $(...) */
197da2e3ebdSchin if(sp->pipe)
1987c2fbfb3SApril Chin sh_subtmpfile(1);
1997c2fbfb3SApril Chin shp->curenv = 0;
20034f9b3eeSRoland Mainz if(pid = sh_fork(FSHOWME,NIL(int*)))
201da2e3ebdSchin {
2027c2fbfb3SApril Chin shp->curenv = curenv;
203da2e3ebdSchin /* this is the parent part of the fork */
204da2e3ebdSchin if(sp->subpid==0)
205da2e3ebdSchin sp->subpid = pid;
20634f9b3eeSRoland Mainz if(trap)
20734f9b3eeSRoland Mainz free((void*)trap);
2087c2fbfb3SApril Chin siglongjmp(*shp->jmplist,SH_JMPSUB);
209da2e3ebdSchin }
210da2e3ebdSchin else
211da2e3ebdSchin {
212da2e3ebdSchin /* this is the child part of the fork */
213da2e3ebdSchin /* setting subpid to 1 causes subshell to exit when reached */
214da2e3ebdSchin sh_onstate(SH_FORKED);
215da2e3ebdSchin sh_onstate(SH_NOLOG);
216*3e14f97fSRoger A. Faulkner sh_offoption(SH_MONITOR);
217da2e3ebdSchin sh_offstate(SH_MONITOR);
218da2e3ebdSchin subshell_data = 0;
2197c2fbfb3SApril Chin shp->subshell = 0;
2207c2fbfb3SApril Chin SH_SUBSHELLNOD->nvalue.s = 0;
221da2e3ebdSchin sp->subpid=0;
22234f9b3eeSRoland Mainz shp->st.trapcom[0] = trap;
223da2e3ebdSchin }
224da2e3ebdSchin }
225da2e3ebdSchin
nv_subsaved(register Namval_t * np)2267c2fbfb3SApril Chin int nv_subsaved(register Namval_t *np)
2277c2fbfb3SApril Chin {
2287c2fbfb3SApril Chin register struct subshell *sp;
2297c2fbfb3SApril Chin register struct Link *lp;
2307c2fbfb3SApril Chin for(sp = (struct subshell*)subshell_data; sp; sp=sp->prev)
2317c2fbfb3SApril Chin {
2327c2fbfb3SApril Chin for(lp=sp->svar; lp; lp = lp->next)
2337c2fbfb3SApril Chin {
2347c2fbfb3SApril Chin if(lp->node==np)
2357c2fbfb3SApril Chin return(1);
2367c2fbfb3SApril Chin }
2377c2fbfb3SApril Chin }
2387c2fbfb3SApril Chin return(0);
2397c2fbfb3SApril Chin }
2407c2fbfb3SApril Chin
241da2e3ebdSchin /*
242da2e3ebdSchin * This routine will make a copy of the given node in the
243da2e3ebdSchin * layer created by the most recent subshell_fork if the
244da2e3ebdSchin * node hasn't already been copied
245da2e3ebdSchin */
sh_assignok(register Namval_t * np,int add)246da2e3ebdSchin Namval_t *sh_assignok(register Namval_t *np,int add)
247da2e3ebdSchin {
248da2e3ebdSchin register Namval_t *mp;
249da2e3ebdSchin register struct Link *lp;
250da2e3ebdSchin register struct subshell *sp = (struct subshell*)subshell_data;
2517c2fbfb3SApril Chin struct Ufunction *rp;
2527c2fbfb3SApril Chin Shell_t *shp = sp->shp;
2537c2fbfb3SApril Chin Dt_t *dp;
2547c2fbfb3SApril Chin Namval_t *mpnext;
2557c2fbfb3SApril Chin Namarr_t *ap;
256da2e3ebdSchin int save;
257da2e3ebdSchin /* don't bother with this */
25834f9b3eeSRoland Mainz if(!sp->shpwd || (nv_isnull(np) && !add) || np==SH_LEVELNOD)
259da2e3ebdSchin return(np);
260da2e3ebdSchin /* don't bother to save if in newer scope */
2617c2fbfb3SApril Chin if(!(rp=shp->st.real_fun) || !(dp=rp->sdict))
2627c2fbfb3SApril Chin dp = sp->var;
2637c2fbfb3SApril Chin if(np->nvenv && !nv_isattr(np,NV_MINIMAL|NV_EXPORT) && shp->last_root)
2647c2fbfb3SApril Chin dp = shp->last_root;
2657c2fbfb3SApril Chin if((mp=nv_search((char*)np,dp,HASH_BUCKET))!=np)
2667c2fbfb3SApril Chin {
2677c2fbfb3SApril Chin if(mp || !np->nvfun || np->nvfun->subshell>=sh.subshell)
268da2e3ebdSchin return(np);
2697c2fbfb3SApril Chin }
2707c2fbfb3SApril Chin if((ap=nv_arrayptr(np)) && (mp=nv_opensub(np)))
2717c2fbfb3SApril Chin {
2727c2fbfb3SApril Chin shp->last_root = ap->table;
2737c2fbfb3SApril Chin sh_assignok(mp,add);
2747c2fbfb3SApril Chin if(!add || array_assoc(ap))
2757c2fbfb3SApril Chin return(np);
2767c2fbfb3SApril Chin }
277da2e3ebdSchin for(lp=subshell_data->svar; lp; lp = lp->next)
278da2e3ebdSchin {
279da2e3ebdSchin if(lp->node==np)
280da2e3ebdSchin return(np);
281da2e3ebdSchin }
2827c2fbfb3SApril Chin /* first two pointers use linkage from np */
2837c2fbfb3SApril Chin lp = (struct Link*)malloc(sizeof(*np)+2*sizeof(void*));
2847c2fbfb3SApril Chin memset(lp,0, sizeof(*mp)+2*sizeof(void*));
285da2e3ebdSchin lp->node = np;
2867c2fbfb3SApril Chin if(!add && nv_isvtree(np))
2877c2fbfb3SApril Chin {
2887c2fbfb3SApril Chin Namval_t fake;
2897c2fbfb3SApril Chin Dt_t *walk, *root=shp->var_tree;
2907c2fbfb3SApril Chin char *name = nv_name(np);
2917c2fbfb3SApril Chin int len = strlen(name);
2927c2fbfb3SApril Chin fake.nvname = name;
2937c2fbfb3SApril Chin mpnext = dtnext(root,&fake);
2947c2fbfb3SApril Chin dp = root->walk?root->walk:root;
2957c2fbfb3SApril Chin while(mp=mpnext)
2967c2fbfb3SApril Chin {
2977c2fbfb3SApril Chin walk = root->walk?root->walk:root;
2987c2fbfb3SApril Chin mpnext = dtnext(root,mp);
2997c2fbfb3SApril Chin if(memcmp(name,mp->nvname,len) || mp->nvname[len]!='.')
3007c2fbfb3SApril Chin break;
3017c2fbfb3SApril Chin nv_delete(mp,walk,NV_NOFREE);
3027c2fbfb3SApril Chin *((Namval_t**)mp) = lp->child;
3037c2fbfb3SApril Chin lp->child = mp;
3047c2fbfb3SApril Chin
3057c2fbfb3SApril Chin }
3067c2fbfb3SApril Chin }
3077c2fbfb3SApril Chin lp->dict = dp;
3087c2fbfb3SApril Chin mp = (Namval_t*)&lp->dict;
309da2e3ebdSchin lp->next = subshell_data->svar;
310da2e3ebdSchin subshell_data->svar = lp;
3117c2fbfb3SApril Chin save = shp->subshell;
3127c2fbfb3SApril Chin shp->subshell = 0;
3137c2fbfb3SApril Chin mp->nvname = np->nvname;
3147c2fbfb3SApril Chin nv_clone(np,mp,(add?(nv_isnull(np)?0:NV_NOFREE)|NV_ARRAY:NV_MOVE));
3157c2fbfb3SApril Chin shp->subshell = save;
316da2e3ebdSchin return(np);
317da2e3ebdSchin }
318da2e3ebdSchin
319da2e3ebdSchin /*
320da2e3ebdSchin * restore the variables
321da2e3ebdSchin */
nv_restore(struct subshell * sp)322da2e3ebdSchin static void nv_restore(struct subshell *sp)
323da2e3ebdSchin {
324da2e3ebdSchin register struct Link *lp, *lq;
325da2e3ebdSchin register Namval_t *mp, *np;
326da2e3ebdSchin const char *save = sp->shpwd;
3277c2fbfb3SApril Chin Namval_t *mpnext;
328da2e3ebdSchin sp->shpwd = 0; /* make sure sh_assignok doesn't save with nv_unset() */
329da2e3ebdSchin for(lp=sp->svar; lp; lp=lq)
330da2e3ebdSchin {
3317c2fbfb3SApril Chin np = (Namval_t*)&lp->dict;
332da2e3ebdSchin lq = lp->next;
3337c2fbfb3SApril Chin mp = lp->node;
3347c2fbfb3SApril Chin if(!mp->nvname)
3357c2fbfb3SApril Chin continue;
336da2e3ebdSchin if(nv_isarray(mp))
337da2e3ebdSchin nv_putsub(mp,NIL(char*),ARRAY_SCAN);
33834f9b3eeSRoland Mainz _nv_unset(mp,NV_RDONLY|NV_CLONE);
3397c2fbfb3SApril Chin if(nv_isarray(np))
3407c2fbfb3SApril Chin {
3417c2fbfb3SApril Chin nv_clone(np,mp,NV_MOVE);
3427c2fbfb3SApril Chin goto skip;
3437c2fbfb3SApril Chin }
344da2e3ebdSchin nv_setsize(mp,nv_size(np));
345da2e3ebdSchin if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(np,NV_EXPORT))
346da2e3ebdSchin mp->nvenv = np->nvenv;
347da2e3ebdSchin mp->nvfun = np->nvfun;
348da2e3ebdSchin mp->nvflag = np->nvflag;
3497c2fbfb3SApril Chin if(nv_cover(mp))
35034f9b3eeSRoland Mainz {
35134f9b3eeSRoland Mainz nv_putval(mp, nv_getval(np),np->nvflag|NV_NOFREE);
35234f9b3eeSRoland Mainz if(!nv_isattr(np,NV_NOFREE))
35334f9b3eeSRoland Mainz nv_offattr(mp,NV_NOFREE);
35434f9b3eeSRoland Mainz }
355da2e3ebdSchin else
356da2e3ebdSchin mp->nvalue.cp = np->nvalue.cp;
357da2e3ebdSchin np->nvfun = 0;
358da2e3ebdSchin if(nv_isattr(mp,NV_EXPORT))
359da2e3ebdSchin {
360da2e3ebdSchin char *name = nv_name(mp);
361da2e3ebdSchin sh_envput(sh.env,mp);
362da2e3ebdSchin if(*name=='_' && strcmp(name,"_AST_FEATURES")==0)
363da2e3ebdSchin astconf(NiL, NiL, NiL);
364da2e3ebdSchin }
365da2e3ebdSchin else if(nv_isattr(np,NV_EXPORT))
366da2e3ebdSchin env_delete(sh.env,nv_name(mp));
3677c2fbfb3SApril Chin skip:
3687c2fbfb3SApril Chin for(mp=lp->child; mp; mp=mpnext)
3697c2fbfb3SApril Chin {
3707c2fbfb3SApril Chin mpnext = *((Namval_t**)mp);
3717c2fbfb3SApril Chin dtinsert(lp->dict,mp);
3727c2fbfb3SApril Chin }
3737c2fbfb3SApril Chin free((void*)lp);
3747c2fbfb3SApril Chin sp->svar = lq;
375da2e3ebdSchin }
376da2e3ebdSchin sp->shpwd=save;
377da2e3ebdSchin }
378da2e3ebdSchin
379da2e3ebdSchin /*
380da2e3ebdSchin * return pointer to alias tree
381da2e3ebdSchin * create new one if in a subshell and one doesn't exist and create is non-zero
382da2e3ebdSchin */
sh_subaliastree(int create)383da2e3ebdSchin Dt_t *sh_subaliastree(int create)
384da2e3ebdSchin {
385da2e3ebdSchin register struct subshell *sp = subshell_data;
386da2e3ebdSchin if(!sp || sh.curenv==0)
387da2e3ebdSchin return(sh.alias_tree);
388da2e3ebdSchin if(!sp->salias && create)
389da2e3ebdSchin {
390da2e3ebdSchin sp->salias = dtopen(&_Nvdisc,Dtoset);
391da2e3ebdSchin dtview(sp->salias,sh.alias_tree);
392da2e3ebdSchin sh.alias_tree = sp->salias;
393da2e3ebdSchin }
394da2e3ebdSchin return(sp->salias);
395da2e3ebdSchin }
396da2e3ebdSchin
397da2e3ebdSchin /*
398da2e3ebdSchin * return pointer to function tree
399da2e3ebdSchin * create new one if in a subshell and one doesn't exist and create is non-zero
400da2e3ebdSchin */
sh_subfuntree(int create)401da2e3ebdSchin Dt_t *sh_subfuntree(int create)
402da2e3ebdSchin {
403da2e3ebdSchin register struct subshell *sp = subshell_data;
404da2e3ebdSchin if(!sp || sh.curenv==0)
405da2e3ebdSchin return(sh.fun_tree);
406da2e3ebdSchin if(!sp->sfun && create)
407da2e3ebdSchin {
408da2e3ebdSchin sp->sfun = dtopen(&_Nvdisc,Dtoset);
409da2e3ebdSchin dtview(sp->sfun,sh.fun_tree);
410da2e3ebdSchin sh.fun_tree = sp->sfun;
411da2e3ebdSchin }
4127c2fbfb3SApril Chin return(sh.fun_tree);
413da2e3ebdSchin }
414da2e3ebdSchin
table_unset(register Dt_t * root,int fun)4157c2fbfb3SApril Chin static void table_unset(register Dt_t *root,int fun)
416da2e3ebdSchin {
417da2e3ebdSchin register Namval_t *np,*nq;
4187c2fbfb3SApril Chin int flag;
419da2e3ebdSchin for(np=(Namval_t*)dtfirst(root);np;np=nq)
420da2e3ebdSchin {
421da2e3ebdSchin nq = (Namval_t*)dtnext(root,np);
4227c2fbfb3SApril Chin flag=0;
42334f9b3eeSRoland Mainz if(fun && np->nvalue.rp && np->nvalue.rp->fname && *np->nvalue.rp->fname=='/')
4247c2fbfb3SApril Chin {
4257c2fbfb3SApril Chin np->nvalue.rp->fdict = 0;
4267c2fbfb3SApril Chin flag = NV_NOFREE;
4277c2fbfb3SApril Chin }
4287c2fbfb3SApril Chin else
4297c2fbfb3SApril Chin _nv_unset(np,NV_RDONLY);
4307c2fbfb3SApril Chin nv_delete(np,root,flag|NV_FUNCTION);
431da2e3ebdSchin }
432da2e3ebdSchin }
433da2e3ebdSchin
sh_subsavefd(register int fd)434da2e3ebdSchin int sh_subsavefd(register int fd)
435da2e3ebdSchin {
436da2e3ebdSchin register struct subshell *sp = subshell_data;
437da2e3ebdSchin register int old=0;
438da2e3ebdSchin if(sp)
439da2e3ebdSchin {
440da2e3ebdSchin old = !(sp->fdsaved&(1<<(fd-1)));
441da2e3ebdSchin sp->fdsaved |= (1<<(fd-1));
442da2e3ebdSchin }
443da2e3ebdSchin return(old);
444da2e3ebdSchin }
445da2e3ebdSchin
sh_subjobcheck(pid_t pid)4467c2fbfb3SApril Chin void sh_subjobcheck(pid_t pid)
4477c2fbfb3SApril Chin {
4487c2fbfb3SApril Chin register struct subshell *sp = subshell_data;
4497c2fbfb3SApril Chin while(sp)
4507c2fbfb3SApril Chin {
4517c2fbfb3SApril Chin if(sp->cpid==pid)
4527c2fbfb3SApril Chin {
4537c2fbfb3SApril Chin sh_close(sp->coutpipe);
4547c2fbfb3SApril Chin sh_close(sp->cpipe);
4557c2fbfb3SApril Chin sp->coutpipe = sp->cpipe = -1;
4567c2fbfb3SApril Chin return;
4577c2fbfb3SApril Chin }
4587c2fbfb3SApril Chin sp = sp->prev;
4597c2fbfb3SApril Chin }
4607c2fbfb3SApril Chin }
4617c2fbfb3SApril Chin
462da2e3ebdSchin /*
463da2e3ebdSchin * Run command tree <t> in a virtual sub-shell
464da2e3ebdSchin * If comsub is not null, then output will be placed in temp file (or buffer)
465da2e3ebdSchin * If comsub is not null, the return value will be a stream consisting of
466da2e3ebdSchin * output of command <t>. Otherwise, NULL will be returned.
467da2e3ebdSchin */
468da2e3ebdSchin
sh_subshell(Shnode_t * t,int flags,int comsub)469da2e3ebdSchin Sfio_t *sh_subshell(Shnode_t *t, int flags, int comsub)
470da2e3ebdSchin {
471da2e3ebdSchin Shell_t *shp = &sh;
472da2e3ebdSchin struct subshell sub_data;
473da2e3ebdSchin register struct subshell *sp = &sub_data;
47434f9b3eeSRoland Mainz int jmpval,nsig=0,duped=0;
475da2e3ebdSchin int savecurenv = shp->curenv;
4767c2fbfb3SApril Chin int savejobpgid = job.curpgid;
477da2e3ebdSchin int16_t subshell;
478da2e3ebdSchin char *savsig;
479da2e3ebdSchin Sfio_t *iop=0;
480da2e3ebdSchin struct checkpt buff;
481da2e3ebdSchin struct sh_scoped savst;
482da2e3ebdSchin struct dolnod *argsav=0;
483da2e3ebdSchin memset((char*)sp, 0, sizeof(*sp));
484da2e3ebdSchin sfsync(shp->outpool);
4857c2fbfb3SApril Chin argsav = sh_arguse(shp);
486da2e3ebdSchin if(shp->curenv==0)
487da2e3ebdSchin {
488da2e3ebdSchin subshell_data=0;
489da2e3ebdSchin subenv = 0;
490da2e3ebdSchin }
491da2e3ebdSchin shp->curenv = ++subenv;
4927c2fbfb3SApril Chin job.curpgid = 0;
493da2e3ebdSchin savst = shp->st;
494da2e3ebdSchin sh_pushcontext(&buff,SH_JMPSUB);
495da2e3ebdSchin subshell = shp->subshell+1;
4967c2fbfb3SApril Chin SH_SUBSHELLNOD->nvalue.s = subshell;
497da2e3ebdSchin shp->subshell = subshell;
498da2e3ebdSchin sp->prev = subshell_data;
4997c2fbfb3SApril Chin sp->shp = shp;
5007c2fbfb3SApril Chin sp->sig = 0;
501da2e3ebdSchin subshell_data = sp;
502da2e3ebdSchin sp->errcontext = &buff.err;
503da2e3ebdSchin sp->var = shp->var_tree;
504da2e3ebdSchin sp->options = shp->options;
505da2e3ebdSchin sp->jobs = job_subsave();
506da2e3ebdSchin /* make sure initialization has occurred */
507da2e3ebdSchin if(!shp->pathlist)
508da2e3ebdSchin path_get(".");
509da2e3ebdSchin sp->pathlist = path_dup((Pathcomp_t*)shp->pathlist);
510da2e3ebdSchin if(!shp->pwd)
511da2e3ebdSchin path_pwd(0);
512da2e3ebdSchin sp->bckpid = shp->bckpid;
5137c2fbfb3SApril Chin if(comsub)
5147c2fbfb3SApril Chin sh_stats(STAT_COMSUB);
51581af778eSCasper H.S. Dik sp->subshare = shp->subshare;
51681af778eSCasper H.S. Dik shp->subshare = comsub==2 || (comsub==1 && sh_isoption(SH_SUBSHARE));
51781af778eSCasper H.S. Dik if(!comsub || !shp->subshare)
518da2e3ebdSchin {
519da2e3ebdSchin sp->shpwd = shp->pwd;
520da2e3ebdSchin sp->pwd = (shp->pwd?strdup(shp->pwd):0);
521da2e3ebdSchin sp->mask = shp->mask;
5227c2fbfb3SApril Chin sh_stats(STAT_SUBSHELL);
523da2e3ebdSchin /* save trap table */
524da2e3ebdSchin shp->st.otrapcom = 0;
525da2e3ebdSchin if((nsig=shp->st.trapmax*sizeof(char*))>0 || shp->st.trapcom[0])
526da2e3ebdSchin {
527da2e3ebdSchin nsig += sizeof(char*);
528da2e3ebdSchin memcpy(savsig=malloc(nsig),(char*)&shp->st.trapcom[0],nsig);
529da2e3ebdSchin /* this nonsense needed for $(trap) */
530da2e3ebdSchin shp->st.otrapcom = (char**)savsig;
531da2e3ebdSchin }
5327c2fbfb3SApril Chin sp->cpid = shp->cpid;
5337c2fbfb3SApril Chin sp->coutpipe = shp->coutpipe;
5347c2fbfb3SApril Chin sp->cpipe = shp->cpipe[1];
5357c2fbfb3SApril Chin shp->cpid = 0;
536da2e3ebdSchin sh_sigreset(0);
537da2e3ebdSchin }
538da2e3ebdSchin jmpval = sigsetjmp(buff.buff,0);
539da2e3ebdSchin if(jmpval==0)
540da2e3ebdSchin {
541da2e3ebdSchin if(comsub)
542da2e3ebdSchin {
543da2e3ebdSchin /* disable job control */
5447c2fbfb3SApril Chin shp->spid = 0;
545da2e3ebdSchin sp->jobcontrol = job.jobcontrol;
546da2e3ebdSchin sp->monitor = (sh_isstate(SH_MONITOR)!=0);
547da2e3ebdSchin job.jobcontrol=0;
548da2e3ebdSchin sh_offstate(SH_MONITOR);
549da2e3ebdSchin sp->pipe = sp;
550da2e3ebdSchin /* save sfstdout and status */
551da2e3ebdSchin sp->saveout = sfswap(sfstdout,NIL(Sfio_t*));
552da2e3ebdSchin sp->fdstatus = shp->fdstatus[1];
553da2e3ebdSchin sp->tmpfd = -1;
554da2e3ebdSchin sp->pipefd = -1;
555da2e3ebdSchin /* use sftmp() file for standard output */
556da2e3ebdSchin if(!(iop = sftmp(PIPE_BUF)))
557da2e3ebdSchin {
558da2e3ebdSchin sfswap(sp->saveout,sfstdout);
559da2e3ebdSchin errormsg(SH_DICT,ERROR_system(1),e_tmpcreate);
560da2e3ebdSchin }
561da2e3ebdSchin sfswap(iop,sfstdout);
562da2e3ebdSchin sfset(sfstdout,SF_READ,0);
563da2e3ebdSchin shp->fdstatus[1] = IOWRITE;
5647c2fbfb3SApril Chin if(!(sp->nofork = sh_state(SH_NOFORK)))
5657c2fbfb3SApril Chin sh_onstate(SH_NOFORK);
5667c2fbfb3SApril Chin flags |= sh_state(SH_NOFORK);
567da2e3ebdSchin }
568da2e3ebdSchin else if(sp->prev)
569da2e3ebdSchin {
570da2e3ebdSchin sp->pipe = sp->prev->pipe;
571da2e3ebdSchin flags &= ~sh_state(SH_NOFORK);
572da2e3ebdSchin }
573da2e3ebdSchin sh_exec(t,flags);
574da2e3ebdSchin }
5757c2fbfb3SApril Chin if(comsub!=2 && jmpval!=SH_JMPSUB && shp->st.trapcom[0] && shp->subshell)
576da2e3ebdSchin {
577da2e3ebdSchin /* trap on EXIT not handled by child */
578da2e3ebdSchin char *trap=shp->st.trapcom[0];
579da2e3ebdSchin shp->st.trapcom[0] = 0; /* prevent recursion */
580da2e3ebdSchin shp->oldexit = shp->exitval;
581da2e3ebdSchin sh_trap(trap,0);
582da2e3ebdSchin free(trap);
583da2e3ebdSchin }
584da2e3ebdSchin sh_popcontext(&buff);
585da2e3ebdSchin if(shp->subshell==0) /* must be child process */
586da2e3ebdSchin {
587da2e3ebdSchin subshell_data = sp->prev;
588da2e3ebdSchin if(jmpval==SH_JMPSCRIPT)
589da2e3ebdSchin siglongjmp(*shp->jmplist,jmpval);
59034f9b3eeSRoland Mainz shp->exitval &= SH_EXITMASK;
5917c2fbfb3SApril Chin sh_done(shp,0);
592da2e3ebdSchin }
593da2e3ebdSchin if(comsub)
594da2e3ebdSchin {
595da2e3ebdSchin /* re-enable job control */
5967c2fbfb3SApril Chin if(!sp->nofork)
5977c2fbfb3SApril Chin sh_offstate(SH_NOFORK);
598da2e3ebdSchin job.jobcontrol = sp->jobcontrol;
599da2e3ebdSchin if(sp->monitor)
600da2e3ebdSchin sh_onstate(SH_MONITOR);
601da2e3ebdSchin if(sp->pipefd>=0)
602da2e3ebdSchin {
603da2e3ebdSchin /* sftmp() file has been returned into pipe */
6047c2fbfb3SApril Chin iop = sh_iostream(shp,sp->pipefd);
605da2e3ebdSchin sfclose(sfstdout);
606da2e3ebdSchin }
607da2e3ebdSchin else
608da2e3ebdSchin {
609da2e3ebdSchin /* move tmp file to iop and restore sfstdout */
610da2e3ebdSchin iop = sfswap(sfstdout,NIL(Sfio_t*));
611da2e3ebdSchin if(!iop)
612da2e3ebdSchin {
613da2e3ebdSchin /* maybe locked try again */
614da2e3ebdSchin sfclrlock(sfstdout);
615da2e3ebdSchin iop = sfswap(sfstdout,NIL(Sfio_t*));
616da2e3ebdSchin }
617da2e3ebdSchin if(iop && sffileno(iop)==1)
618da2e3ebdSchin {
619da2e3ebdSchin int fd=sfsetfd(iop,3);
620da2e3ebdSchin if(fd<0)
62134f9b3eeSRoland Mainz {
62234f9b3eeSRoland Mainz shp->toomany = 1;
62334f9b3eeSRoland Mainz ((struct checkpt*)shp->jmplist)->mode = SH_JMPERREXIT;
624da2e3ebdSchin errormsg(SH_DICT,ERROR_system(1),e_toomany);
62534f9b3eeSRoland Mainz }
626da2e3ebdSchin shp->sftable[fd] = iop;
627da2e3ebdSchin fcntl(fd,F_SETFD,FD_CLOEXEC);
628da2e3ebdSchin shp->fdstatus[fd] = (shp->fdstatus[1]|IOCLEX);
629da2e3ebdSchin shp->fdstatus[1] = IOCLOSE;
630da2e3ebdSchin }
631da2e3ebdSchin sfset(iop,SF_READ,1);
632da2e3ebdSchin }
633da2e3ebdSchin sfswap(sp->saveout,sfstdout);
634da2e3ebdSchin /* check if standard output was preserved */
635da2e3ebdSchin if(sp->tmpfd>=0)
636da2e3ebdSchin {
637da2e3ebdSchin close(1);
63834f9b3eeSRoland Mainz if (fcntl(sp->tmpfd,F_DUPFD,1) != 1)
63934f9b3eeSRoland Mainz duped++;
640da2e3ebdSchin sh_close(sp->tmpfd);
641da2e3ebdSchin }
642da2e3ebdSchin shp->fdstatus[1] = sp->fdstatus;
643da2e3ebdSchin }
644da2e3ebdSchin if(sp->subpid)
6457c2fbfb3SApril Chin {
6467c2fbfb3SApril Chin if(shp->exitval > SH_EXITSIG)
6477c2fbfb3SApril Chin sp->sig = (shp->exitval&SH_EXITMASK);
6487c2fbfb3SApril Chin shp->exitval = 0;
6497c2fbfb3SApril Chin if(comsub)
6507c2fbfb3SApril Chin shp->spid = sp->subpid;
6517c2fbfb3SApril Chin }
6527c2fbfb3SApril Chin if(comsub && iop && sp->pipefd<0)
653da2e3ebdSchin sfseek(iop,(off_t)0,SEEK_SET);
654da2e3ebdSchin path_delete((Pathcomp_t*)shp->pathlist);
655da2e3ebdSchin shp->pathlist = (void*)sp->pathlist;
656da2e3ebdSchin job_subrestore(sp->jobs);
657da2e3ebdSchin shp->jobenv = savecurenv;
6587c2fbfb3SApril Chin job.curpgid = savejobpgid;
659da2e3ebdSchin shp->bckpid = sp->bckpid;
660da2e3ebdSchin if(sp->shpwd) /* restore environment if saved */
661da2e3ebdSchin {
6627c2fbfb3SApril Chin int n;
663da2e3ebdSchin shp->options = sp->options;
664da2e3ebdSchin nv_restore(sp);
665da2e3ebdSchin if(sp->salias)
666da2e3ebdSchin {
667da2e3ebdSchin shp->alias_tree = dtview(sp->salias,0);
6687c2fbfb3SApril Chin table_unset(sp->salias,0);
669da2e3ebdSchin dtclose(sp->salias);
670da2e3ebdSchin }
671da2e3ebdSchin if(sp->sfun)
672da2e3ebdSchin {
673da2e3ebdSchin shp->fun_tree = dtview(sp->sfun,0);
6747c2fbfb3SApril Chin table_unset(sp->sfun,1);
675da2e3ebdSchin dtclose(sp->sfun);
676da2e3ebdSchin }
6777c2fbfb3SApril Chin n = shp->st.trapmax-savst.trapmax;
678da2e3ebdSchin sh_sigreset(1);
6797c2fbfb3SApril Chin if(n>0)
6807c2fbfb3SApril Chin memset(&shp->st.trapcom[savst.trapmax],0,n*sizeof(char*));
681da2e3ebdSchin shp->st = savst;
682da2e3ebdSchin shp->curenv = savecurenv;
683da2e3ebdSchin if(nsig)
684da2e3ebdSchin {
685da2e3ebdSchin memcpy((char*)&shp->st.trapcom[0],savsig,nsig);
686da2e3ebdSchin free((void*)savsig);
687da2e3ebdSchin }
688da2e3ebdSchin shp->options = sp->options;
689da2e3ebdSchin if(!shp->pwd || strcmp(sp->pwd,shp->pwd))
690da2e3ebdSchin {
691da2e3ebdSchin /* restore PWDNOD */
6927c2fbfb3SApril Chin Namval_t *pwdnod = sh_scoped(shp,PWDNOD);
693da2e3ebdSchin if(shp->pwd)
694da2e3ebdSchin {
695da2e3ebdSchin chdir(shp->pwd=sp->pwd);
696da2e3ebdSchin path_newdir(shp->pathlist);
697da2e3ebdSchin }
698da2e3ebdSchin if(nv_isattr(pwdnod,NV_NOFREE))
699da2e3ebdSchin pwdnod->nvalue.cp = (const char*)sp->pwd;
700da2e3ebdSchin }
701da2e3ebdSchin else if(sp->shpwd != shp->pwd)
702da2e3ebdSchin {
703da2e3ebdSchin shp->pwd = sp->pwd;
704da2e3ebdSchin if(PWDNOD->nvalue.cp==sp->shpwd)
705da2e3ebdSchin PWDNOD->nvalue.cp = sp->pwd;
706da2e3ebdSchin }
707da2e3ebdSchin else
708da2e3ebdSchin free((void*)sp->pwd);
709da2e3ebdSchin if(sp->mask!=shp->mask)
7107c2fbfb3SApril Chin umask(shp->mask=sp->mask);
71134f9b3eeSRoland Mainz if(shp->coutpipe!=sp->coutpipe)
7127c2fbfb3SApril Chin {
7137c2fbfb3SApril Chin sh_close(shp->coutpipe);
7147c2fbfb3SApril Chin sh_close(shp->cpipe[1]);
715da2e3ebdSchin }
7167c2fbfb3SApril Chin shp->cpid = sp->cpid;
7177c2fbfb3SApril Chin shp->cpipe[1] = sp->cpipe;
7187c2fbfb3SApril Chin shp->coutpipe = sp->coutpipe;
7197c2fbfb3SApril Chin }
72081af778eSCasper H.S. Dik shp->subshare = sp->subshare;
7217c2fbfb3SApril Chin if(shp->subshell)
7227c2fbfb3SApril Chin SH_SUBSHELLNOD->nvalue.s = --shp->subshell;
72334f9b3eeSRoland Mainz subshell = shp->subshell;
72434f9b3eeSRoland Mainz subshell_data = sp->prev;
72534f9b3eeSRoland Mainz sh_argfree(shp,argsav,0);
72634f9b3eeSRoland Mainz if(shp->topfd != buff.topfd)
72734f9b3eeSRoland Mainz sh_iorestore(shp,buff.topfd|IOSUBSHELL,jmpval);
7287c2fbfb3SApril Chin if(sp->sig)
7297c2fbfb3SApril Chin {
7307c2fbfb3SApril Chin if(sp->prev)
7317c2fbfb3SApril Chin sp->prev->sig = sp->sig;
7327c2fbfb3SApril Chin else
7337c2fbfb3SApril Chin {
7347c2fbfb3SApril Chin sh_fault(sp->sig);
7357c2fbfb3SApril Chin sh_chktrap();
7367c2fbfb3SApril Chin }
7377c2fbfb3SApril Chin }
73834f9b3eeSRoland Mainz sh_sigcheck();
739da2e3ebdSchin shp->trapnote = 0;
74034f9b3eeSRoland Mainz if(sp->subpid && !comsub)
74134f9b3eeSRoland Mainz job_wait(sp->subpid);
742da2e3ebdSchin if(shp->exitval > SH_EXITSIG)
743da2e3ebdSchin {
744da2e3ebdSchin int sig = shp->exitval&SH_EXITMASK;
745da2e3ebdSchin if(sig==SIGINT || sig== SIGQUIT)
746da2e3ebdSchin sh_fault(sig);
747da2e3ebdSchin }
74834f9b3eeSRoland Mainz if(duped)
74934f9b3eeSRoland Mainz {
75034f9b3eeSRoland Mainz ((struct checkpt*)shp->jmplist)->mode = SH_JMPERREXIT;
75134f9b3eeSRoland Mainz shp->toomany = 1;
75234f9b3eeSRoland Mainz errormsg(SH_DICT,ERROR_system(1),e_redirect);
75334f9b3eeSRoland Mainz }
75434f9b3eeSRoland Mainz if(jmpval && shp->toomany)
75534f9b3eeSRoland Mainz siglongjmp(*shp->jmplist,jmpval);
756da2e3ebdSchin return(iop);
757da2e3ebdSchin }
758