📄 jobs.c
字号:
/* $NetBSD: jobs.c,v 1.62 2003/12/18 00:56:05 christos Exp $ *//*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#include <sys/cdefs.h>#ifndef lint#if 0static char sccsid[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95";#else__RCSID("$NetBSD: jobs.c,v 1.62 2003/12/18 00:56:05 christos Exp $");#endif#endif /* not lint */#include <fcntl.h>#include <signal.h>#include <errno.h>#include <unistd.h>#include <stdlib.h>#define _PATH_DEVNULL "/dev/null"#include <sys/types.h>#include <sys/param.h>#ifdef BSD#include <sys/wait.h>#include <sys/time.h>#include <sys/resource.h>#endif#include <sys/wait.h>#define killpg(s,i) kill(-(s),i)#include <sys/ioctl.h>#include "shell.h"#if JOBS#if OLD_TTY_DRIVER#include "sgtty.h"#else#include <termios.h>#endif#undef CEOF /* syntax.h redefines this */#endif#include "redir.h"#include "show.h"#include "main.h"#include "parser.h"#include "nodes.h"#include "jobs.h"#include "options.h"#include "trap.h"#include "syntax.h"#include "input.h"#include "output.h"#include "memalloc.h"#include "error.h"#include "mystring.h"// Use of process groups is disabled to allow adb shell children to terminate when the shell dies#define USE_PROCESS_GROUPSstatic struct job *jobtab; /* array of jobs */static int njobs; /* size of array */static int jobs_invalid; /* set in child */MKINIT pid_t backgndpid = -1; /* pid of last background process */#if JOBSint initialpgrp; /* pgrp of shell on invocation */static int curjob = -1; /* current job */#endifstatic int ttyfd = -1;STATIC void restartjob(struct job *);STATIC void freejob(struct job *);STATIC struct job *getjob(const char *, int);STATIC int dowait(int, struct job *);STATIC int onsigchild(void);STATIC int waitproc(int, struct job *, int *);STATIC void cmdtxt(union node *);STATIC void cmdlist(union node *, int);STATIC void cmdputs(const char *);#ifdef OLD_TTY_DRIVERstatic pid_t tcgetpgrp(int fd);static int tcsetpgrp(int fd, pid_t pgrp);static pid_ttcgetpgrp(int fd){ pid_t pgrp; if (ioctl(fd, TIOCGPGRP, (char *)&pgrp) == -1) return -1; else return pgrp;}static inttcsetpgrp(int fd, pid_tpgrp){ return ioctl(fd, TIOCSPGRP, (char *)&pgrp);}#endif/* * Turn job control on and off. * * Note: This code assumes that the third arg to ioctl is a character * pointer, which is true on Berkeley systems but not System V. Since * System V doesn't have job control yet, this isn't a problem now. */MKINIT int jobctl;voidsetjobctl(int on){#ifdef OLD_TTY_DRIVER int ldisc;#endif if (on == jobctl || rootshell == 0) return; if (on) {#if defined(FIOCLEX) || defined(FD_CLOEXEC) int err; int i; if (ttyfd != -1) close(ttyfd); if ((ttyfd = open("/dev/tty", O_RDWR)) == -1) { for (i = 0; i < 3; i++) { if (isatty(i) && (ttyfd = dup(i)) != -1) break; } if (i == 3) goto out; } /* Move to a high fd */ for (i = 10; i > 2; i--) { if ((err = fcntl(ttyfd, F_DUPFD, (1 << i) - 1)) != -1) break; } if (err != -1) { close(ttyfd); ttyfd = err; }#ifdef FIOCLEX err = ioctl(ttyfd, FIOCLEX, 0);#elif FD_CLOEXEC err = fcntl(ttyfd, F_SETFD, fcntl(ttyfd, F_GETFD, 0) | FD_CLOEXEC);#endif if (err == -1) { close(ttyfd); ttyfd = -1; goto out; }#else out2str("sh: Need FIOCLEX or FD_CLOEXEC to support job control"); goto out;#endif do { /* while we are in the background */ if ((initialpgrp = tcgetpgrp(ttyfd)) < 0) {out: out2str("sh: can't access tty; job control turned off\n"); mflag = 0; return; } if (initialpgrp == -1) initialpgrp = getpgrp(); else if (initialpgrp != getpgrp()) { killpg(0, SIGTTIN); continue; } } while (0);#ifdef OLD_TTY_DRIVER if (ioctl(ttyfd, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) { out2str("sh: need new tty driver to run job control; job control turned off\n"); mflag = 0; return; }#endif setsignal(SIGTSTP, 0); setsignal(SIGTTOU, 0); setsignal(SIGTTIN, 0);#ifdef USE_PROCESS_GROUPS if (getpgid(0) != rootpid && setpgid(0, rootpid) == -1) error("Cannot set process group (%s) at %d", strerror(errno), __LINE__); if (tcsetpgrp(ttyfd, rootpid) == -1) error("Cannot set tty process group (%s) at %d", strerror(errno), __LINE__);#endif } else { /* turning job control off */#ifdef USE_PROCESS_GROUPS if (getpgid(0) != initialpgrp && setpgid(0, initialpgrp) == -1) error("Cannot set process group (%s) at %d", strerror(errno), __LINE__); if (tcsetpgrp(ttyfd, initialpgrp) == -1) error("Cannot set tty process group (%s) at %d", strerror(errno), __LINE__);#endif close(ttyfd); ttyfd = -1; setsignal(SIGTSTP, 0); setsignal(SIGTTOU, 0); setsignal(SIGTTIN, 0); } jobctl = on;}#ifdef mkinitINCLUDE <stdlib.h>SHELLPROC { backgndpid = -1;#if JOBS jobctl = 0;#endif}#endif#if JOBSintfgcmd(int argc, char **argv){ struct job *jp; int i; int status; nextopt(""); jp = getjob(*argptr, 0); if (jp->jobctl == 0) error("job not created under job control"); out1fmt("%s", jp->ps[0].cmd); for (i = 1; i < jp->nprocs; i++) out1fmt(" | %s", jp->ps[i].cmd ); out1c('\n'); flushall(); for (i = 0; i < jp->nprocs; i++) if (tcsetpgrp(ttyfd, jp->ps[i].pid) != -1) break; if (i >= jp->nprocs) { error("Cannot set tty process group (%s) at %d", strerror(errno), __LINE__); } restartjob(jp); INTOFF; status = waitforjob(jp); INTON; return status;}static voidset_curjob(struct job *jp, int mode){ struct job *jp1, *jp2; int i, ji; ji = jp - jobtab; /* first remove from list */ if (ji == curjob) curjob = jp->prev_job; else { for (i = 0; i < njobs; i++) { if (jobtab[i].prev_job != ji) continue; jobtab[i].prev_job = jp->prev_job; break; } } /* Then re-insert in correct position */ switch (mode) { case 0: /* job being deleted */ jp->prev_job = -1; break; case 1: /* newly created job or backgrounded job, put after all stopped jobs. */ if (curjob != -1 && jobtab[curjob].state == JOBSTOPPED) { for (jp1 = jobtab + curjob; ; jp1 = jp2) { if (jp1->prev_job == -1) break; jp2 = jobtab + jp1->prev_job; if (jp2->state != JOBSTOPPED) break; } jp->prev_job = jp1->prev_job; jp1->prev_job = ji; break; } /* FALLTHROUGH */ case 2: /* newly stopped job - becomes curjob */ jp->prev_job = curjob; curjob = ji; break; }}intbgcmd(int argc, char **argv){ struct job *jp; int i; nextopt(""); do { jp = getjob(*argptr, 0); if (jp->jobctl == 0) error("job not created under job control"); set_curjob(jp, 1); out1fmt("[%ld] %s", (long)(jp - jobtab + 1), jp->ps[0].cmd); for (i = 1; i < jp->nprocs; i++) out1fmt(" | %s", jp->ps[i].cmd ); out1c('\n'); flushall(); restartjob(jp); } while (*argptr && *++argptr); return 0;}STATIC voidrestartjob(struct job *jp){ struct procstat *ps; int i; if (jp->state == JOBDONE) return; INTOFF; for (i = 0; i < jp->nprocs; i++) if (killpg(jp->ps[i].pid, SIGCONT) != -1) break; if (i >= jp->nprocs) error("Cannot continue job (%s)", strerror(errno)); for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { if (WIFSTOPPED(ps->status)) { ps->status = -1; jp->state = JOBRUNNING; } } INTON;}#endifstatic voidshowjob(struct output *out, struct job *jp, int mode){ int procno; int st; struct procstat *ps; int col; char s[64];#if JOBS if (mode & SHOW_PGID) { /* just output process (group) id of pipeline */ outfmt(out, "%ld\n", (long)jp->ps->pid); return; }#endif procno = jp->nprocs; if (!procno) return; if (mode & SHOW_PID) mode |= SHOW_MULTILINE; if ((procno > 1 && !(mode & SHOW_MULTILINE)) || (mode & SHOW_SIGNALLED)) { /* See if we have more than one status to report */ ps = jp->ps; st = ps->status; do { int st1 = ps->status; if (st1 != st) /* yes - need multi-line output */ mode |= SHOW_MULTILINE; if (st1 == -1 || !(mode & SHOW_SIGNALLED) || WIFEXITED(st1)) continue; if (WIFSTOPPED(st1) || ((st1 = WTERMSIG(st1) & 0x7f) && st1 != SIGINT && st1 != SIGPIPE)) mode |= SHOW_ISSIG; } while (ps++, --procno); procno = jp->nprocs; } if (mode & SHOW_SIGNALLED && !(mode & SHOW_ISSIG)) { if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) { TRACE(("showjob: freeing job %d\n", jp - jobtab + 1)); freejob(jp); } return; } for (ps = jp->ps; --procno >= 0; ps++) { /* for each process */ if (ps == jp->ps) fmtstr(s, 16, "[%ld] %c ", (long)(jp - jobtab + 1),#if JOBS jp == jobtab + curjob ? '+' : curjob != -1 && jp == jobtab + jobtab[curjob].prev_job ? '-' :#endif ' '); else fmtstr(s, 16, " " ); col = strlen(s); if (mode & SHOW_PID) { fmtstr(s + col, 16, "%ld ", (long)ps->pid); col += strlen(s + col); } if (ps->status == -1) { scopy("Running", s + col); } else if (WIFEXITED(ps->status)) { st = WEXITSTATUS(ps->status); if (st) fmtstr(s + col, 16, "Done(%d)", st); else fmtstr(s + col, 16, "Done"); } else {#if JOBS if (WIFSTOPPED(ps->status)) st = WSTOPSIG(ps->status); else /* WIFSIGNALED(ps->status) */#endif st = WTERMSIG(ps->status); st &= 0x7f; if (st < NSIG && sys_siglist[st]) scopyn(sys_siglist[st], s + col, 32); else fmtstr(s + col, 16, "Signal %d", st); if (WCOREDUMP(ps->status)) { col += strlen(s + col); scopyn(" (core dumped)", s + col, 64 - col); } } col += strlen(s + col); outstr(s, out); do { outc(' ', out); col++; } while (col < 30); outstr(ps->cmd, out); if (mode & SHOW_MULTILINE) { if (procno > 0) { outc(' ', out); outc('|', out); } } else { while (--procno >= 0) outfmt(out, " | %s", (++ps)->cmd ); } outc('\n', out); } flushout(out); jp->changed = 0; if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) freejob(jp);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -