/* 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; }
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; }