📄 jobs.c
字号:
(j->state == PEXITED) ? 0 : TF_MIPSKLUDGE); /* Don't use tty mode if job is stopped and * later restarted and exits. Consider * the sequence: * vi foo (stopped) * ... * stty something * ... * fg (vi; ZZ) * mode should be that of the stty, not what * was before the vi started. */ if (j->state == PSTOPPED) j->flags &= ~JF_USETTYMODE; } }#ifdef JOBS /* If it looks like user hit ^C to kill a job, pretend we got * one too to break out of for loops, etc. (at&t ksh does this * even when not monitoring, but this doesn't make sense since * a tty generated ^C goes to the whole process group) */ status = j->last_proc->status; if (Flag(FMONITOR) && j->state == PSIGNALLED && WIFSIGNALED(status) && (sigtraps[WTERMSIG(status)].flags & TF_TTY_INTR)) trapsig(WTERMSIG(status));#endif /* JOBS */ } j_usrtime = j->usrtime; j_systime = j->systime; rv = j->status; if (!(flags & JW_ASYNCNOTIFY) && (!Flag(FMONITOR) || j->state != PSTOPPED)) { j_print(j, JP_SHORT, shl_out); shf_flush(shl_out); } if (j->state != PSTOPPED && (!Flag(FMONITOR) || !(flags & JW_ASYNCNOTIFY))) remove_job(j, where); return rv;}/* SIGCHLD handler to reap children and update job states * * If jobs are compiled in then this routine expects sigchld to be blocked. */static RETSIGTYPEj_sigchld(sig) int sig;{ int errno_ = errno; Job *j; Proc UNINITIALIZED(*p); int pid; WAIT_T status; struct tms t0, t1;#ifdef JOB_SIGS /* Don't wait for any processes if a job is partially started. * This is so we don't do away with the process group leader * before all the processes in a pipe line are started (so the * setpgid() won't fail) */ for (j = job_list; j; j = j->next) if (j->ppid == procpid && !(j->flags & JF_STARTED)) { held_sigchld = 1; return RETSIGVAL; }#endif /* JOB_SIGS */ ksh_times(&t0); do {#ifdef JOB_SIGS pid = ksh_waitpid(-1, &status, (WNOHANG|WUNTRACED));#else /* JOB_SIGS */ pid = wait(&status);#endif /* JOB_SIGS */ if (pid <= 0) /* return if would block (0) ... */ break; /* ... or no children or interrupted (-1) */ ksh_times(&t1); /* find job and process structures for this pid */ for (j = job_list; j != (Job *) 0; j = j->next) for (p = j->proc_list; p != (Proc *) 0; p = p->next) if (p->pid == pid) goto found;found: if (j == (Job *) 0) { /* Can occur if process has kids, then execs shell warningf(TRUE, "bad process waited for (pid = %d)", pid); */ t0 = t1; continue; } j->usrtime += t1.tms_cutime - t0.tms_cutime; j->systime += t1.tms_cstime - t0.tms_cstime; t0 = t1; p->status = status;#ifdef JOBS if (WIFSTOPPED(status)) p->state = PSTOPPED; else#endif /* JOBS */ if (WIFSIGNALED(status)) p->state = PSIGNALLED; else p->state = PEXITED; check_job(j); /* check to see if entire job is done */ }#ifdef JOB_SIGS while (1);#else /* JOB_SIGS */ while (0);#endif /* JOB_SIGS */ errno = errno_; return RETSIGVAL;}/* * Called only when a process in j has exited/stopped (ie, called only * from j_sigchld()). If no processes are running, the job status * and state are updated, asynchronous job notification is done and, * if unneeded, the job is removed. * * If jobs are compiled in then this routine expects sigchld to be blocked. */static voidcheck_job(j) Job *j;{ int jstate; Proc *p; /* XXX debugging (nasty - interrupt routine using shl_out) */ if (!(j->flags & JF_STARTED)) { internal_errorf(0, "check_job: job started (flags 0x%x)", j->flags); return; } jstate = PRUNNING; for (p=j->proc_list; p != (Proc *) 0; p = p->next) { if (p->state == PRUNNING) return; /* some processes still running */ if (p->state > jstate) jstate = p->state; } j->state = jstate; switch (j->last_proc->state) { case PEXITED: j->status = WEXITSTATUS(j->last_proc->status); break; case PSIGNALLED: j->status = 128 + WTERMSIG(j->last_proc->status); break; default: j->status = 0; break; }#ifdef KSH /* Note when co-process dies: can't be done in j_wait() nor * remove_job() since neither may be called for non-interactive * shells. */ if (j->state == PEXITED || j->state == PSIGNALLED) { /* No need to keep co-process input any more * (at leasst, this is what ksh93d thinks) */ if (coproc.job == j) { coproc.job = (void *) 0; /* XXX would be nice to get the closes out of here * so they aren't done in the signal handler. * Would mean a check in coproc_getfd() to * do "if job == 0 && write >= 0, close write". */ coproc_write_close(coproc.write); } /* Do we need to keep the output? */ if (j->coproc_id && j->coproc_id == coproc.id && --coproc.njobs == 0) coproc_readw_close(coproc.read); }#endif /* KSH */ j->flags |= JF_CHANGED;#ifdef JOBS if (Flag(FMONITOR) && !(j->flags & JF_XXCOM)) { /* Only put stopped jobs at the front to avoid confusing * the user (don't want finished jobs effecting %+ or %-) */ if (j->state == PSTOPPED) put_job(j, PJ_ON_FRONT); if (Flag(FNOTIFY) && (j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY)) != JF_WAITING) { /* Look for the real file descriptor 2 */ { struct env *ep; int fd = 2; for (ep = e; ep; ep = ep->oenv) if (ep->savefd && ep->savefd[2]) fd = ep->savefd[2]; shf_reopen(fd, SHF_WR, shl_j); } /* Can't call j_notify() as it removes jobs. The job * must stay in the job list as j_waitj() may be * running with this job. */ j_print(j, JP_MEDIUM, shl_j); shf_flush(shl_j); if (!(j->flags & JF_WAITING) && j->state != PSTOPPED) remove_job(j, "notify"); } }#endif /* JOBS */ if (!Flag(FMONITOR) && !(j->flags & (JF_WAITING|JF_FG)) && j->state != PSTOPPED) { if (j == async_job || (j->flags & JF_KNOWN)) { j->flags |= JF_ZOMBIE; j->job = -1; nzombie++; } else remove_job(j, "checkjob"); }}/* * Print job status in either short, medium or long format. * * If jobs are compiled in then this routine expects sigchld to be blocked. */static voidj_print(j, how, shf) Job *j; int how; struct shf *shf;{ Proc *p; int state; WAIT_T status; int coredumped; char jobchar = ' '; char buf[64]; const char *filler; int output = 0; if (how == JP_PGRP) { /* POSIX doesn't say what to do it there is no process * group leader (ie, !FMONITOR). We arbitrarily return * last pid (which is what $! returns). */ shf_fprintf(shf, "%d\n", j->pgrp ? j->pgrp : (j->last_proc ? j->last_proc->pid : 0)); return; } j->flags &= ~JF_CHANGED; filler = j->job > 10 ? "\n " : "\n "; if (j == job_list) jobchar = '+'; else if (j == job_list->next) jobchar = '-'; for (p = j->proc_list; p != (Proc *) 0;) { coredumped = 0; switch (p->state) { case PRUNNING: strcpy(buf, "Running"); break; case PSTOPPED: strcpy(buf, sigtraps[WSTOPSIG(p->status)].mess); break; case PEXITED: if (how == JP_SHORT) buf[0] = '\0'; else if (WEXITSTATUS(p->status) == 0) strcpy(buf, "Done"); else shf_snprintf(buf, sizeof(buf), "Done (%d)", WEXITSTATUS(p->status)); break; case PSIGNALLED: if (WIFCORED(p->status)) coredumped = 1; /* kludge for not reporting `normal termination signals' * (ie, SIGINT, SIGPIPE) */ if (how == JP_SHORT && !coredumped && (WTERMSIG(p->status) == SIGINT || WTERMSIG(p->status) == SIGPIPE)) { buf[0] = '\0'; } else strcpy(buf, sigtraps[WTERMSIG(p->status)].mess); break; } if (how != JP_SHORT) if (p == j->proc_list) shf_fprintf(shf, "[%d] %c ", j->job, jobchar); else shf_fprintf(shf, "%s", filler); if (how == JP_LONG) shf_fprintf(shf, "%5d ", p->pid); if (how == JP_SHORT) { if (buf[0]) { output = 1; shf_fprintf(shf, "%s%s ", buf, coredumped ? " (core dumped)" : null); } } else { output = 1; shf_fprintf(shf, "%-20s %s%s%s", buf, p->command, p->next ? "|" : null, coredumped ? " (core dumped)" : null); } state = p->state; status = p->status; p = p->next; while (p && p->state == state && WSTATUS(p->status) == WSTATUS(status)) { if (how == JP_LONG) shf_fprintf(shf, "%s%5d %-20s %s%s", filler, p->pid, space, p->command, p->next ? "|" : null); else if (how == JP_MEDIUM) shf_fprintf(shf, " %s%s", p->command, p->next ? "|" : null); p = p->next; } } if (output) shf_fprintf(shf, newline);}/* Convert % sequence to job * * If jobs are compiled in then this routine expects sigchld to be blocked. */static Job *j_lookup(cp, ecodep) const char *cp; int *ecodep;{ Job *j, *last_match; Proc *p; int len, job = 0; if (digit(*cp)) { job = atoi(cp); /* Look for last_proc->pid (what $! returns) first... */ for (j = job_list; j != (Job *) 0; j = j->next) if (j->last_proc && j->last_proc->pid == job) return j; /* ...then look for process group (this is non-POSIX), * but should not break anything (so FPOSIX isn't used). */ for (j = job_list; j != (Job *) 0; j = j->next) if (j->pgrp && j->pgrp == job) return j; if (ecodep) *ecodep = JL_NOSUCH; return (Job *) 0; } if (*cp != '%') { if (ecodep) *ecodep = JL_INVALID; return (Job *) 0; } switch (*++cp) { case '\0': /* non-standard */ case '+': case '%': if (job_list != (Job *) 0) return job_list; break; case '-': if (job_list != (Job *) 0 && job_list->next) return job_list->next; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': job = atoi(cp); for (j = job_list; j != (Job *) 0; j = j->next) if (j->job == job) return j; break; case '?': /* %?string */ last_match = (Job *) 0; for (j = job_list; j != (Job *) 0; j = j->next) for (p = j->proc_list; p != (Proc *) 0; p = p->next) if (strstr(p->command, cp+1) != (char *) 0) { if (last_match) { if (ecodep) *ecodep = JL_AMBIG; return (Job *) 0; } last_match = j; } if (last_match) return last_match; break; default: /* %string */ len = strlen(cp); last_match = (Job *) 0; for (j = job_list; j != (Job *) 0; j = j->next) if (strncmp(cp, j->proc_list->command, len) == 0) { if (last_match) { if (ecodep) *ecodep = JL_AMBIG; return (Job *) 0; } last_match = j; } if (last_match) return last_match; break; } if (ecodep) *ecodep = JL_NOSUCH; return (Job *) 0;}static Job *free_jobs;static Proc *free_procs;/* allocate a new job and fill in the job number. * * If jobs are compiled in then this routine expects sigchld to be blocked. */static Job *new_job(){ int i; Job *newj, *j; if (free_jobs != (Job *) 0) { newj = free_jobs; free_jobs = free_jobs->next; } else newj = (Job *) alloc(sizeof(Job), APERM); /* brute force method */ for (i = 1; ; i++) { for (j = job_list; j && j->job != i; j = j->next) ; if (j == (Job *) 0) break; } newj->job = i; return newj;}/* Allocate new process strut * * If jobs are compiled in then this routine expects sigchld to be blocked. */static Proc *new_proc(){ Proc *p; if (free_procs != (Proc *) 0) { p = free_procs; free_procs = free_procs->next; } else p = (Proc *) alloc(sizeof(Proc), APERM); return p;}/* Take job out of job_list and put old structures into free list. * Keeps nzombies, last_job and async_job up to date. * * If jobs are compiled in then this routine expects sigchld to be blocked. */static voidremove_job(j, where) Job *j; const char *where;{ Proc *p, *tmp; Job **prev, *curr; prev = &job_list; curr = *prev; for (; curr != (Job *) 0 && curr != j; prev = &curr->next, curr = *prev) ; if (curr != j) { internal_errorf(0, "remove_job: job not found (%s)", where); return; } *prev = curr->next; /* free up proc structures */ for (p = j->proc_list; p != (Proc *) 0; ) { tmp = p; p = p->next; tmp->next = free_procs; free_procs = tmp; } if ((j->flags & JF_ZOMBIE) && j->ppid == procpid) --nzombie; j->next = free_jobs; free_jobs = j; if (j == last_job) last_job = (Job *) 0; if (j == async_job) async_job = (Job *) 0;}/* put j in a particular location (taking it out job_list if it is there * already) * * If jobs are compiled in then this routine expects sigchld to be blocked. */static voidput_job(j, where) Job *j; int where;{ Job **prev, *curr; /* Remove job from list (if there) */ prev = &job_list; curr = job_list; for (; curr && curr != j; prev = &curr->next, curr = *prev) ; if (curr == j) *prev = curr->next; switch (where) { case PJ_ON_FRONT: j->next = job_list; job_list = j; break; case PJ_PAST_STOPPED: prev = &job_list; curr = job_list; for (; curr && curr->state == PSTOPPED; prev = &curr->next, curr = *prev) ; j->next = curr; *prev = j; break; }}/* nuke a job (called when unable to start full job). * * If jobs are compiled in then this routine expects sigchld to be blocked. */static voidkill_job(j) Job *j;{ Proc *p; for (p = j->proc_list; p != (Proc *) 0; p = p->next) if (p->pid != 0) (void) kill(p->pid, SIGKILL);}/* put a more useful name on a process than snptreef does (in certain cases) */static voidfill_command(c, len, t) char *c; int len; struct op *t;{ int alen; char **ap; if (t->type == TEXEC || t->type == TCOM) { /* Causes problems when set -u is in effect, can also cause problems when array indices evaluated (may have side effects, eg, assignment, incr, etc.) if (t->type == TCOM) ap = eval(t->args, DOBLANK|DONTRUNCOMMAND); else */ ap = t->args; --len; /* save room for the null */ while (len > 0 && *ap != (char *) 0) { alen = strlen(*ap); if (alen > len) alen = len; memcpy(c, *ap, alen); c += alen; len -= alen; if (len > 0) { *c++ = ' '; len--; } ap++; } *c = '\0'; } else snptreef(c, len, "%T", t);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -