xref: /titanic_44/usr/src/lib/libshell/common/sh/subshell.c (revision de860bd9529e1034e1666f74f7bc0ec8cd5ca701)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *           Copyright (c) 1982-2007 AT&T Knowledge Ventures            *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                      by AT&T Knowledge Ventures                      *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                  David Korn <dgk@research.att.com>                   *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22  *   Create and manage subshells avoiding forks when possible
23  *
24  *   David Korn
25  *   AT&T Labs
26  *
27  */
28 
29 #include	"defs.h"
30 #include	<ls.h>
31 #include	"io.h"
32 #include	"fault.h"
33 #include	"shnodes.h"
34 #include	"shlex.h"
35 #include	"jobs.h"
36 #include	"variables.h"
37 #include	"path.h"
38 
39 #ifndef PIPE_BUF
40 #   define PIPE_BUF	512
41 #endif
42 
43 /*
44  * Note that the following structure must be the same
45  * size as the Dtlink_t structure
46  */
47 struct Link
48 {
49 	struct Link	*next;
50 	Namval_t	*node;
51 };
52 
53 /*
54  * The following structure is used for command substitution and (...)
55  */
56 static struct subshell
57 {
58 	struct subshell	*prev;	/* previous subshell data */
59 	struct subshell	*pipe;	/* subshell where output goes to pipe on fork */
60 	Dt_t		*var;	/* variable table at time of subshell */
61 	struct Link	*svar;	/* save shell variable table */
62 	Dt_t		*sfun;	/* function scope for subshell */
63 	Dt_t		*salias;/* alias scope for subshell */
64 #ifdef PATH_BFPATH
65 	Pathcomp_t	*pathlist; /* for PATH variable */
66 #endif
67 #if (ERROR_VERSION >= 20030214L)
68 	struct Error_context_s *errcontext;
69 #else
70 	struct errorcontext *errcontext;
71 #endif
72 	Shopt_t		options;/* save shell options */
73 	pid_t		subpid;	/* child process id */
74 	Sfio_t*	saveout;/*saved standard output */
75 	char		*pwd;	/* present working directory */
76 	const char	*shpwd;	/* saved pointer to sh.pwd */
77 	void		*jobs;	/* save job info */
78 	mode_t		mask;	/* saved umask */
79 	short		tmpfd;	/* saved tmp file descriptor */
80 	short		pipefd;	/* read fd if pipe is created */
81 	char		jobcontrol;
82 	char		monitor;
83 	unsigned char	fdstatus;
84 	int		fdsaved; /* bit make for saved files */
85 	int		bckpid;
86 } *subshell_data;
87 
88 static int subenv;
89 
90 /*
91  * This routine will turn the sftmp() file into a real /tmp file or pipe
92  * if the /tmp file create fails
93  */
94 void	sh_subtmpfile(void)
95 {
96 	if(sfset(sfstdout,0,0)&SF_STRING)
97 	{
98 		register int fd;
99 		register struct checkpt	*pp = (struct checkpt*)sh.jmplist;
100 		register struct subshell *sp = subshell_data->pipe;
101 		/* save file descriptor 1 if open */
102 		if((sp->tmpfd = fd = fcntl(1,F_DUPFD,10)) >= 0)
103 		{
104 			fcntl(fd,F_SETFD,FD_CLOEXEC);
105 			sh.fdstatus[fd] = sh.fdstatus[1]|IOCLEX;
106 			close(1);
107 		}
108 		else if(errno!=EBADF)
109 			errormsg(SH_DICT,ERROR_system(1),e_toomany);
110 		/* popping a discipline forces a /tmp file create */
111 		sfdisc(sfstdout,SF_POPDISC);
112 		if((fd=sffileno(sfstdout))<0)
113 		{
114 			/* unable to create the /tmp file so use a pipe */
115 			int fds[2];
116 			Sfoff_t off;
117 			sh_pipe(fds);
118 			sp->pipefd = fds[0];
119 			sh_fcntl(sp->pipefd,F_SETFD,FD_CLOEXEC);
120 			/* write the data to the pipe */
121 			if(off = sftell(sfstdout))
122 				write(fds[1],sfsetbuf(sfstdout,(Void_t*)sfstdout,0),(size_t)off);
123 			sfclose(sfstdout);
124 			if((sh_fcntl(fds[1],F_DUPFD, 1)) != 1)
125 				errormsg(SH_DICT,ERROR_system(1),e_file+4);
126 			sh_close(fds[1]);
127 		}
128 		else
129 		{
130 			sh.fdstatus[fd] = IOREAD|IOWRITE;
131 			sfsync(sfstdout);
132 			if(fd==1)
133 				fcntl(1,F_SETFD,0);
134 			else
135 			{
136 				sfsetfd(sfstdout,1);
137 				sh.fdstatus[1] = sh.fdstatus[fd];
138 				sh.fdstatus[fd] = IOCLOSE;
139 			}
140 		}
141 		sh_iostream(1);
142 		sfset(sfstdout,SF_SHARE|SF_PUBLIC,1);
143 		sfpool(sfstdout,sh.outpool,SF_WRITE);
144 		if(pp && pp->olist  && pp->olist->strm == sfstdout)
145 			pp->olist->strm = 0;
146 	}
147 }
148 
149 /*
150  * This routine creates a temp file if necessary and creates a subshell.
151  * The parent routine longjmps back to sh_subshell()
152  * The child continues possibly with its standard output replaced by temp file
153  */
154 void sh_subfork(void)
155 {
156 	register struct subshell *sp = subshell_data;
157 	pid_t pid;
158 	/* see whether inside $(...) */
159 	if(sp->pipe)
160 		sh_subtmpfile();
161 	if(pid = sh_fork(0,NIL(int*)))
162 	{
163 		/* this is the parent part of the fork */
164 		if(sp->subpid==0)
165 			sp->subpid = pid;
166 		siglongjmp(*sh.jmplist,SH_JMPSUB);
167 	}
168 	else
169 	{
170 		int16_t subshell;
171 		/* this is the child part of the fork */
172 		/* setting subpid to 1 causes subshell to exit when reached */
173 		sh_onstate(SH_FORKED);
174 		sh_onstate(SH_NOLOG);
175 		sh_offstate(SH_MONITOR);
176 		subshell_data = 0;
177 		subshell = sh.subshell = 0;
178 		nv_putval(SH_SUBSHELLNOD, (char*)&subshell, NV_INT16);
179 		sp->subpid=0;
180 	}
181 }
182 
183 /*
184  * This routine will make a copy of the given node in the
185  * layer created by the most recent subshell_fork if the
186  * node hasn't already been copied
187  */
188 Namval_t *sh_assignok(register Namval_t *np,int add)
189 {
190 	register Namval_t *mp;
191 	register struct Link *lp;
192 	register struct subshell *sp = (struct subshell*)subshell_data;
193 	int save;
194 	/* don't bother with this */
195 	if(!sp->shpwd || (nv_isnull(np) && !add))
196 		return(np);
197 	/* don't bother to save if in newer scope */
198 	if(nv_search((char*)np,sp->var,HASH_BUCKET)!=np)
199 		return(np);
200 	for(lp=subshell_data->svar; lp; lp = lp->next)
201 	{
202 		if(lp->node==np)
203 			return(np);
204 	}
205 	mp =  newof(0,Namval_t,1,0);
206 	lp = (struct Link*)mp;
207 	lp->node = np;
208 	lp->next = subshell_data->svar;
209 	subshell_data->svar = lp;
210 	save = sh.subshell;
211 	sh.subshell = 0;;
212 	nv_clone(np,mp,NV_NOFREE);
213 	sh.subshell = save;
214 	return(np);
215 }
216 
217 /*
218  * restore the variables
219  */
220 static void nv_restore(struct subshell *sp)
221 {
222 	register struct Link *lp, *lq;
223 	register Namval_t *mp, *np;
224 	const char *save = sp->shpwd;
225 	sp->shpwd = 0;	/* make sure sh_assignok doesn't save with nv_unset() */
226 	for(lp=sp->svar; lp; lp=lq)
227 	{
228 		np = (Namval_t*)lp;
229 		mp = lp->node;
230 		lq = lp->next;
231 		if(nv_isarray(mp))
232 			 nv_putsub(mp,NIL(char*),ARRAY_SCAN);
233 		_nv_unset(mp,NV_RDONLY);
234 		nv_setsize(mp,nv_size(np));
235 		if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(np,NV_EXPORT))
236 			mp->nvenv = np->nvenv;
237 		mp->nvfun = np->nvfun;
238 		mp->nvflag = np->nvflag;
239 		if((mp==nv_scoped(PATHNOD)) || (mp==nv_scoped(IFSNOD)))
240 			nv_putval(mp, np->nvalue.cp,0);
241 		else
242 			mp->nvalue.cp = np->nvalue.cp;
243 		np->nvfun = 0;
244 		if(nv_isattr(mp,NV_EXPORT))
245 		{
246 			char *name = nv_name(mp);
247 			sh_envput(sh.env,mp);
248 			if(*name=='_' && strcmp(name,"_AST_FEATURES")==0)
249 				astconf(NiL, NiL, NiL);
250 		}
251 		else if(nv_isattr(np,NV_EXPORT))
252 			env_delete(sh.env,nv_name(mp));
253 		free((void*)np);
254 	}
255 	sp->shpwd=save;
256 }
257 
258 /*
259  * return pointer to alias tree
260  * create new one if in a subshell and one doesn't exist and create is non-zero
261  */
262 Dt_t *sh_subaliastree(int create)
263 {
264 	register struct subshell *sp = subshell_data;
265 	if(!sp || sh.curenv==0)
266 		return(sh.alias_tree);
267 	if(!sp->salias && create)
268 	{
269 		sp->salias = dtopen(&_Nvdisc,Dtoset);
270 		dtview(sp->salias,sh.alias_tree);
271 		sh.alias_tree = sp->salias;
272 	}
273 	return(sp->salias);
274 }
275 
276 /*
277  * return pointer to function tree
278  * create new one if in a subshell and one doesn't exist and create is non-zero
279  */
280 Dt_t *sh_subfuntree(int create)
281 {
282 	register struct subshell *sp = subshell_data;
283 	if(!sp || sh.curenv==0)
284 		return(sh.fun_tree);
285 	if(!sp->sfun && create)
286 	{
287 		sp->sfun = dtopen(&_Nvdisc,Dtoset);
288 		dtview(sp->sfun,sh.fun_tree);
289 		sh.fun_tree = sp->sfun;
290 	}
291 	return(sp->sfun);
292 }
293 
294 static void table_unset(register Dt_t *root)
295 {
296 	register Namval_t *np,*nq;
297 	for(np=(Namval_t*)dtfirst(root);np;np=nq)
298 	{
299 		_nv_unset(np,NV_RDONLY);
300 		nq = (Namval_t*)dtnext(root,np);
301 		dtdelete(root,np);
302 		free((void*)np);
303 	}
304 }
305 
306 int sh_subsavefd(register int fd)
307 {
308 	register struct subshell *sp = subshell_data;
309 	register int old=0;
310 	if(sp)
311 	{
312 		old = !(sp->fdsaved&(1<<(fd-1)));
313 		sp->fdsaved |= (1<<(fd-1));
314 	}
315 	return(old);
316 }
317 
318 /*
319  * Run command tree <t> in a virtual sub-shell
320  * If comsub is not null, then output will be placed in temp file (or buffer)
321  * If comsub is not null, the return value will be a stream consisting of
322  * output of command <t>.  Otherwise, NULL will be returned.
323  */
324 
325 Sfio_t *sh_subshell(Shnode_t *t, int flags, int comsub)
326 {
327 	Shell_t *shp = &sh;
328 	struct subshell sub_data;
329 	register struct subshell *sp = &sub_data;
330 	int jmpval,nsig;
331 	int savecurenv = shp->curenv;
332 	int16_t subshell;
333 	char *savsig;
334 	Sfio_t *iop=0;
335 	struct checkpt buff;
336 	struct sh_scoped savst;
337 	struct dolnod   *argsav=0;
338 	memset((char*)sp, 0, sizeof(*sp));
339 	sfsync(shp->outpool);
340 	argsav = sh_arguse();
341 	if(shp->curenv==0)
342 	{
343 		subshell_data=0;
344 		subenv = 0;
345 	}
346 	shp->curenv = ++subenv;
347 	savst = shp->st;
348 	sh_pushcontext(&buff,SH_JMPSUB);
349 	subshell = shp->subshell+1;
350 	nv_putval(SH_SUBSHELLNOD, (char*)&subshell, NV_INT16);
351 	shp->subshell = subshell;
352 	sp->prev = subshell_data;
353 	subshell_data = sp;
354 	sp->errcontext = &buff.err;
355 	sp->var = shp->var_tree;
356 	sp->options = shp->options;
357 	sp->jobs = job_subsave();
358 #ifdef PATH_BFPATH
359 	/* make sure initialization has occurred */
360 	if(!shp->pathlist)
361 		path_get(".");
362 	sp->pathlist = path_dup((Pathcomp_t*)shp->pathlist);
363 #endif
364 	if(!shp->pwd)
365 		path_pwd(0);
366 	sp->bckpid = shp->bckpid;
367 	if(!comsub || !sh_isoption(SH_SUBSHARE))
368 	{
369 		sp->shpwd = shp->pwd;
370 		sp->pwd = (shp->pwd?strdup(shp->pwd):0);
371 		sp->mask = shp->mask;
372 		/* save trap table */
373 		shp->st.otrapcom = 0;
374 		if((nsig=shp->st.trapmax*sizeof(char*))>0 || shp->st.trapcom[0])
375 		{
376 			nsig += sizeof(char*);
377 			memcpy(savsig=malloc(nsig),(char*)&shp->st.trapcom[0],nsig);
378 			/* this nonsense needed for $(trap) */
379 			shp->st.otrapcom = (char**)savsig;
380 		}
381 		sh_sigreset(0);
382 	}
383 	jmpval = sigsetjmp(buff.buff,0);
384 	if(jmpval==0)
385 	{
386 		if(comsub)
387 		{
388 			/* disable job control */
389 			sp->jobcontrol = job.jobcontrol;
390 			sp->monitor = (sh_isstate(SH_MONITOR)!=0);
391 			job.jobcontrol=0;
392 			sh_offstate(SH_MONITOR);
393 			sp->pipe = sp;
394 			/* save sfstdout and status */
395 			sp->saveout = sfswap(sfstdout,NIL(Sfio_t*));
396 			sp->fdstatus = shp->fdstatus[1];
397 			sp->tmpfd = -1;
398 			sp->pipefd = -1;
399 			/* use sftmp() file for standard output */
400 			if(!(iop = sftmp(PIPE_BUF)))
401 			{
402 				sfswap(sp->saveout,sfstdout);
403 				errormsg(SH_DICT,ERROR_system(1),e_tmpcreate);
404 			}
405 			sfswap(iop,sfstdout);
406 			sfset(sfstdout,SF_READ,0);
407 			shp->fdstatus[1] = IOWRITE;
408 		}
409 		else if(sp->prev)
410 		{
411 			sp->pipe = sp->prev->pipe;
412 			flags &= ~sh_state(SH_NOFORK);
413 		}
414 		sh_exec(t,flags);
415 	}
416 	if(jmpval!=SH_JMPSUB && shp->st.trapcom[0] && shp->subshell)
417 	{
418 		/* trap on EXIT not handled by child */
419 		char *trap=shp->st.trapcom[0];
420 		shp->st.trapcom[0] = 0;	/* prevent recursion */
421 		shp->oldexit = shp->exitval;
422 		sh_trap(trap,0);
423 		free(trap);
424 	}
425 	sh_popcontext(&buff);
426 	if(shp->subshell==0)	/* must be child process */
427 	{
428 		subshell_data = sp->prev;
429 		if(jmpval==SH_JMPSCRIPT)
430 			siglongjmp(*shp->jmplist,jmpval);
431 		sh_done(0);
432 	}
433 	if(comsub)
434 	{
435 		/* re-enable job control */
436 		job.jobcontrol = sp->jobcontrol;
437 		if(sp->monitor)
438 			sh_onstate(SH_MONITOR);
439 		if(sp->pipefd>=0)
440 		{
441 			/* sftmp() file has been returned into pipe */
442 			iop = sh_iostream(sp->pipefd);
443 			sfdisc(iop,SF_POPDISC);
444 			sfclose(sfstdout);
445 		}
446 		else
447 		{
448 			/* move tmp file to iop and restore sfstdout */
449 			iop = sfswap(sfstdout,NIL(Sfio_t*));
450 			if(!iop)
451 			{
452 				/* maybe locked try again */
453 				sfclrlock(sfstdout);
454 				iop = sfswap(sfstdout,NIL(Sfio_t*));
455 			}
456 			if(iop && sffileno(iop)==1)
457 			{
458 				int fd=sfsetfd(iop,3);
459 				if(fd<0)
460 					errormsg(SH_DICT,ERROR_system(1),e_toomany);
461 				shp->sftable[fd] = iop;
462 				fcntl(fd,F_SETFD,FD_CLOEXEC);
463 				shp->fdstatus[fd] = (shp->fdstatus[1]|IOCLEX);
464 				shp->fdstatus[1] = IOCLOSE;
465 			}
466 			sfset(iop,SF_READ,1);
467 		}
468 		sfswap(sp->saveout,sfstdout);
469 		/*  check if standard output was preserved */
470 		if(sp->tmpfd>=0)
471 		{
472 			close(1);
473 			fcntl(sp->tmpfd,F_DUPFD,1);
474 			sh_close(sp->tmpfd);
475 		}
476 		shp->fdstatus[1] = sp->fdstatus;
477 	}
478 	if(sp->subpid)
479 		job_wait(sp->subpid);
480 	if(comsub && iop)
481 		sfseek(iop,(off_t)0,SEEK_SET);
482 	if(shp->subshell)
483 		shp->subshell--;
484 	subshell = shp->subshell;
485 	nv_putval(SH_SUBSHELLNOD, (char*)&subshell, NV_INT16);
486 #ifdef PATH_BFPATH
487 	path_delete((Pathcomp_t*)shp->pathlist);
488 	shp->pathlist = (void*)sp->pathlist;
489 #endif
490 	job_subrestore(sp->jobs);
491 	shp->jobenv = savecurenv;
492 	shp->bckpid = sp->bckpid;
493 	if(sp->shpwd)	/* restore environment if saved */
494 	{
495 		shp->options = sp->options;
496 		nv_restore(sp);
497 		if(sp->salias)
498 		{
499 			shp->alias_tree = dtview(sp->salias,0);
500 			table_unset(sp->salias);
501 			dtclose(sp->salias);
502 		}
503 		if(sp->sfun)
504 		{
505 			shp->fun_tree = dtview(sp->sfun,0);
506 			table_unset(sp->sfun);
507 			dtclose(sp->sfun);
508 		}
509 		sh_sigreset(1);
510 		shp->st = savst;
511 		shp->curenv = savecurenv;
512 		if(nsig)
513 		{
514 			memcpy((char*)&shp->st.trapcom[0],savsig,nsig);
515 			free((void*)savsig);
516 		}
517 		shp->options = sp->options;
518 		if(!shp->pwd || strcmp(sp->pwd,shp->pwd))
519 		{
520 			/* restore PWDNOD */
521 			Namval_t *pwdnod = nv_scoped(PWDNOD);
522 			if(shp->pwd)
523 			{
524 				chdir(shp->pwd=sp->pwd);
525 #ifdef PATH_BFPATH
526 				path_newdir(shp->pathlist);
527 #endif
528 			}
529 			if(nv_isattr(pwdnod,NV_NOFREE))
530 				pwdnod->nvalue.cp = (const char*)sp->pwd;
531 		}
532 		else if(sp->shpwd != shp->pwd)
533 		{
534 			shp->pwd = sp->pwd;
535 			if(PWDNOD->nvalue.cp==sp->shpwd)
536 				PWDNOD->nvalue.cp = sp->pwd;
537 		}
538 		else
539 			free((void*)sp->pwd);
540 		if(sp->mask!=shp->mask)
541 			umask(shp->mask);
542 	}
543 	subshell_data = sp->prev;
544 	sh_argfree(argsav,0);
545 	shp->trapnote = 0;
546 	if(shp->topfd != buff.topfd)
547 		sh_iorestore(buff.topfd|IOSUBSHELL,jmpval);
548 	if(shp->exitval > SH_EXITSIG)
549 	{
550 		int sig = shp->exitval&SH_EXITMASK;
551 		if(sig==SIGINT || sig== SIGQUIT)
552 			sh_fault(sig);
553 	}
554 	return(iop);
555 }
556