Example #1
0
/* execute tree in child subprocess */
int
exchild(struct op *t, int flags, volatile int *xerrok,
    int close_fd)	/* used if XPCLOSE or XCCLOSE */
{
	static Proc	*last_proc;	/* for pipelines */

	int		i;
	sigset_t	omask;
	Proc		*p;
	Job		*j;
	int		rv = 0;
	int		forksleep;
	int		ischild;

	if (flags & XEXEC)
		/* Clear XFORK|XPCLOSE|XCCLOSE|XCOPROC|XPIPEO|XPIPEI|XXCOM|XBGND
		 * (also done in another execute() below)
		 */
		return execute(t, flags & (XEXEC | XERROK), xerrok);

	/* no SIGCHLD's while messing with job and process lists */
	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);

	p = new_proc();
	p->next = (Proc *) 0;
	p->state = PRUNNING;
	p->status = 0;
	p->pid = 0;

	/* link process into jobs list */
	if (flags&XPIPEI) {	/* continuing with a pipe */
		if (!last_job)
			internal_errorf(1,
			    "exchild: XPIPEI and no last_job - pid %d",
			    (int) procpid);
		j = last_job;
		last_proc->next = p;
		last_proc = p;
	} else {
		j = new_job(); /* fills in j->job */
		/* we don't consider XXCOM's foreground since they don't get
		 * tty process group and we don't save or restore tty modes.
		 */
		j->flags = (flags & XXCOM) ? JF_XXCOM :
		    ((flags & XBGND) ? 0 : (JF_FG|JF_USETTYMODE));
		timerclear(&j->usrtime);
		timerclear(&j->systime);
		j->state = PRUNNING;
		j->pgrp = 0;
		j->ppid = procpid;
		j->age = ++njobs;
		j->proc_list = p;
		j->coproc_id = 0;
		last_job = j;
		last_proc = p;
		put_job(j, PJ_PAST_STOPPED);
	}

	snptreef(p->command, sizeof(p->command), "%T", t);

	/* create child process */
	forksleep = 1;
	while ((i = fork()) < 0 && errno == EAGAIN && forksleep < 32) {
		if (intrsig)	 /* allow user to ^C out... */
			break;
		sleep(forksleep);
		forksleep <<= 1;
	}
	if (i < 0) {
		kill_job(j, SIGKILL);
		remove_job(j, "fork failed");
		sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
		errorf("cannot fork - try again");
	}
	ischild = i == 0;
	if (ischild)
		p->pid = procpid = getpid();
	else
		p->pid = i;

#ifdef JOBS
	/* job control set up */
	if (Flag(FMONITOR) && !(flags&XXCOM)) {
		int	dotty = 0;
		if (j->pgrp == 0) {	/* First process */
			j->pgrp = p->pid;
			dotty = 1;
		}

		/* set pgrp in both parent and child to deal with race
		 * condition
		 */
		setpgid(p->pid, j->pgrp);
		/* YYY: should this be
		   if (ttypgrp_ok && ischild && !(flags&XBGND))
			tcsetpgrp(tty_fd, j->pgrp);
		   instead? (see also YYY below)
		 */
		if (ttypgrp_ok && dotty && !(flags & XBGND))
			tcsetpgrp(tty_fd, j->pgrp);
	}
#endif /* JOBS */

	/* used to close pipe input fd */
	if (close_fd >= 0 && (((flags & XPCLOSE) && !ischild) ||
	    ((flags & XCCLOSE) && ischild)))
		close(close_fd);
	if (ischild) {		/* child */
		/* Do this before restoring signal */
		if (flags & XCOPROC)
			coproc_cleanup(false);
		sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
		cleanup_parents_env();
#ifdef JOBS
		/* If FMONITOR or FTALKING is set, these signals are ignored,
		 * if neither FMONITOR nor FTALKING are set, the signals have
		 * their inherited values.
		 */
		if (Flag(FMONITOR) && !(flags & XXCOM)) {
			for (i = NELEM(tt_sigs); --i >= 0; )
				setsig(&sigtraps[tt_sigs[i]], SIG_DFL,
				    SS_RESTORE_DFL|SS_FORCE);
		}
#endif /* JOBS */
		if (Flag(FBGNICE) && (flags & XBGND))
			nice(4);
		if ((flags & XBGND) && !Flag(FMONITOR)) {
			setsig(&sigtraps[SIGINT], SIG_IGN,
			    SS_RESTORE_IGN|SS_FORCE);
			setsig(&sigtraps[SIGQUIT], SIG_IGN,
			    SS_RESTORE_IGN|SS_FORCE);
			if (!(flags & (XPIPEI | XCOPROC))) {
				int fd = open("/dev/null", 0);
				if (fd != 0) {
					(void) ksh_dup2(fd, 0, true);
					close(fd);
				}
			}
		}
		remove_job(j, "child");	/* in case of `jobs` command */
		nzombie = 0;
#ifdef JOBS
		ttypgrp_ok = 0;
		Flag(FMONITOR) = 0;
#endif /* JOBS */
		Flag(FTALKING) = 0;
		tty_close();
		cleartraps();
		execute(t, (flags & XERROK) | XEXEC, NULL); /* no return */
		internal_errorf(0, "exchild: execute() returned");
		unwind(LLEAVE);
		/* NOTREACHED */
	}

	/* shell (parent) stuff */
	/* Ensure next child gets a (slightly) different $RANDOM sequence */
	change_random();
	if (!(flags & XPIPEO)) {	/* last process in a job */
#ifdef JOBS
		/* YYY: Is this needed? (see also YYY above)
		   if (Flag(FMONITOR) && !(flags&(XXCOM|XBGND)))
			tcsetpgrp(tty_fd, j->pgrp);
		*/
#endif /* JOBS */
		j_startjob(j);
		if (flags & XCOPROC) {
			j->coproc_id = coproc.id;
			coproc.njobs++; /* n jobs using co-process output */
			coproc.job = (void *) j; /* j using co-process input */
		}
		if (flags & XBGND) {
			j_set_async(j);
			if (Flag(FTALKING)) {
				shf_fprintf(shl_out, "[%d]", j->job);
				for (p = j->proc_list; p; p = p->next)
					shf_fprintf(shl_out, " %d", p->pid);
				shf_putchar('\n', shl_out);
				shf_flush(shl_out);
			}
		} else
			rv = j_waitj(j, JW_NONE, "jw:last proc");
	}

	sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);

	return rv;
}
/*
 * set up redirection, saving old fds in e->savefd
 */
static int
iosetup(struct ioword *iop, struct tbl *tp)
{
	int u = -1;
	char *cp = iop->name;
	int iotype = iop->flag & IOTYPE;
	bool do_open = true, do_close = false;
	int flags = 0;
	struct ioword iotmp;
	struct stat statb;

	if (iotype != IOHERE)
		cp = evalonestr(cp, DOTILDE|(Flag(FTALKING_I) ? DOGLOB : 0));

	/* Used for tracing and error messages to print expanded cp */
	iotmp = *iop;
	iotmp.name = (iotype == IOHERE) ? NULL : cp;
	iotmp.flag |= IONAMEXP;

	if (Flag(FXTRACE))
		shellf("%s%s\n",
		    substitute(str_val(global("PS4")), 0),
		    snptreef(NULL, 32, "%R", &iotmp));

	switch (iotype) {
	case IOREAD:
		flags = O_RDONLY;
		break;

	case IOCAT:
		flags = O_WRONLY | O_APPEND | O_CREAT;
		break;

	case IOWRITE:
		flags = O_WRONLY | O_CREAT | O_TRUNC;
		/*
		 * The stat() is here to allow redirections to
		 * things like /dev/null without error.
		 */
		if (Flag(FNOCLOBBER) && !(iop->flag & IOCLOB) &&
		    (stat(cp, &statb) < 0 || S_ISREG(statb.st_mode)))
			flags |= O_EXCL;
		break;

	case IORDWR:
		flags = O_RDWR | O_CREAT;
		break;

	case IOHERE:
		do_open = false;
		/* herein() returns -2 if error has been printed */
		u = herein(iop, NULL);
		/* cp may have wrong name */
		break;

	case IODUP: {
		const char *emsg;

		do_open = false;
		if (*cp == '-' && !cp[1]) {
			/* prevent error return below */
			u = 1009;
			do_close = true;
		} else if ((u = check_fd(cp,
		    X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK),
		    &emsg)) < 0) {
			warningf(true, "%s: %s",
			    snptreef(NULL, 32, "%R", &iotmp), emsg);
			return (-1);
		}
		if (u == iop->unit)
			/* "dup from" == "dup to" */
			return (0);
		break;
	    }
	}

	if (do_open) {
		if (Flag(FRESTRICTED) && (flags & O_CREAT)) {
			warningf(true, "%s: %s", cp, "restricted");
			return (-1);
		}
		u = open(cp, flags, 0666);
	}
	if (u < 0) {
		/* herein() may already have printed message */
		if (u == -1) {
			u = errno;
			warningf(true, "can't %s %s: %s",
			    iotype == IODUP ? "dup" :
			    (iotype == IOREAD || iotype == IOHERE) ?
			    "open" : "create", cp, cstrerror(u));
		}
		return (-1);
	}
	/* Do not save if it has already been redirected (i.e. "cat >x >y"). */
	if (e->savefd[iop->unit] == 0) {
		/* If these are the same, it means unit was previously closed */
		if (u == iop->unit)
			e->savefd[iop->unit] = -1;
		else
			/*
			 * c_exec() assumes e->savefd[fd] set for any
			 * redirections. Ask savefd() not to close iop->unit;
			 * this allows error messages to be seen if iop->unit
			 * is 2; also means we can't lose the fd (eg, both
			 * dup2 below and dup2 in restfd() failing).
			 */
			e->savefd[iop->unit] = savefd(iop->unit);
	}

	if (do_close)
		close(iop->unit);
	else if (u != iop->unit) {
		if (ksh_dup2(u, iop->unit, true) < 0) {
			int eno;

			eno = errno;
			warningf(true, "%s %s %s",
			    "can't finish (dup) redirection",
			    snptreef(NULL, 32, "%R", &iotmp),
			    cstrerror(eno));
			if (iotype != IODUP)
				close(u);
			return (-1);
		}
		if (iotype != IODUP)
			close(u);
		/*
		 * Touching any co-process fd in an empty exec
		 * causes the shell to close its copies
		 */
		else if (tp && tp->type == CSHELL && tp->val.f == c_exec) {
			if (iop->flag & IORDUP)
				/* possible exec <&p */
				coproc_read_close(u);
			else
				/* possible exec >&p */
				coproc_write_close(u);
		}
	}
	if (u == 2)
		/* Clear any write errors */
		shf_reopen(2, SHF_WR, shl_out);
	return (0);
}
Example #3
0
File: jobs.c Project: adtools/abcsh
int
exchild(struct op *t, int flags,
        int close_fd)       /* used if XPCLOSE or XCCLOSE */
{
        static Proc     *last_proc;     /* for pipelines */

        int             i;
        sigset_t        omask;
        Proc            *p;
        Job             *j;
        int             rv = 0;
        int             forksleep;
        int             ischild;

        if (flags & XEXEC)
                /* Clear XFORK|XPCLOSE|XCCLOSE|XCOPROC|XPIPEO|XPIPEI|XXCOM|XBGND
                 * (also done in another execute() below)
                 */
                return execute(t, flags & (XEXEC | XERROK));

        p = new_proc();
        p->next = (Proc *) 0;
        p->state = PRUNNING;
        p->status = 0;
        p->pid = 0;

        /* link process into jobs list */
        if (flags&XPIPEI) {     /* continuing with a pipe */
                if (!last_job)
                        internal_errorf(1,
                                "exchild: XPIPEI and no last_job - pid %d",
                                (int) procpid);
                j = last_job;
                last_proc->next = p;
                last_proc = p;
        } else {
                j = new_job(); /* fills in j->job */
                /* we don't consider XXCOM's foreground since they don't get
                 * tty process group and we don't save or restore tty modes.
                 */
                j->flags = (flags & XXCOM) ? JF_XXCOM :
                        ((flags & XBGND) ? 0 : (JF_FG|JF_USETTYMODE));
                j->usrtime = j->systime = 0;
                j->state = PRUNNING;
                j->pgrp = 0;
                j->ppid = procpid;
                j->age = ++njobs;
                j->proc_list = p;
                j->coproc_id = 0;
                last_job = j;
                last_proc = p;
                put_job(j, PJ_PAST_STOPPED);
        }

        snptreef(p->command, sizeof(p->command), "%T", t);

        /* create child process */
        forksleep = 1;
        while ((i = fork()) < 0 && errno == EAGAIN && forksleep < 32) {
                if (intrsig)     /* allow user to ^C out... */
                        break;
                sleep(forksleep);
                forksleep <<= 1;
        }
        if (i < 0) {
                kill_job(j, SIGKILL);
                remove_job(j, "fork failed");
                sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
                errorf("cannot fork - try again");
        }
        ischild = i == 0;
        if (ischild)
                p->pid = procpid = getpid();
        else
                p->pid = i;

        /* used to close pipe input fd */
        if (close_fd >= 0 && (((flags & XPCLOSE) && !ischild) ||
                ((flags & XCCLOSE) && ischild)))
                        close(close_fd);
        if (ischild) {          /* child */
                /* Do this before restoring signal */
                if (flags & XCOPROC)
                        coproc_cleanup(false);
                sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
		cleanup_parents_env();
                if ((flags & XBGND)) {
                        setsig(&sigtraps[SIGINT], SIG_IGN,
                                SS_RESTORE_IGN|SS_FORCE);
                        setsig(&sigtraps[SIGQUIT], SIG_IGN,
                                SS_RESTORE_IGN|SS_FORCE);
                        if (!(flags & (XPIPEI | XCOPROC))) {
                                int fd = open("/dev/null", 0);
                                if (fd != 0) {
				        (void) ksh_dup2(fd, 0, true);
                                        close(fd);
                                }

                        }
                }
                remove_job(j, "child"); /* in case of `jobs` command */
                nzombie = 0;
                Flag(FTALKING) = 0;
                tty_close();
                cleartraps();
                execute(t, (flags & XERROK) | XEXEC); /* no return */
                internal_errorf(0, "exchild: execute() returned");
                unwind(LLEAVE);
                /* NOTREACHED */
        }

        /* shell (parent) stuff */
        /* Ensure next child gets a (slightly) different $RANDOM sequence */
        change_random();
        if (!(flags & XPIPEO)) {        /* last process in a job */
                j_startjob(j);
                if (flags & XCOPROC) {
                        j->coproc_id = coproc.id;
                        coproc.njobs++; /* n jobs using co-process output */
                        coproc.job = (void *) j; /* j using co-process input */
                }
                if (flags & XBGND) {
                        j_set_async(j);
                        if (Flag(FTALKING)) {
                                shf_fprintf(shl_out, "[%d]", j->job);
                                for (p = j->proc_list; p; p = p->next)
                                        shf_fprintf(shl_out, " %d", p->pid);
                                shf_putchar('\n', shl_out);
                                shf_flush(shl_out);
                        }
                } else
                        rv = j_waitj(j, JW_NONE, "jw:last proc");
        }

        sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);

        return rv;
}