/* wait till none of the processes in the stack starting at k is live */ int waitstack(int k) { int npending, status, totstatus; int i; totstatus = 0; npending = 0; for(i=k ; i<nproc; ++i) if(! procstack[i].done) ++npending; enbint(SIG_IGN); if(dbgflag > 1) printf("waitstack(%d)\n", k); while(npending>0 && proclive>0) { if(waitproc(&status) >= k) --npending; totstatus |= status; } if(nproc > k) nproc = k; enbint(intrupt); return totstatus; }
/* * Check to see if any children have exited, and if so, read any unread * input and then remove the child from the list of children. */ static void reap(int dummy) { CHILD *pc; int save_errno = errno; int status = 0; pid_t pid; debugmsg(DM_CALL, "reap() called\n"); /* * Reap every child that has exited. Break out of the * loop as soon as we run out of children that have * exited so far. */ for ( ; ; ) { /* * Do a non-blocking check for exiting processes */ pid = waitproc(&status, FALSE); debugmsg(DM_MISC, "reap() pid = %d status = %d activechildren=%d\n", pid, status, activechildren); /* * See if a child really exited */ if (pid == 0) break; if (pid < 0) { if (errno != ECHILD) error("Wait failed: %s", SYSERR); break; } /* * Find the process (pid) and mark it as dead. */ for (pc = childlist; pc; pc = pc->c_next) if (pc->c_pid == pid) { needscan = TRUE; pc->c_state = PSdead; } } /* * Reset signals */ (void) signal(SIGCHLD, reap); debugmsg(DM_CALL, "reap() done\n"); errno = save_errno; }
int dosys(char *comstring, int nohalt, int nowait, char *prefix) { int status; struct process *procp; /* make sure there is room in the process stack */ if(nproc >= MAXPROC) waitstack(MAXPROC-1); /* make sure fewer than proclimit processes are running */ while(proclive >= proclimit) { enbint(SIG_IGN); waitproc(&status); enbint(intrupt); } if(prefix) { fputs(prefix, stdout); fputs(comstring, stdout); } procp = procstack + nproc; procp->pid = (forceshell || metas(comstring) ) ? doshell(comstring,nohalt) : doexec(comstring); if(procp->pid == -1) fatal("fork failed"); procstack[nproc].nohalt = nohalt; procstack[nproc].nowait = nowait; procstack[nproc].done = NO; ++proclive; ++nproc; if(nowait) { printf(" &%d\n", procp->pid); fflush(stdout); return 0; } if(prefix) { putchar('\n'); fflush(stdout); } return waitstack(nproc-1); }
STATIC int dowait(int flags, struct job *job) { int pid; int status; struct procstat *sp; struct job *jp; struct job *thisjob; int done; int stopped; extern volatile char gotsig[]; TRACE(("dowait(%x) called\n", flags)); do { pid = waitproc(flags & WBLOCK, job, &status); TRACE(("wait returns pid %d, status %d\n", pid, status)); } while (pid == -1 && errno == EINTR && gotsig[SIGINT - 1] == 0); if (pid <= 0) return pid; INTOFF; thisjob = NULL; for (jp = jobtab ; jp < jobtab + njobs ; jp++) { if (jp->used) { done = 1; stopped = 1; for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { if (sp->pid == -1) continue; if (sp->pid == pid) { TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jp - jobtab + 1, pid, sp->status, status)); sp->status = status; thisjob = jp; } if (sp->status == -1) stopped = 0; else if (WIFSTOPPED(sp->status)) done = 0; } if (stopped) { /* stopped or done */ int state = done ? JOBDONE : JOBSTOPPED; if (jp->state != state) { TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state)); jp->state = state; #if JOBS if (done) set_curjob(jp, 0); #endif } } } } if (thisjob && thisjob->state != JOBRUNNING) { int mode = 0; if (!rootshell || !iflag) mode = SHOW_SIGNALLED; if ((job == thisjob && (flags & WNOFREE) == 0) || (job != thisjob && (flags & WNOFREE) != 0)) mode = SHOW_SIGNALLED | SHOW_NO_FREE; if (mode) showjob(out2, thisjob, mode); else { TRACE(("Not printing status, rootshell=%d, job=%p\n", rootshell, job)); thisjob->changed = 1; } } INTON; return pid; }
/* #if defined HAVE_SELECT * * Wait for children to send output for us to read. * #else !HAVE_SELECT * * Wait up for children to exit. * #endif */ void waitup(void) { #if defined(HAVE_SELECT) int count; CHILD *pc; fd_set *rchildfdsp = NULL; int rchildfdsn = 0; size_t bytes; debugmsg(DM_CALL, "waitup() start\n"); if (needscan) childscan(); if (activechildren <= 0) return; /* * Settup which children we want to select() on. */ for (pc = childlist; pc; pc = pc->c_next) if (pc->c_readfd > rchildfdsn) rchildfdsn = pc->c_readfd; bytes = howmany(rchildfdsn+1, NFDBITS) * sizeof(fd_mask); if ((rchildfdsp = (fd_set *)malloc(bytes)) == NULL) return; memset(rchildfdsp, 0, bytes); for (pc = childlist; pc; pc = pc->c_next) if (pc->c_readfd > 0) { debugmsg(DM_MISC, "waitup() select on %d (%s)\n", pc->c_readfd, pc->c_name); FD_SET(pc->c_readfd, rchildfdsp); } /* * Actually call select() */ /* XXX remove debugmsg() calls */ debugmsg(DM_MISC, "waitup() Call select(), activechildren=%d\n", activechildren); count = select(rchildfdsn+1, (SELECT_FD_TYPE *) rchildfdsp, NULL, NULL, NULL); debugmsg(DM_MISC, "waitup() select returned %d activechildren = %d\n", count, activechildren); /* * select() will return count < 0 and errno == EINTR when * there are no active children left. */ if (count < 0) { if (errno != EINTR) error("Select failed reading children input: %s", SYSERR); free(rchildfdsp); return; } /* * This should never happen. */ if (count == 0) { error("Select returned an unexpected count of 0."); free(rchildfdsp); return; } /* * Go through the list of children and read from each child * which select() detected as ready for reading. */ for (pc = childlist; pc && count > 0; pc = pc->c_next) { /* * Make sure child still exists */ if (pc->c_name && kill(pc->c_pid, 0) < 0 && errno == ESRCH) { debugmsg(DM_MISC, "waitup() proc %d (%s) died unexpectedly!", pc->c_pid, pc->c_name); pc->c_state = PSdead; needscan = TRUE; } if (pc->c_name == NULL || !FD_ISSET(pc->c_readfd, rchildfdsp)) continue; readchild(pc); --count; } free(rchildfdsp); #else /* !defined(HAVE_SELECT) */ /* * The non-select() version of waitproc() */ debugmsg(DM_CALL, "waitup() start\n"); if (waitproc(NULL, TRUE) > 0) --activechildren; #endif /* defined(HAVE_SELECT) */ debugmsg(DM_CALL, "waitup() end\n"); }