1N/A/***********************************************************************
1N/A* *
1N/A* This software is part of the ast package *
1N/A* Copyright (c) 1982-2011 AT&T Intellectual Property *
1N/A* and is licensed under the *
1N/A* Common Public License, Version 1.0 *
1N/A* by AT&T Intellectual Property *
1N/A* *
1N/A* A copy of the License is available at *
1N/A* http://www.opensource.org/licenses/cpl1.0.txt *
1N/A* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
1N/A* *
1N/A* Information and Software Systems Research *
1N/A* AT&T Research *
1N/A* Florham Park NJ *
1N/A* *
1N/A* David Korn <dgk@research.att.com> *
1N/A* *
1N/A***********************************************************************/
1N/A#pragma prototyped
1N/A/*
1N/A * Create and manage subshells avoiding forks when possible
1N/A *
1N/A * David Korn
1N/A * AT&T Labs
1N/A *
1N/A */
1N/A/*
1N/A * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
1N/A */
1N/A
1N/A#include "defs.h"
1N/A#include <ls.h>
1N/A#include "io.h"
1N/A#include "fault.h"
1N/A#include "shnodes.h"
1N/A#include "shlex.h"
1N/A#include "jobs.h"
1N/A#include "variables.h"
1N/A#include "path.h"
1N/A
1N/A#ifndef PIPE_BUF
1N/A# define PIPE_BUF 512
1N/A#endif
1N/A
1N/A/*
1N/A * Note that the following structure must be the same
1N/A * size as the Dtlink_t structure
1N/A */
1N/Astruct Link
1N/A{
1N/A struct Link *next;
1N/A Namval_t *child;
1N/A Dt_t *dict;
1N/A Namval_t *node;
1N/A};
1N/A
1N/A/*
1N/A * The following structure is used for command substitution and (...)
1N/A */
1N/Astatic struct subshell
1N/A{
1N/A Shell_t *shp; /* shell interpreter */
1N/A struct subshell *prev; /* previous subshell data */
1N/A struct subshell *pipe; /* subshell where output goes to pipe on fork */
1N/A Dt_t *var; /* variable table at time of subshell */
1N/A struct Link *svar; /* save shell variable table */
1N/A Dt_t *sfun; /* function scope for subshell */
1N/A Dt_t *salias;/* alias scope for subshell */
1N/A Pathcomp_t *pathlist; /* for PATH variable */
1N/A#if (ERROR_VERSION >= 20030214L)
1N/A struct Error_context_s *errcontext;
1N/A#else
1N/A struct errorcontext *errcontext;
1N/A#endif
1N/A Shopt_t options;/* save shell options */
1N/A pid_t subpid; /* child process id */
1N/A Sfio_t* saveout;/*saved standard output */
1N/A char *pwd; /* present working directory */
1N/A const char *shpwd; /* saved pointer to sh.pwd */
1N/A void *jobs; /* save job info */
1N/A mode_t mask; /* saved umask */
1N/A short tmpfd; /* saved tmp file descriptor */
1N/A short pipefd; /* read fd if pipe is created */
1N/A char jobcontrol;
1N/A char monitor;
1N/A unsigned char fdstatus;
1N/A int fdsaved; /* bit make for saved files */
1N/A int sig; /* signal for $$ */
1N/A pid_t bckpid;
1N/A pid_t cpid;
1N/A int coutpipe;
1N/A int cpipe;
1N/A int nofork;
1N/A int subdup;
1N/A char subshare;
1N/A char comsub;
1N/A#if SHOPT_COSHELL
1N/A void *coshell;
1N/A#endif /* SHOPT_COSHELL */
1N/A} *subshell_data;
1N/A
1N/Astatic int subenv;
1N/A
1N/A
1N/A/*
1N/A * This routine will turn the sftmp() file into a real /tmp file or pipe
1N/A * if the /tmp file create fails
1N/A */
1N/Avoid sh_subtmpfile(Shell_t *shp)
1N/A{
1N/A if(sfset(sfstdout,0,0)&SF_STRING)
1N/A {
1N/A register int fd;
1N/A register struct checkpt *pp = (struct checkpt*)shp->jmplist;
1N/A register struct subshell *sp = subshell_data->pipe;
1N/A /* save file descriptor 1 if open */
1N/A if((sp->tmpfd = fd = fcntl(1,F_DUPFD,10)) >= 0)
1N/A {
1N/A fcntl(fd,F_SETFD,FD_CLOEXEC);
1N/A VALIDATE_FD(shp, fd);
1N/A shp->fdstatus[fd] = shp->fdstatus[1]|IOCLEX;
1N/A close(1);
1N/A }
1N/A else if(errno!=EBADF)
1N/A errormsg(SH_DICT,ERROR_system(1),e_toomany);
1N/A /* popping a discipline forces a /tmp file create */
1N/A sfdisc(sfstdout,SF_POPDISC);
1N/A if((fd=sffileno(sfstdout))<0)
1N/A {
1N/A /* unable to create the /tmp file so use a pipe */
1N/A int fds[3];
1N/A Sfoff_t off;
1N/A fds[2] = 0;
1N/A sh_pipe(fds);
1N/A sp->pipefd = fds[0];
1N/A sh_fcntl(sp->pipefd,F_SETFD,FD_CLOEXEC);
1N/A /* write the data to the pipe */
1N/A if(off = sftell(sfstdout))
1N/A write(fds[1],sfsetbuf(sfstdout,(Void_t*)sfstdout,0),(size_t)off);
1N/A sfclose(sfstdout);
1N/A if((sh_fcntl(fds[1],F_DUPFD, 1)) != 1)
1N/A errormsg(SH_DICT,ERROR_system(1),e_file+4);
1N/A sh_close(fds[1]);
1N/A }
1N/A else
1N/A {
1N/A VALIDATE_FD(shp, fd);
1N/A shp->fdstatus[fd] = IOREAD|IOWRITE;
1N/A sfsync(sfstdout);
1N/A if(fd==1)
1N/A fcntl(1,F_SETFD,0);
1N/A else
1N/A {
1N/A sfsetfd(sfstdout,1);
1N/A shp->fdstatus[1] = shp->fdstatus[fd];
1N/A shp->fdstatus[fd] = IOCLOSE;
1N/A }
1N/A }
1N/A sh_iostream(shp,1);
1N/A sfset(sfstdout,SF_SHARE|SF_PUBLIC,1);
1N/A sfpool(sfstdout,shp->outpool,SF_WRITE);
1N/A if(pp && pp->olist && pp->olist->strm == sfstdout)
1N/A pp->olist->strm = 0;
1N/A }
1N/A}
1N/A
1N/A
1N/A/*
1N/A * This routine creates a temp file if necessary and creates a subshell.
1N/A * The parent routine longjmps back to sh_subshell()
1N/A * The child continues possibly with its standard output replaced by temp file
1N/A */
1N/Avoid sh_subfork(void)
1N/A{
1N/A register struct subshell *sp = subshell_data;
1N/A Shell_t *shp = sp->shp;
1N/A int curenv = shp->curenv;
1N/A pid_t pid;
1N/A char *trap = shp->st.trapcom[0];
1N/A if(trap)
1N/A trap = strdup(trap);
1N/A /* see whether inside $(...) */
1N/A if(sp->pipe)
1N/A sh_subtmpfile(shp);
1N/A shp->curenv = 0;
1N/A shp->savesig = -1;
1N/A if(pid = sh_fork(shp,FSHOWME,NIL(int*)))
1N/A {
1N/A shp->curenv = curenv;
1N/A /* this is the parent part of the fork */
1N/A if(sp->subpid==0)
1N/A sp->subpid = pid;
1N/A if(trap)
1N/A free((void*)trap);
1N/A siglongjmp(*shp->jmplist,SH_JMPSUB);
1N/A }
1N/A else
1N/A {
1N/A /* this is the child part of the fork */
1N/A /* setting subpid to 1 causes subshell to exit when reached */
1N/A sh_onstate(SH_FORKED);
1N/A sh_onstate(SH_NOLOG);
1N/A sh_offoption(SH_MONITOR);
1N/A sh_offstate(SH_MONITOR);
1N/A subshell_data = 0;
1N/A shp->subshell = 0;
1N/A shp->comsub = 0;
1N/A SH_SUBSHELLNOD->nvalue.s = 0;
1N/A sp->subpid=0;
1N/A shp->st.trapcom[0] = trap;
1N/A shp->savesig = 0;
1N/A }
1N/A}
1N/A
1N/Aint nv_subsaved(register Namval_t *np)
1N/A{
1N/A register struct subshell *sp;
1N/A register struct Link *lp;
1N/A for(sp = (struct subshell*)subshell_data; sp; sp=sp->prev)
1N/A {
1N/A for(lp=sp->svar; lp; lp = lp->next)
1N/A {
1N/A if(lp->node==np)
1N/A return(1);
1N/A }
1N/A }
1N/A return(0);
1N/A}
1N/A
1N/A/*
1N/A * This routine will make a copy of the given node in the
1N/A * layer created by the most recent subshell_fork if the
1N/A * node hasn't already been copied
1N/A */
1N/ANamval_t *sh_assignok(register Namval_t *np,int add)
1N/A{
1N/A register Namval_t *mp;
1N/A register struct Link *lp;
1N/A register struct subshell *sp = (struct subshell*)subshell_data;
1N/A Shell_t *shp = sp->shp;
1N/A Dt_t *dp= shp->var_tree;
1N/A Namval_t *mpnext;
1N/A Namarr_t *ap;
1N/A int save;
1N/A /* don't bother with this */
1N/A if(!sp->shpwd || np==SH_LEVELNOD || np==L_ARGNOD || np==SH_SUBSCRNOD || np==SH_NAMENOD)
1N/A return(np);
1N/A if((ap=nv_arrayptr(np)) && (mp=nv_opensub(np)))
1N/A {
1N/A shp->last_root = ap->table;
1N/A sh_assignok(mp,add);
1N/A if(!add || array_assoc(ap))
1N/A return(np);
1N/A }
1N/A for(lp=sp->svar; lp;lp = lp->next)
1N/A {
1N/A if(lp->node==np)
1N/A return(np);
1N/A }
1N/A /* first two pointers use linkage from np */
1N/A lp = (struct Link*)malloc(sizeof(*np)+2*sizeof(void*));
1N/A memset(lp,0, sizeof(*mp)+2*sizeof(void*));
1N/A lp->node = np;
1N/A if(!add && nv_isvtree(np))
1N/A {
1N/A Namval_t fake;
1N/A Dt_t *walk, *root=shp->var_tree;
1N/A char *name = nv_name(np);
1N/A int len = strlen(name);
1N/A fake.nvname = name;
1N/A mpnext = dtnext(root,&fake);
1N/A dp = root->walk?root->walk:root;
1N/A while(mp=mpnext)
1N/A {
1N/A walk = root->walk?root->walk:root;
1N/A mpnext = dtnext(root,mp);
1N/A if(memcmp(name,mp->nvname,len) || mp->nvname[len]!='.')
1N/A break;
1N/A nv_delete(mp,walk,NV_NOFREE);
1N/A *((Namval_t**)mp) = lp->child;
1N/A lp->child = mp;
1N/A
1N/A }
1N/A }
1N/A lp->dict = dp;
1N/A mp = (Namval_t*)&lp->dict;
1N/A lp->next = subshell_data->svar;
1N/A subshell_data->svar = lp;
1N/A save = shp->subshell;
1N/A shp->subshell = 0;
1N/A mp->nvname = np->nvname;
1N/A if(nv_isattr(np,NV_NOFREE))
1N/A nv_onattr(mp,NV_IDENT);
1N/A nv_clone(np,mp,(add?(nv_isnull(np)?0:NV_NOFREE)|NV_ARRAY:NV_MOVE));
1N/A shp->subshell = save;
1N/A return(np);
1N/A}
1N/A
1N/A/*
1N/A * restore the variables
1N/A */
1N/Astatic void nv_restore(struct subshell *sp)
1N/A{
1N/A register struct Link *lp, *lq;
1N/A register Namval_t *mp, *np;
1N/A const char *save = sp->shpwd;
1N/A Namval_t *mpnext;
1N/A int flags;
1N/A sp->shpwd = 0; /* make sure sh_assignok doesn't save with nv_unset() */
1N/A for(lp=sp->svar; lp; lp=lq)
1N/A {
1N/A np = (Namval_t*)&lp->dict;
1N/A lq = lp->next;
1N/A mp = lp->node;
1N/A if(!mp->nvname)
1N/A continue;
1N/A flags = 0;
1N/A if(nv_isattr(mp,NV_MINIMAL) && !nv_isattr(np,NV_EXPORT))
1N/A flags |= NV_MINIMAL;
1N/A if(nv_isarray(mp))
1N/A nv_putsub(mp,NIL(char*),ARRAY_SCAN);
1N/A _nv_unset(mp,NV_RDONLY|NV_CLONE);
1N/A if(nv_isarray(np))
1N/A {
1N/A nv_clone(np,mp,NV_MOVE);
1N/A goto skip;
1N/A }
1N/A nv_setsize(mp,nv_size(np));
1N/A if(!(flags&NV_MINIMAL))
1N/A mp->nvenv = np->nvenv;
1N/A mp->nvfun = np->nvfun;
1N/A if(nv_isattr(np,NV_IDENT))
1N/A {
1N/A nv_offattr(np,NV_IDENT);
1N/A flags |= NV_NOFREE;
1N/A }
1N/A mp->nvflag = np->nvflag|(flags&NV_MINIMAL);
1N/A if(nv_cover(mp))
1N/A {
1N/A nv_putval(mp, nv_getval(np),np->nvflag|NV_NOFREE);
1N/A if(!nv_isattr(np,NV_NOFREE))
1N/A nv_offattr(mp,NV_NOFREE);
1N/A }
1N/A else
1N/A mp->nvalue.cp = np->nvalue.cp;
1N/A np->nvfun = 0;
1N/A if(nv_isattr(mp,NV_EXPORT))
1N/A {
1N/A char *name = nv_name(mp);
1N/A sh_envput(sp->shp->env,mp);
1N/A if(*name=='_' && strcmp(name,"_AST_FEATURES")==0)
1N/A astconf(NiL, NiL, NiL);
1N/A }
1N/A else if(nv_isattr(np,NV_EXPORT))
1N/A env_delete(sp->shp->env,nv_name(mp));
1N/A nv_onattr(mp,flags);
1N/A skip:
1N/A for(mp=lp->child; mp; mp=mpnext)
1N/A {
1N/A mpnext = *((Namval_t**)mp);
1N/A dtinsert(lp->dict,mp);
1N/A }
1N/A free((void*)lp);
1N/A sp->svar = lq;
1N/A }
1N/A sp->shpwd=save;
1N/A}
1N/A
1N/A/*
1N/A * return pointer to alias tree
1N/A * create new one if in a subshell and one doesn't exist and create is non-zero
1N/A */
1N/ADt_t *sh_subaliastree(int create)
1N/A{
1N/A register struct subshell *sp = subshell_data;
1N/A if(!sp || sp->shp->curenv==0)
1N/A return(sh.alias_tree);
1N/A if(!sp->salias && create)
1N/A {
1N/A sp->salias = dtopen(&_Nvdisc,Dtoset);
1N/A dtview(sp->salias,sp->shp->alias_tree);
1N/A sp->shp->alias_tree = sp->salias;
1N/A }
1N/A return(sp->salias);
1N/A}
1N/A
1N/A/*
1N/A * return pointer to function tree
1N/A * create new one if in a subshell and one doesn't exist and create is non-zero
1N/A */
1N/ADt_t *sh_subfuntree(int create)
1N/A{
1N/A register struct subshell *sp = subshell_data;
1N/A if(!sp || sp->shp->curenv==0)
1N/A return(sh.fun_tree);
1N/A if(!sp->sfun && create)
1N/A {
1N/A sp->sfun = dtopen(&_Nvdisc,Dtoset);
1N/A dtview(sp->sfun,sp->shp->fun_tree);
1N/A sp->shp->fun_tree = sp->sfun;
1N/A }
1N/A return(sp->shp->fun_tree);
1N/A}
1N/A
1N/Astatic void table_unset(register Dt_t *root,int fun)
1N/A{
1N/A register Namval_t *np,*nq;
1N/A int flag;
1N/A for(np=(Namval_t*)dtfirst(root);np;np=nq)
1N/A {
1N/A nq = (Namval_t*)dtnext(root,np);
1N/A flag=0;
1N/A if(fun && np->nvalue.rp && np->nvalue.rp->fname && *np->nvalue.rp->fname=='/')
1N/A {
1N/A np->nvalue.rp->fdict = 0;
1N/A flag = NV_NOFREE;
1N/A }
1N/A else
1N/A _nv_unset(np,NV_RDONLY);
1N/A nv_delete(np,root,flag|NV_FUNCTION);
1N/A }
1N/A}
1N/A
1N/Aint sh_subsavefd(register int fd)
1N/A{
1N/A register struct subshell *sp = subshell_data;
1N/A register int old=0;
1N/A if(sp)
1N/A {
1N/A old = !(sp->fdsaved&(1<<(fd-1)));
1N/A sp->fdsaved |= (1<<(fd-1));
1N/A }
1N/A return(old);
1N/A}
1N/A
1N/Avoid sh_subjobcheck(pid_t pid)
1N/A{
1N/A register struct subshell *sp = subshell_data;
1N/A while(sp)
1N/A {
1N/A if(sp->cpid==pid)
1N/A {
1N/A sh_close(sp->coutpipe);
1N/A sh_close(sp->cpipe);
1N/A sp->coutpipe = sp->cpipe = -1;
1N/A return;
1N/A }
1N/A sp = sp->prev;
1N/A }
1N/A}
1N/A
1N/A/*
1N/A * Run command tree <t> in a virtual sub-shell
1N/A * If comsub is not null, then output will be placed in temp file (or buffer)
1N/A * If comsub is not null, the return value will be a stream consisting of
1N/A * output of command <t>. Otherwise, NULL will be returned.
1N/A */
1N/A
1N/ASfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, int flags, int comsub)
1N/A{
1N/A struct subshell sub_data;
1N/A register struct subshell *sp = &sub_data;
1N/A int jmpval,nsig=0,duped=0;
1N/A int savecurenv = shp->curenv;
1N/A int savejobpgid = job.curpgid;
1N/A int16_t subshell;
1N/A char *savsig;
1N/A Sfio_t *iop=0;
1N/A struct checkpt buff;
1N/A struct sh_scoped savst;
1N/A struct dolnod *argsav=0;
1N/A int argcnt;
1N/A memset((char*)sp, 0, sizeof(*sp));
1N/A sfsync(shp->outpool);
1N/A sh_sigcheck(shp);
1N/A shp->savesig = -1;
1N/A if(argsav = sh_arguse(shp))
1N/A argcnt = argsav->dolrefcnt;
1N/A if(shp->curenv==0)
1N/A {
1N/A subshell_data=0;
1N/A subenv = 0;
1N/A }
1N/A shp->curenv = ++subenv;
1N/A savst = shp->st;
1N/A sh_pushcontext(shp,&buff,SH_JMPSUB);
1N/A subshell = shp->subshell+1;
1N/A SH_SUBSHELLNOD->nvalue.s = subshell;
1N/A shp->subshell = subshell;
1N/A sp->prev = subshell_data;
1N/A sp->shp = shp;
1N/A sp->sig = 0;
1N/A subshell_data = sp;
1N/A sp->errcontext = &buff.err;
1N/A sp->var = shp->var_tree;
1N/A sp->options = shp->options;
1N/A sp->jobs = job_subsave();
1N/A sp->subdup = shp->subdup;
1N/A#if SHOPT_COSHELL
1N/A sp->coshell = shp->coshell;
1N/A shp->coshell = 0;
1N/A#endif /* SHOPT_COSHELL */
1N/A /* make sure initialization has occurred */
1N/A if(!shp->pathlist)
1N/A path_get(shp,".");
1N/A sp->pathlist = path_dup((Pathcomp_t*)shp->pathlist);
1N/A if(!shp->pwd)
1N/A path_pwd(shp,0);
1N/A sp->bckpid = shp->bckpid;
1N/A if(comsub)
1N/A sh_stats(STAT_COMSUB);
1N/A else
1N/A job.curpgid = 0;
1N/A sp->subshare = shp->subshare;
1N/A sp->comsub = shp->comsub;
1N/A shp->subshare = comsub==2 || (comsub==1 && sh_isoption(SH_SUBSHARE));
1N/A if(comsub)
1N/A shp->comsub = comsub;
1N/A if(!comsub || !shp->subshare)
1N/A {
1N/A sp->shpwd = shp->pwd;
1N/A sp->pwd = (shp->pwd?strdup(shp->pwd):0);
1N/A sp->mask = shp->mask;
1N/A sh_stats(STAT_SUBSHELL);
1N/A /* save trap table */
1N/A shp->st.otrapcom = 0;
1N/A if((nsig=shp->st.trapmax*sizeof(char*))>0 || shp->st.trapcom[0])
1N/A {
1N/A nsig += sizeof(char*);
1N/A memcpy(savsig=malloc(nsig),(char*)&shp->st.trapcom[0],nsig);
1N/A /* this nonsense needed for $(trap) */
1N/A shp->st.otrapcom = (char**)savsig;
1N/A }
1N/A sp->cpid = shp->cpid;
1N/A sp->coutpipe = shp->coutpipe;
1N/A sp->cpipe = shp->cpipe[1];
1N/A shp->cpid = 0;
1N/A sh_sigreset(0);
1N/A }
1N/A jmpval = sigsetjmp(buff.buff,0);
1N/A if(jmpval==0)
1N/A {
1N/A if(comsub)
1N/A {
1N/A /* disable job control */
1N/A shp->spid = 0;
1N/A sp->jobcontrol = job.jobcontrol;
1N/A sp->monitor = (sh_isstate(SH_MONITOR)!=0);
1N/A job.jobcontrol=0;
1N/A sh_offstate(SH_MONITOR);
1N/A sp->pipe = sp;
1N/A /* save sfstdout and status */
1N/A sp->saveout = sfswap(sfstdout,NIL(Sfio_t*));
1N/A sp->fdstatus = shp->fdstatus[1];
1N/A sp->tmpfd = -1;
1N/A sp->pipefd = -1;
1N/A /* use sftmp() file for standard output */
1N/A if(!(iop = sftmp(PIPE_BUF)))
1N/A {
1N/A sfswap(sp->saveout,sfstdout);
1N/A errormsg(SH_DICT,ERROR_system(1),e_tmpcreate);
1N/A }
1N/A sfswap(iop,sfstdout);
1N/A sfset(sfstdout,SF_READ,0);
1N/A shp->fdstatus[1] = IOWRITE;
1N/A if(!(sp->nofork = sh_state(SH_NOFORK)))
1N/A sh_onstate(SH_NOFORK);
1N/A flags |= sh_state(SH_NOFORK);
1N/A }
1N/A else if(sp->prev)
1N/A {
1N/A sp->pipe = sp->prev->pipe;
1N/A flags &= ~sh_state(SH_NOFORK);
1N/A }
1N/A if(shp->savesig < 0)
1N/A {
1N/A shp->savesig = 0;
1N/A sh_exec(t,flags);
1N/A }
1N/A }
1N/A if(comsub!=2 && jmpval!=SH_JMPSUB && shp->st.trapcom[0] && shp->subshell)
1N/A {
1N/A /* trap on EXIT not handled by child */
1N/A char *trap=shp->st.trapcom[0];
1N/A shp->st.trapcom[0] = 0; /* prevent recursion */
1N/A shp->oldexit = shp->exitval;
1N/A sh_trap(trap,0);
1N/A free(trap);
1N/A }
1N/A sh_popcontext(shp,&buff);
1N/A if(shp->subshell==0) /* must be child process */
1N/A {
1N/A subshell_data = sp->prev;
1N/A if(jmpval==SH_JMPSCRIPT)
1N/A siglongjmp(*shp->jmplist,jmpval);
1N/A shp->exitval &= SH_EXITMASK;
1N/A sh_done(shp,0);
1N/A }
1N/A if(!shp->savesig)
1N/A shp->savesig = -1;
1N/A if(comsub)
1N/A {
1N/A /* re-enable job control */
1N/A if(!sp->nofork)
1N/A sh_offstate(SH_NOFORK);
1N/A job.jobcontrol = sp->jobcontrol;
1N/A if(sp->monitor)
1N/A sh_onstate(SH_MONITOR);
1N/A if(sp->pipefd>=0)
1N/A {
1N/A /* sftmp() file has been returned into pipe */
1N/A iop = sh_iostream(shp,sp->pipefd);
1N/A sfclose(sfstdout);
1N/A }
1N/A else
1N/A {
1N/A /* move tmp file to iop and restore sfstdout */
1N/A iop = sfswap(sfstdout,NIL(Sfio_t*));
1N/A if(!iop)
1N/A {
1N/A /* maybe locked try again */
1N/A sfclrlock(sfstdout);
1N/A iop = sfswap(sfstdout,NIL(Sfio_t*));
1N/A }
1N/A if(iop && sffileno(iop)==1)
1N/A {
1N/A int fd=sfsetfd(iop,3);
1N/A if(fd<0)
1N/A {
1N/A shp->toomany = 1;
1N/A ((struct checkpt*)shp->jmplist)->mode = SH_JMPERREXIT;
1N/A errormsg(SH_DICT,ERROR_system(1),e_toomany);
1N/A }
1N/A VALIDATE_FD(shp, fd);
1N/A shp->sftable[fd] = iop;
1N/A fcntl(fd,F_SETFD,FD_CLOEXEC);
1N/A shp->fdstatus[fd] = (shp->fdstatus[1]|IOCLEX);
1N/A shp->fdstatus[1] = IOCLOSE;
1N/A }
1N/A sfset(iop,SF_READ,1);
1N/A }
1N/A sfswap(sp->saveout,sfstdout);
1N/A /* check if standard output was preserved */
1N/A if(sp->tmpfd>=0)
1N/A {
1N/A close(1);
1N/A if (fcntl(sp->tmpfd,F_DUPFD,1) != 1)
1N/A duped++;
1N/A sh_close(sp->tmpfd);
1N/A }
1N/A shp->fdstatus[1] = sp->fdstatus;
1N/A }
1N/A path_delete((Pathcomp_t*)shp->pathlist);
1N/A shp->pathlist = (void*)sp->pathlist;
1N/A job_subrestore(sp->jobs);
1N/A shp->jobenv = savecurenv;
1N/A job.curpgid = savejobpgid;
1N/A shp->bckpid = sp->bckpid;
1N/A if(sp->shpwd) /* restore environment if saved */
1N/A {
1N/A int n;
1N/A shp->options = sp->options;
1N/A nv_restore(sp);
1N/A if(sp->salias)
1N/A {
1N/A shp->alias_tree = dtview(sp->salias,0);
1N/A table_unset(sp->salias,0);
1N/A dtclose(sp->salias);
1N/A }
1N/A if(sp->sfun)
1N/A {
1N/A shp->fun_tree = dtview(sp->sfun,0);
1N/A table_unset(sp->sfun,1);
1N/A dtclose(sp->sfun);
1N/A }
1N/A n = shp->st.trapmax-savst.trapmax;
1N/A sh_sigreset(1);
1N/A if(n>0)
1N/A memset(&shp->st.trapcom[savst.trapmax],0,n*sizeof(char*));
1N/A shp->st = savst;
1N/A shp->curenv = savecurenv;
1N/A if(nsig)
1N/A {
1N/A memcpy((char*)&shp->st.trapcom[0],savsig,nsig);
1N/A free((void*)savsig);
1N/A }
1N/A shp->options = sp->options;
1N/A if(!shp->pwd || strcmp(sp->pwd,shp->pwd))
1N/A {
1N/A /* restore PWDNOD */
1N/A Namval_t *pwdnod = sh_scoped(shp,PWDNOD);
1N/A if(shp->pwd)
1N/A {
1N/A chdir(shp->pwd=sp->pwd);
1N/A path_newdir(shp,shp->pathlist);
1N/A }
1N/A if(nv_isattr(pwdnod,NV_NOFREE))
1N/A pwdnod->nvalue.cp = (const char*)sp->pwd;
1N/A }
1N/A else if(sp->shpwd != shp->pwd)
1N/A {
1N/A shp->pwd = sp->pwd;
1N/A if(PWDNOD->nvalue.cp==sp->shpwd)
1N/A PWDNOD->nvalue.cp = sp->pwd;
1N/A }
1N/A else
1N/A free((void*)sp->pwd);
1N/A if(sp->mask!=shp->mask)
1N/A umask(shp->mask=sp->mask);
1N/A if(shp->coutpipe!=sp->coutpipe)
1N/A {
1N/A sh_close(shp->coutpipe);
1N/A sh_close(shp->cpipe[1]);
1N/A }
1N/A shp->cpid = sp->cpid;
1N/A shp->cpipe[1] = sp->cpipe;
1N/A shp->coutpipe = sp->coutpipe;
1N/A }
1N/A shp->subshare = sp->subshare;
1N/A shp->comsub = sp->comsub;
1N/A shp->subdup = sp->subdup;
1N/A#if SHOPT_COSHELL
1N/A shp->coshell = sp->coshell;
1N/A#endif /* SHOPT_COSHELL */
1N/A if(shp->subshell)
1N/A SH_SUBSHELLNOD->nvalue.s = --shp->subshell;
1N/A subshell = shp->subshell;
1N/A subshell_data = sp->prev;
1N/A if(!argsav || argsav->dolrefcnt==argcnt)
1N/A sh_argfree(shp,argsav,0);
1N/A if(shp->topfd != buff.topfd)
1N/A sh_iorestore(shp,buff.topfd|IOSUBSHELL,jmpval);
1N/A if(sp->sig)
1N/A {
1N/A if(sp->prev)
1N/A sp->prev->sig = sp->sig;
1N/A else
1N/A {
1N/A sh_fault(sp->sig);
1N/A sh_chktrap(shp);
1N/A }
1N/A }
1N/A sh_sigcheck(shp);
1N/A shp->trapnote = 0;
1N/A nsig = shp->savesig;
1N/A shp->savesig = 0;
1N/A if(nsig>0)
1N/A sh_fault(nsig);
1N/A if(sp->subpid)
1N/A job_wait(sp->subpid);
1N/A if(comsub && iop && sp->pipefd<0)
1N/A sfseek(iop,(off_t)0,SEEK_SET);
1N/A if(shp->trapnote)
1N/A sh_chktrap(shp);
1N/A if(shp->exitval > SH_EXITSIG)
1N/A {
1N/A int sig = shp->exitval&SH_EXITMASK;
1N/A if(sig==SIGINT || sig== SIGQUIT)
1N/A sh_fault(sig);
1N/A }
1N/A if(duped)
1N/A {
1N/A ((struct checkpt*)shp->jmplist)->mode = SH_JMPERREXIT;
1N/A shp->toomany = 1;
1N/A errormsg(SH_DICT,ERROR_system(1),e_redirect);
1N/A }
1N/A if(shp->ignsig)
1N/A sh_fault(shp->ignsig);
1N/A if(jmpval==SH_JMPSUB && shp->lastsig)
1N/A sh_fault(shp->lastsig);
1N/A if(jmpval && shp->toomany)
1N/A siglongjmp(*shp->jmplist,jmpval);
1N/A return(iop);
1N/A}