📄 jobs.c
字号:
}intjobscmd(int argc, char **argv){ int mode, m; int sv = jobs_invalid; jobs_invalid = 0; mode = 0; while ((m = nextopt("lp"))) if (m == 'l') mode = SHOW_PID; else mode = SHOW_PGID; if (*argptr) do showjob(out1, getjob(*argptr,0), mode); while (*++argptr); else showjobs(out1, mode); jobs_invalid = sv; return 0;}/* * Print a list of jobs. If "change" is nonzero, only print jobs whose * statuses have changed since the last call to showjobs. * * If the shell is interrupted in the process of creating a job, the * result may be a job structure containing zero processes. Such structures * will be freed here. */voidshowjobs(struct output *out, int mode){ int jobno; struct job *jp; int silent = 0, gotpid; TRACE(("showjobs(%x) called\n", mode)); /* If not even one one job changed, there is nothing to do */ gotpid = dowait(0, NULL); while (dowait(0, NULL) > 0) continue;#ifdef JOBS /* * Check if we are not in our foreground group, and if not * put us in it. */ if (mflag && gotpid != -1 && tcgetpgrp(ttyfd) != getpid()) { if (tcsetpgrp(ttyfd, getpid()) == -1) error("Cannot set tty process group (%s) at %d", strerror(errno), __LINE__); TRACE(("repaired tty process group\n")); silent = 1; }#endif if (jobs_invalid) return; for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { if (!jp->used) continue; if (jp->nprocs == 0) { freejob(jp); continue; } if ((mode & SHOW_CHANGED) && !jp->changed) continue; if (silent && jp->changed) { jp->changed = 0; continue; } showjob(out, jp, mode); }}/* * Mark a job structure as unused. */STATIC voidfreejob(struct job *jp){ INTOFF; if (jp->ps != &jp->ps0) { ckfree(jp->ps); jp->ps = &jp->ps0; } jp->nprocs = 0; jp->used = 0;#if JOBS set_curjob(jp, 0);#endif INTON;}intwaitcmd(int argc, char **argv){ struct job *job; int status, retval = 127; struct job *jp; nextopt(""); if (!*argptr) { /* wait for all jobs */ jp = jobtab; if (jobs_invalid) return 0; for (;;) { if (jp >= jobtab + njobs) { /* no running procs */ return 0; } if (!jp->used || jp->state != JOBRUNNING) { jp++; continue; } if (dowait(1, (struct job *)NULL) == -1) return 128 + SIGINT; jp = jobtab; } } for (; *argptr; argptr++) { job = getjob(*argptr, 1); if (!job) { retval = 127; continue; } /* loop until process terminated or stopped */ while (job->state == JOBRUNNING) { if (dowait(1, (struct job *)NULL) == -1) return 128 + SIGINT; } status = job->ps[job->nprocs].status; if (WIFEXITED(status)) retval = WEXITSTATUS(status);#if JOBS else if (WIFSTOPPED(status)) retval = WSTOPSIG(status) + 128;#endif else { /* XXX: limits number of signals */ retval = WTERMSIG(status) + 128; } if (!iflag) freejob(job); } return retval;}intjobidcmd(int argc, char **argv){ struct job *jp; int i; nextopt(""); jp = getjob(*argptr, 0); for (i = 0 ; i < jp->nprocs ; ) { out1fmt("%ld", (long)jp->ps[i].pid); out1c(++i < jp->nprocs ? ' ' : '\n'); } return 0;}intgetjobpgrp(const char *name){ struct job *jp; jp = getjob(name, 1); if (jp == 0) return 0; return -jp->ps[0].pid;}/* * Convert a job name to a job structure. */STATIC struct job *getjob(const char *name, int noerror){ int jobno = -1; struct job *jp; int pid; int i; const char *err_msg = "No such job: %s"; if (name == NULL) {#if JOBS jobno = curjob;#endif err_msg = "No current job"; } else if (name[0] == '%') { if (is_number(name + 1)) { jobno = number(name + 1) - 1; } else if (!name[2]) { switch (name[1]) {#if JOBS case 0: case '+': case '%': jobno = curjob; err_msg = "No current job"; break; case '-': jobno = curjob; if (jobno != -1) jobno = jobtab[jobno].prev_job; err_msg = "No previous job"; break;#endif default: goto check_pattern; } } else { struct job *found; check_pattern: found = NULL; for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { if (!jp->used || jp->nprocs <= 0) continue; if ((name[1] == '?' && strstr(jp->ps[0].cmd, name + 2)) || prefix(name + 1, jp->ps[0].cmd)) { if (found) { err_msg = "%s: ambiguous"; found = 0; break; } found = jp; } } if (found) return found; } } else if (is_number(name)) { pid = number(name); for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { if (jp->used && jp->nprocs > 0 && jp->ps[jp->nprocs - 1].pid == pid) return jp; } } if (!jobs_invalid && jobno >= 0 && jobno < njobs) { jp = jobtab + jobno; if (jp->used) return jp; } if (!noerror) error(err_msg, name); return 0;}/* * Return a new job structure, */struct job *makejob(union node *node, int nprocs){ int i; struct job *jp; if (jobs_invalid) { for (i = njobs, jp = jobtab ; --i >= 0 ; jp++) { if (jp->used) freejob(jp); } jobs_invalid = 0; } for (i = njobs, jp = jobtab ; ; jp++) { if (--i < 0) { INTOFF; if (njobs == 0) { jobtab = ckmalloc(4 * sizeof jobtab[0]); } else { jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); memcpy(jp, jobtab, njobs * sizeof jp[0]); /* Relocate `ps' pointers */ for (i = 0; i < njobs; i++) if (jp[i].ps == &jobtab[i].ps0) jp[i].ps = &jp[i].ps0; ckfree(jobtab); jobtab = jp; } jp = jobtab + njobs; for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0); INTON; break; } if (jp->used == 0) break; } INTOFF; jp->state = JOBRUNNING; jp->used = 1; jp->changed = 0; jp->nprocs = 0;#if JOBS jp->jobctl = jobctl; set_curjob(jp, 1);#endif if (nprocs > 1) { jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); } else { jp->ps = &jp->ps0; } INTON; TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, jp - jobtab + 1)); return jp;}/* * Fork off a subshell. If we are doing job control, give the subshell its * own process group. Jp is a job structure that the job is to be added to. * N is the command that will be evaluated by the child. Both jp and n may * be NULL. The mode parameter can be one of the following: * FORK_FG - Fork off a foreground process. * FORK_BG - Fork off a background process. * FORK_NOJOB - Like FORK_FG, but don't give the process its own * process group even if job control is on. * * When job control is turned off, background processes have their standard * input redirected to /dev/null (except for the second and later processes * in a pipeline). */intforkshell(struct job *jp, union node *n, int mode){ int pid; TRACE(("forkshell(%%%d, %p, %d) called\n", jp - jobtab, n, mode)); switch ((pid = fork())) { case -1: TRACE(("Fork failed, errno=%d\n", errno)); INTON; error("Cannot fork"); break; case 0: forkchild(jp, n, mode, 0); return 0; default: return forkparent(jp, n, mode, pid); }}intforkparent(struct job *jp, union node *n, int mode, pid_t pid){ int pgrp; if (rootshell && mode != FORK_NOJOB && mflag) { if (jp == NULL || jp->nprocs == 0) pgrp = pid; else pgrp = jp->ps[0].pid;#ifdef USE_PROCESS_GROUPS /* This can fail because we are doing it in the child also */ (void)setpgid(pid, pgrp);#endif } if (mode == FORK_BG) backgndpid = pid; /* set $! */ if (jp) { struct procstat *ps = &jp->ps[jp->nprocs++]; ps->pid = pid; ps->status = -1; ps->cmd[0] = 0; if (/* iflag && rootshell && */ n) commandtext(ps, n); } TRACE(("In parent shell: child = %d\n", pid)); return pid;}voidforkchild(struct job *jp, union node *n, int mode, int vforked){ int wasroot; int pgrp; const char *devnull = _PATH_DEVNULL; const char *nullerr = "Can't open %s"; wasroot = rootshell; TRACE(("Child shell %d\n", getpid())); if (!vforked) rootshell = 0; closescript(vforked); clear_traps(vforked);#if JOBS if (!vforked) jobctl = 0; /* do job control only in root shell */ if (wasroot && mode != FORK_NOJOB && mflag) { if (jp == NULL || jp->nprocs == 0) pgrp = getpid(); else pgrp = jp->ps[0].pid;#ifdef USE_PROCESS_GROUPS /* This can fail because we are doing it in the parent also */ (void)setpgid(0, pgrp); if (mode == FORK_FG) { if (tcsetpgrp(ttyfd, pgrp) == -1) error("Cannot set tty process group (%s) at %d", strerror(errno), __LINE__); }#endif setsignal(SIGTSTP, vforked); setsignal(SIGTTOU, vforked); } else if (mode == FORK_BG) { ignoresig(SIGINT, vforked); ignoresig(SIGQUIT, vforked); if ((jp == NULL || jp->nprocs == 0) && ! fd0_redirected_p ()) { close(0); if (open(devnull, O_RDONLY) != 0) error(nullerr, devnull); } }#else if (mode == FORK_BG) { ignoresig(SIGINT, vforked); ignoresig(SIGQUIT, vforked); if ((jp == NULL || jp->nprocs == 0) && ! fd0_redirected_p ()) { close(0); if (open(devnull, O_RDONLY) != 0) error(nullerr, devnull); } }#endif if (wasroot && iflag) { setsignal(SIGINT, vforked); setsignal(SIGQUIT, vforked); setsignal(SIGTERM, vforked); } if (!vforked) jobs_invalid = 1;}/* * Wait for job to finish. * * Under job control we have the problem that while a child process is * running interrupts generated by the user are sent to the child but not * to the shell. This means that an infinite loop started by an inter- * active user may be hard to kill. With job control turned off, an * interactive user may place an interactive program inside a loop. If * the interactive program catches interrupts, the user doesn't want * these interrupts to also abort the loop. The approach we take here * is to have the shell ignore interrupt signals while waiting for a * forground process to terminate, and then send itself an interrupt * signal if the child process was terminated by an interrupt signal. * Unfortunately, some programs want to do a bit of cleanup and then * exit on interrupt; unless these processes terminate themselves by * sending a signal to themselves (instead of calling exit) they will * confuse this approach. */intwaitforjob(struct job *jp){#if JOBS int mypgrp = getpgrp();#endif int status; int st; INTOFF; TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); while (jp->state == JOBRUNNING) { dowait(1, jp);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -