📄 jobs.c
字号:
} } remove_job(j, "child"); /* in case of `jobs` command */ nzombie = 0;#ifdef JOBS ttypgrp_ok = 0; Flag(FMONITOR) = 0;#endif /* JOBS */ Flag(FTALKING) = 0;#ifdef OS2 if (tty_fd >= 0) flags |= XINTACT;#endif /* OS2 */ 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 */#ifdef TTY_PGRP /* YYY: Is this needed? (see also YYY above) if (Flag(FMONITOR) && !(flags&(XXCOM|XBGND))) tcsetpgrp(tty_fd, j->pgrp); */#endif /* TTY_PGRP */ j_startjob(j);#ifdef KSH 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 */ }#endif /* KSH */ 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"); }#ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */ return rv;}/* start the last job: only used for `command` jobs */voidstartlast(){#ifdef JOB_SIGS sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);#endif /* JOB_SIGS */ if (last_job) { /* no need to report error - waitlast() will do it */ /* ensure it isn't removed by check_job() */ last_job->flags |= JF_WAITING; j_startjob(last_job); }#ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */}/* wait for last job: only used for `command` jobs */intwaitlast(){ int rv; Job *j;#ifdef JOB_SIGS sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);#endif /* JOB_SIGS */ j = last_job; if (!j || !(j->flags & JF_STARTED)) { if (!j) warningf(TRUE, "waitlast: no last job"); else internal_errorf(0, "waitlast: not started");#ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */ return 125; /* not so arbitrary, non-zero value */ } rv = j_waitj(j, JW_NONE, "jw:waitlast");#ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */ return rv;}/* wait for child, interruptable. */intwaitfor(cp, sigp) const char *cp; int *sigp;{ int rv; Job *j; int ecode; int flags = JW_INTERRUPT|JW_ASYNCNOTIFY;#ifdef JOB_SIGS sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);#endif /* JOB_SIGS */ *sigp = 0; if (cp == (char *) 0) { /* wait for an unspecified job - always returns 0, so * don't have to worry about exited/signaled jobs */ for (j = job_list; j; j = j->next) /* at&t ksh will wait for stopped jobs - we don't */ if (j->ppid == procpid && j->state == PRUNNING) break; if (!j) {#ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */ return -1; } } else if ((j = j_lookup(cp, &ecode))) { /* don't report normal job completion */ flags &= ~JW_ASYNCNOTIFY; if (j->ppid != procpid) {#ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */ return -1; } } else {#ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */ if (ecode != JL_NOSUCH) bi_errorf("%s: %s", cp, lookup_msgs[ecode]); return -1; } /* at&t ksh will wait for stopped jobs - we don't */ rv = j_waitj(j, flags, "jw:waitfor");#ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */ if (rv < 0) /* we were interrupted */ *sigp = 128 + -rv; return rv;}/* kill (built-in) a job */intj_kill(cp, sig) const char *cp; int sig;{ Job *j; Proc *p; int rv = 0; int ecode;#ifdef JOB_SIGS sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);#endif /* JOB_SIGS */ if ((j = j_lookup(cp, &ecode)) == (Job *) 0) {#ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */ bi_errorf("%s: %s", cp, lookup_msgs[ecode]); return 1; } if (j->pgrp == 0) { /* started when !Flag(FMONITOR) */ for (p=j->proc_list; p != (Proc *) 0; p = p->next) if (kill(p->pid, sig) < 0) { bi_errorf("%s: %s", cp, strerror(errno)); rv = 1; } } else {#ifdef JOBS if (j->state == PSTOPPED && (sig == SIGTERM || sig == SIGHUP)) (void) killpg(j->pgrp, SIGCONT);#endif /* JOBS */ if (killpg(j->pgrp, sig) < 0) { bi_errorf("%s: %s", cp, strerror(errno)); rv = 1; } }#ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */ return rv;}#ifdef JOBS/* fg and bg built-ins: called only if Flag(FMONITOR) set */intj_resume(cp, bg) const char *cp; int bg;{ Job *j; Proc *p; int ecode; int running; int rv = 0; sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); if ((j = j_lookup(cp, &ecode)) == (Job *) 0) { sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); bi_errorf("%s: %s", cp, lookup_msgs[ecode]); return 1; } if (j->pgrp == 0) { sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); bi_errorf("job not job-controlled"); return 1; } if (bg) shprintf("[%d] ", j->job); running = 0; for (p = j->proc_list; p != (Proc *) 0; p = p->next) { if (p->state == PSTOPPED) { p->state = PRUNNING; WSTATUS(p->status) = 0; running = 1; } shprintf("%s%s", p->command, p->next ? "| " : null); } shprintf(newline); shf_flush(shl_stdout); if (running) j->state = PRUNNING; put_job(j, PJ_PAST_STOPPED); if (bg) j_set_async(j); else {# ifdef TTY_PGRP /* attach tty to job */ if (j->state == PRUNNING) { if (ttypgrp_ok && (j->flags & JF_SAVEDTTY)) { set_tty(tty_fd, &j->ttystate, TF_NONE); } /* See comment in j_waitj regarding saved_ttypgrp. */ if (ttypgrp_ok && tcsetpgrp(tty_fd, (j->flags & JF_SAVEDTTYPGRP) ? j->saved_ttypgrp : j->pgrp) < 0) { if (j->flags & JF_SAVEDTTY) { set_tty(tty_fd, &tty_state, TF_NONE); } sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); bi_errorf("1st tcsetpgrp(%d, %d) failed: %s", tty_fd, (int) ((j->flags & JF_SAVEDTTYPGRP) ? j->saved_ttypgrp : j->pgrp), strerror(errno)); return 1; } }# endif /* TTY_PGRP */ j->flags |= JF_FG; j->flags &= ~JF_KNOWN; if (j == async_job) async_job = (Job *) 0; } if (j->state == PRUNNING && killpg(j->pgrp, SIGCONT) < 0) { int err = errno; if (!bg) { j->flags &= ~JF_FG;# ifdef TTY_PGRP if (ttypgrp_ok && (j->flags & JF_SAVEDTTY)) { set_tty(tty_fd, &tty_state, TF_NONE); } if (ttypgrp_ok && tcsetpgrp(tty_fd, our_pgrp) < 0) { warningf(TRUE, "fg: 2nd tcsetpgrp(%d, %d) failed: %s", tty_fd, (int) our_pgrp, strerror(errno)); }# endif /* TTY_PGRP */ } sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); bi_errorf("cannot continue job %s: %s", cp, strerror(err)); return 1; } if (!bg) {# ifdef TTY_PGRP if (ttypgrp_ok) { j->flags &= ~(JF_SAVEDTTY | JF_SAVEDTTYPGRP); }# endif /* TTY_PGRP */ rv = j_waitj(j, JW_NONE, "jw:resume"); } sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); return rv;}#endif /* JOBS *//* are there any running or stopped jobs ? */intj_stopped_running(){ Job *j; int which = 0; for (j = job_list; j != (Job *) 0; j = j->next) {#ifdef JOBS if (j->ppid == procpid && j->state == PSTOPPED) which |= 1;#endif /* JOBS */ if (Flag(FLOGIN) && !Flag(FNOHUP) && procpid == kshpid && j->ppid == procpid && j->state == PRUNNING) which |= 2; } if (which) { shellf("You have %s%s%s jobs\n", which & 1 ? "stopped" : "", which == 3 ? " and " : "", which & 2 ? "running" : ""); return 1; } return 0;}/* list jobs for jobs built-in */intj_jobs(cp, slp, nflag) const char *cp; int slp; /* 0: short, 1: long, 2: pgrp */ int nflag;{ Job *j, *tmp; int how; int zflag = 0;#ifdef JOB_SIGS sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);#endif /* JOB_SIGS */ if (nflag < 0) { /* kludge: print zombies */ nflag = 0; zflag = 1; } if (cp) { int ecode; if ((j = j_lookup(cp, &ecode)) == (Job *) 0) {#ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */ bi_errorf("%s: %s", cp, lookup_msgs[ecode]); return 1; } } else j = job_list; how = slp == 0 ? JP_MEDIUM : (slp == 1 ? JP_LONG : JP_PGRP); for (; j; j = j->next) { if ((!(j->flags & JF_ZOMBIE) || zflag) && (!nflag || (j->flags & JF_CHANGED))) { j_print(j, how, shl_stdout); if (j->state == PEXITED || j->state == PSIGNALLED) j->flags |= JF_REMOVE; } if (cp) break; } /* Remove jobs after printing so there won't be multiple + or - jobs */ for (j = job_list; j; j = tmp) { tmp = j->next; if (j->flags & JF_REMOVE) remove_job(j, "jobs"); }#ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */ return 0;}/* list jobs for top-level notification */voidj_notify(){ Job *j, *tmp;#ifdef JOB_SIGS sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);#endif /* JOB_SIGS */ for (j = job_list; j; j = j->next) {#ifdef JOBS if (Flag(FMONITOR) && (j->flags & JF_CHANGED)) j_print(j, JP_MEDIUM, shl_out);#endif /* JOBS */ /* Remove job after doing reports so there aren't * multiple +/- jobs. */ if (j->state == PEXITED || j->state == PSIGNALLED) j->flags |= JF_REMOVE; } for (j = job_list; j; j = tmp) { tmp = j->next; if (j->flags & JF_REMOVE) remove_job(j, "notify"); } shf_flush(shl_out);#ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */}/* Return pid of last process in last asynchornous job */pid_tj_async(){#ifdef JOB_SIGS sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);#endif /* JOB_SIGS */ if (async_job) async_job->flags |= JF_KNOWN;#ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */ return async_pid;}/* Make j the last async process * * If jobs are compiled in then this routine expects sigchld to be blocked. */static voidj_set_async(j) Job *j;{ Job *jl, *oldest; if (async_job && (async_job->flags & (JF_KNOWN|JF_ZOMBIE)) == JF_ZOMBIE) remove_job(async_job, "async"); if (!(j->flags & JF_STARTED)) { internal_errorf(0, "j_async: job not started"); return; } async_job = j; async_pid = j->last_proc->pid; while (nzombie > child_max) { oldest = (Job *) 0; for (jl = job_list; jl; jl = jl->next) if (jl != async_job && (jl->flags & JF_ZOMBIE) && (!oldest || jl->age < oldest->age)) oldest = jl; if (!oldest) { /* XXX debugging */ if (!(async_job->flags & JF_ZOMBIE) || nzombie != 1) { internal_errorf(0, "j_async: bad nzombie (%d)", nzombie); nzombie = 0; } break; } remove_job(oldest, "zombie"); }}/* Start a job: set STARTED, check for held signals and set j->last_proc * * If jobs are compiled in then this routine expects sigchld to be blocked. */static voidj_startjob(j) Job *j;{ Proc *p; j->flags |= JF_STARTED; for (p = j->proc_list; p->next; p = p->next) ; j->last_proc = p;#ifdef NEED_PGRP_SYNC if (j_sync_open) { j_sync_open = 0; closepipe(j_sync_pipe); }#endif /* NEED_PGRP_SYNC */#ifdef JOB_SIGS if (held_sigchld) { held_sigchld = 0; /* Don't call j_sigchld() as it may remove job... */ kill(procpid, SIGCHLD); }#endif /* JOB_SIGS */}/* * wait for job to complete or change state * * If jobs are compiled in then this routine expects sigchld to be blocked. */static intj_waitj(j, flags, where) Job *j; int flags; /* see JW_* */ const char *where;{ int rv; /* * No auto-notify on the job we are waiting on. */ j->flags |= JF_WAITING; if (flags & JW_ASYNCNOTIFY) j->flags |= JF_W_ASYNCNOTIFY; if (!Flag(FMONITOR)) flags |= JW_STOPPEDWAIT; while ((volatile int) j->state == PRUNNING || ((flags & JW_STOPPEDWAIT) && (volatile int) j->state == PSTOPPED)) {#ifdef JOB_SIGS sigsuspend(&sm_default);#else /* JOB_SIGS */ j_sigchld(SIGCHLD);#endif /* JOB_SIGS */ if (fatal_trap) { int oldf = j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY); j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY); runtraps(TF_FATAL); j->flags |= oldf; /* not reached... */ } if ((flags & JW_INTERRUPT) && (rv = trap_pending())) { j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY); return -rv; } } j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY); if (j->flags & JF_FG) { WAIT_T status; j->flags &= ~JF_FG;#ifdef TTY_PGRP if (Flag(FMONITOR) && ttypgrp_ok && j->pgrp) { /* * Save the tty's current pgrp so it can be restored * when the job is foregrounded. This is to * deal with things like the GNU su which does * a fork/exec instead of an exec (the fork means * the execed shell gets a different pid from its * pgrp, so naturally it sets its pgrp and gets hosed * when it gets forgrounded by the parent shell, which * has restored the tty's pgrp to that of the su * process). */ if (j->state == PSTOPPED && (j->saved_ttypgrp = tcgetpgrp(tty_fd)) >= 0) j->flags |= JF_SAVEDTTYPGRP; if (tcsetpgrp(tty_fd, our_pgrp) < 0) { warningf(TRUE, "j_waitj: tcsetpgrp(%d, %d) failed: %s", tty_fd, (int) our_pgrp, strerror(errno)); } if (j->state == PSTOPPED) { j->flags |= JF_SAVEDTTY; get_tty(tty_fd, &j->ttystate); } }#endif /* TTY_PGRP */ if (tty_fd >= 0) { /* Only restore tty settings if job was originally * started in the foreground. Problems can be * caused by things like `more foobar &' which will * typically get and save the shell's vi/emacs tty * settings before setting up the tty for itself; * when more exits, it restores the `original' * settings, and things go down hill from there... */ if (j->state == PEXITED && j->status == 0 && (j->flags & JF_USETTYMODE)) { get_tty(tty_fd, &tty_state); } else { set_tty(tty_fd, &tty_state,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -