[BACK]Return to jobs.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / bin / sh

Annotation of src/bin/sh/jobs.c, Revision 1.92

1.92    ! kre         1: /*     $NetBSD: jobs.c,v 1.91 2017/10/23 10:52:07 kre Exp $    */
1.15      cgd         2:
1.1       cgd         3: /*-
1.8       jtc         4:  * Copyright (c) 1991, 1993
                      5:  *     The Regents of the University of California.  All rights reserved.
1.1       cgd         6:  *
                      7:  * This code is derived from software contributed to Berkeley by
                      8:  * Kenneth Almquist.
                      9:  *
                     10:  * Redistribution and use in source and binary forms, with or without
                     11:  * modification, are permitted provided that the following conditions
                     12:  * are met:
                     13:  * 1. Redistributions of source code must retain the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer.
                     15:  * 2. Redistributions in binary form must reproduce the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer in the
                     17:  *    documentation and/or other materials provided with the distribution.
1.58      agc        18:  * 3. Neither the name of the University nor the names of its contributors
1.1       cgd        19:  *    may be used to endorse or promote products derived from this software
                     20:  *    without specific prior written permission.
                     21:  *
                     22:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     23:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     24:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     25:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     26:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     27:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     28:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     29:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     30:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     31:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     32:  * SUCH DAMAGE.
                     33:  */
                     34:
1.22      christos   35: #include <sys/cdefs.h>
1.1       cgd        36: #ifndef lint
1.15      cgd        37: #if 0
1.16      christos   38: static char sccsid[] = "@(#)jobs.c     8.5 (Berkeley) 5/4/95";
1.15      cgd        39: #else
1.92    ! kre        40: __RCSID("$NetBSD: jobs.c,v 1.91 2017/10/23 10:52:07 kre Exp $");
1.15      cgd        41: #endif
1.1       cgd        42: #endif /* not lint */
                     43:
1.16      christos   44: #include <fcntl.h>
                     45: #include <signal.h>
                     46: #include <errno.h>
                     47: #include <unistd.h>
                     48: #include <stdlib.h>
1.26      fair       49: #include <paths.h>
1.16      christos   50: #include <sys/types.h>
                     51: #include <sys/param.h>
                     52: #ifdef BSD
                     53: #include <sys/wait.h>
                     54: #include <sys/time.h>
                     55: #include <sys/resource.h>
                     56: #endif
1.19      christos   57: #include <sys/ioctl.h>
1.16      christos   58:
1.1       cgd        59: #include "shell.h"
                     60: #if JOBS
1.19      christos   61: #if OLD_TTY_DRIVER
1.1       cgd        62: #include "sgtty.h"
1.19      christos   63: #else
                     64: #include <termios.h>
                     65: #endif
1.1       cgd        66: #undef CEOF                    /* syntax.h redefines this */
                     67: #endif
1.16      christos   68: #include "redir.h"
                     69: #include "show.h"
1.1       cgd        70: #include "main.h"
                     71: #include "parser.h"
                     72: #include "nodes.h"
                     73: #include "jobs.h"
                     74: #include "options.h"
1.69      christos   75: #include "builtins.h"
1.1       cgd        76: #include "trap.h"
                     77: #include "syntax.h"
                     78: #include "input.h"
                     79: #include "output.h"
                     80: #include "memalloc.h"
                     81: #include "error.h"
                     82: #include "mystring.h"
                     83:
                     84:
1.91      kre        85: #ifndef        WCONTINUED
                     86: #define        WCONTINUED 0            /* So we can compile on old systems */
                     87: #endif
                     88: #ifndef        WIFCONTINUED
                     89: #define        WIFCONTINUED(x) (0)             /* ditto */
                     90: #endif
                     91:
                     92:
1.55      christos   93: static struct job *jobtab;             /* array of jobs */
                     94: static int njobs;                      /* size of array */
                     95: static int jobs_invalid;               /* set in child */
                     96: MKINIT pid_t backgndpid = -1;  /* pid of last background process */
1.1       cgd        97: #if JOBS
                     98: int initialpgrp;               /* pgrp of shell on invocation */
1.55      christos   99: static int curjob = -1;                /* current job */
1.1       cgd       100: #endif
1.44      christos  101: static int ttyfd = -1;
1.1       cgd       102:
1.55      christos  103: STATIC void restartjob(struct job *);
                    104: STATIC void freejob(struct job *);
                    105: STATIC struct job *getjob(const char *, int);
                    106: STATIC int dowait(int, struct job *);
1.68      christos  107: #define WBLOCK 1
                    108: #define WNOFREE 2
1.91      kre       109: #define WSILENT 4
1.90      kre       110: STATIC int jobstatus(const struct job *, int);
1.55      christos  111: STATIC int waitproc(int, struct job *, int *);
                    112: STATIC void cmdtxt(union node *);
                    113: STATIC void cmdlist(union node *, int);
                    114: STATIC void cmdputs(const char *);
1.78      kre       115: inline static void cmdputi(int);
1.1       cgd       116:
1.66      dholland  117: #ifdef SYSV
                    118: STATIC int onsigchild(void);
                    119: #endif
                    120:
1.44      christos  121: #ifdef OLD_TTY_DRIVER
1.55      christos  122: static pid_t tcgetpgrp(int fd);
                    123: static int tcsetpgrp(int fd, pid_t pgrp);
1.44      christos  124:
                    125: static pid_t
1.55      christos  126: tcgetpgrp(int fd)
1.44      christos  127: {
                    128:        pid_t pgrp;
                    129:        if (ioctl(fd, TIOCGPGRP, (char *)&pgrp) == -1)
                    130:                return -1;
                    131:        else
                    132:                return pgrp;
                    133: }
                    134:
                    135: static int
1.55      christos  136: tcsetpgrp(int fd, pid_tpgrp)
1.44      christos  137: {
                    138:        return ioctl(fd, TIOCSPGRP, (char *)&pgrp);
                    139: }
                    140: #endif
1.8       jtc       141:
1.80      kre       142: static void
                    143: ttyfd_change(int from, int to)
                    144: {
                    145:        if (ttyfd == from)
                    146:                ttyfd = to;
                    147: }
                    148:
1.1       cgd       149: /*
                    150:  * Turn job control on and off.
                    151:  *
                    152:  * Note:  This code assumes that the third arg to ioctl is a character
                    153:  * pointer, which is true on Berkeley systems but not System V.  Since
                    154:  * System V doesn't have job control yet, this isn't a problem now.
                    155:  */
                    156:
                    157: MKINIT int jobctl;
                    158:
                    159: void
1.55      christos  160: setjobctl(int on)
1.13      cgd       161: {
1.8       jtc       162: #ifdef OLD_TTY_DRIVER
1.1       cgd       163:        int ldisc;
1.8       jtc       164: #endif
1.1       cgd       165:
                    166:        if (on == jobctl || rootshell == 0)
                    167:                return;
                    168:        if (on) {
1.44      christos  169: #if defined(FIOCLEX) || defined(FD_CLOEXEC)
1.57      christos  170:                int i;
1.80      kre       171:
1.44      christos  172:                if (ttyfd != -1)
1.80      kre       173:                        sh_close(ttyfd);
1.45      christos  174:                if ((ttyfd = open("/dev/tty", O_RDWR)) == -1) {
                    175:                        for (i = 0; i < 3; i++) {
                    176:                                if (isatty(i) && (ttyfd = dup(i)) != -1)
                    177:                                        break;
                    178:                        }
                    179:                        if (i == 3)
                    180:                                goto out;
                    181:                }
1.76      christos  182:                ttyfd = to_upper_fd(ttyfd);     /* Move to a high fd */
1.80      kre       183:                register_sh_fd(ttyfd, ttyfd_change);
1.18      mycroft   184: #else
1.44      christos  185:                out2str("sh: Need FIOCLEX or FD_CLOEXEC to support job control");
                    186:                goto out;
1.18      mycroft   187: #endif
1.44      christos  188:                do { /* while we are in the background */
                    189:                        if ((initialpgrp = tcgetpgrp(ttyfd)) < 0) {
1.80      kre       190:  out:
1.8       jtc       191:                                out2str("sh: can't access tty; job control turned off\n");
                    192:                                mflag = 0;
1.1       cgd       193:                                return;
                    194:                        }
                    195:                        if (initialpgrp == -1)
1.10      jtc       196:                                initialpgrp = getpgrp();
                    197:                        else if (initialpgrp != getpgrp()) {
1.44      christos  198:                                killpg(0, SIGTTIN);
1.1       cgd       199:                                continue;
                    200:                        }
                    201:                } while (0);
1.44      christos  202:
1.8       jtc       203: #ifdef OLD_TTY_DRIVER
1.44      christos  204:                if (ioctl(ttyfd, TIOCGETD, (char *)&ldisc) < 0
                    205:                    || ldisc != NTTYDISC) {
1.8       jtc       206:                        out2str("sh: need new tty driver to run job control; job control turned off\n");
                    207:                        mflag = 0;
1.1       cgd       208:                        return;
                    209:                }
1.8       jtc       210: #endif
1.47      christos  211:                setsignal(SIGTSTP, 0);
                    212:                setsignal(SIGTTOU, 0);
                    213:                setsignal(SIGTTIN, 0);
1.64      tv        214:                if (getpgrp() != rootpid && setpgid(0, rootpid) == -1)
1.47      christos  215:                        error("Cannot set process group (%s) at %d",
                    216:                            strerror(errno), __LINE__);
                    217:                if (tcsetpgrp(ttyfd, rootpid) == -1)
                    218:                        error("Cannot set tty process group (%s) at %d",
                    219:                            strerror(errno), __LINE__);
1.1       cgd       220:        } else { /* turning job control off */
1.64      tv        221:                if (getpgrp() != initialpgrp && setpgid(0, initialpgrp) == -1)
1.47      christos  222:                        error("Cannot set process group (%s) at %d",
                    223:                            strerror(errno), __LINE__);
                    224:                if (tcsetpgrp(ttyfd, initialpgrp) == -1)
                    225:                        error("Cannot set tty process group (%s) at %d",
                    226:                            strerror(errno), __LINE__);
1.80      kre       227:                sh_close(ttyfd);
1.44      christos  228:                ttyfd = -1;
1.47      christos  229:                setsignal(SIGTSTP, 0);
                    230:                setsignal(SIGTTOU, 0);
                    231:                setsignal(SIGTTIN, 0);
1.1       cgd       232:        }
                    233:        jobctl = on;
                    234: }
                    235:
                    236:
                    237: #ifdef mkinit
1.16      christos  238: INCLUDE <stdlib.h>
1.1       cgd       239:
                    240: SHELLPROC {
                    241:        backgndpid = -1;
                    242: #if JOBS
                    243:        jobctl = 0;
                    244: #endif
                    245: }
                    246:
                    247: #endif
                    248:
                    249:
                    250:
                    251: #if JOBS
1.71      dsl       252: static int
                    253: do_fgcmd(const char *arg_ptr)
1.13      cgd       254: {
1.1       cgd       255:        struct job *jp;
1.47      christos  256:        int i;
1.1       cgd       257:        int status;
                    258:
1.91      kre       259:        if (jobs_invalid)
                    260:                error("No current jobs");
1.71      dsl       261:        jp = getjob(arg_ptr, 0);
1.1       cgd       262:        if (jp->jobctl == 0)
                    263:                error("job not created under job control");
1.55      christos  264:        out1fmt("%s", jp->ps[0].cmd);
                    265:        for (i = 1; i < jp->nprocs; i++)
                    266:                out1fmt(" | %s", jp->ps[i].cmd );
                    267:        out1c('\n');
                    268:        flushall();
1.47      christos  269:
1.48      christos  270:        for (i = 0; i < jp->nprocs; i++)
1.47      christos  271:            if (tcsetpgrp(ttyfd, jp->ps[i].pid) != -1)
                    272:                    break;
                    273:
1.48      christos  274:        if (i >= jp->nprocs) {
1.47      christos  275:                error("Cannot set tty process group (%s) at %d",
                    276:                    strerror(errno), __LINE__);
                    277:        }
1.1       cgd       278:        restartjob(jp);
                    279:        INTOFF;
                    280:        status = waitforjob(jp);
                    281:        INTON;
                    282:        return status;
                    283: }
                    284:
1.71      dsl       285: int
                    286: fgcmd(int argc, char **argv)
                    287: {
                    288:        nextopt("");
                    289:        return do_fgcmd(*argptr);
                    290: }
                    291:
                    292: int
                    293: fgcmd_percent(int argc, char **argv)
                    294: {
                    295:        nextopt("");
                    296:        return do_fgcmd(*argv);
                    297: }
                    298:
1.55      christos  299: static void
                    300: set_curjob(struct job *jp, int mode)
                    301: {
                    302:        struct job *jp1, *jp2;
                    303:        int i, ji;
                    304:
                    305:        ji = jp - jobtab;
                    306:
                    307:        /* first remove from list */
                    308:        if (ji == curjob)
                    309:                curjob = jp->prev_job;
                    310:        else {
                    311:                for (i = 0; i < njobs; i++) {
                    312:                        if (jobtab[i].prev_job != ji)
                    313:                                continue;
                    314:                        jobtab[i].prev_job = jp->prev_job;
                    315:                        break;
                    316:                }
                    317:        }
                    318:
                    319:        /* Then re-insert in correct position */
                    320:        switch (mode) {
                    321:        case 0: /* job being deleted */
                    322:                jp->prev_job = -1;
                    323:                break;
                    324:        case 1: /* newly created job or backgrounded job,
                    325:                   put after all stopped jobs. */
                    326:                if (curjob != -1 && jobtab[curjob].state == JOBSTOPPED) {
                    327:                        for (jp1 = jobtab + curjob; ; jp1 = jp2) {
                    328:                                if (jp1->prev_job == -1)
                    329:                                        break;
                    330:                                jp2 = jobtab + jp1->prev_job;
                    331:                                if (jp2->state != JOBSTOPPED)
                    332:                                        break;
                    333:                        }
                    334:                        jp->prev_job = jp1->prev_job;
                    335:                        jp1->prev_job = ji;
                    336:                        break;
                    337:                }
                    338:                /* FALLTHROUGH */
                    339:        case 2: /* newly stopped job - becomes curjob */
                    340:                jp->prev_job = curjob;
                    341:                curjob = ji;
                    342:                break;
                    343:        }
                    344: }
1.1       cgd       345:
1.13      cgd       346: int
1.55      christos  347: bgcmd(int argc, char **argv)
1.13      cgd       348: {
1.1       cgd       349:        struct job *jp;
1.55      christos  350:        int i;
1.1       cgd       351:
1.55      christos  352:        nextopt("");
1.91      kre       353:        if (jobs_invalid)
                    354:                error("No current jobs");
1.1       cgd       355:        do {
1.55      christos  356:                jp = getjob(*argptr, 0);
1.1       cgd       357:                if (jp->jobctl == 0)
                    358:                        error("job not created under job control");
1.55      christos  359:                set_curjob(jp, 1);
1.56      agc       360:                out1fmt("[%ld] %s", (long)(jp - jobtab + 1), jp->ps[0].cmd);
1.55      christos  361:                for (i = 1; i < jp->nprocs; i++)
                    362:                        out1fmt(" | %s", jp->ps[i].cmd );
                    363:                out1c('\n');
                    364:                flushall();
1.1       cgd       365:                restartjob(jp);
1.55      christos  366:        } while (*argptr && *++argptr);
1.1       cgd       367:        return 0;
                    368: }
                    369:
                    370:
                    371: STATIC void
1.55      christos  372: restartjob(struct job *jp)
1.13      cgd       373: {
1.1       cgd       374:        struct procstat *ps;
                    375:        int i;
                    376:
                    377:        if (jp->state == JOBDONE)
                    378:                return;
                    379:        INTOFF;
1.48      christos  380:        for (i = 0; i < jp->nprocs; i++)
1.47      christos  381:                if (killpg(jp->ps[i].pid, SIGCONT) != -1)
                    382:                        break;
1.48      christos  383:        if (i >= jp->nprocs)
1.47      christos  384:                error("Cannot continue job (%s)", strerror(errno));
1.1       cgd       385:        for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
1.23      christos  386:                if (WIFSTOPPED(ps->status)) {
1.92    ! kre       387:                        VTRACE(DBG_JOBS, (
        !           388:                           "restartjob: [%jd] pid %d status change"
        !           389:                           " from %#x (stopped) to -1 (running)\n",
        !           390:                           (size_t)(jp-jobtab+1), ps->pid, ps->status));
1.1       cgd       391:                        ps->status = -1;
1.55      christos  392:                        jp->state = JOBRUNNING;
1.1       cgd       393:                }
                    394:        }
                    395:        INTON;
                    396: }
                    397: #endif
                    398:
1.78      kre       399: inline static void
1.77      kre       400: cmdputi(int n)
                    401: {
                    402:        char str[20];
                    403:
                    404:        fmtstr(str, sizeof str, "%d", n);
                    405:        cmdputs(str);
                    406: }
                    407:
1.55      christos  408: static void
                    409: showjob(struct output *out, struct job *jp, int mode)
                    410: {
                    411:        int procno;
                    412:        int st;
                    413:        struct procstat *ps;
                    414:        int col;
                    415:        char s[64];
                    416:
                    417: #if JOBS
                    418:        if (mode & SHOW_PGID) {
                    419:                /* just output process (group) id of pipeline */
                    420:                outfmt(out, "%ld\n", (long)jp->ps->pid);
                    421:                return;
                    422:        }
                    423: #endif
                    424:
                    425:        procno = jp->nprocs;
                    426:        if (!procno)
                    427:                return;
                    428:
                    429:        if (mode & SHOW_PID)
                    430:                mode |= SHOW_MULTILINE;
                    431:
                    432:        if ((procno > 1 && !(mode & SHOW_MULTILINE))
                    433:            || (mode & SHOW_SIGNALLED)) {
                    434:                /* See if we have more than one status to report */
                    435:                ps = jp->ps;
                    436:                st = ps->status;
                    437:                do {
                    438:                        int st1 = ps->status;
                    439:                        if (st1 != st)
                    440:                                /* yes - need multi-line output */
                    441:                                mode |= SHOW_MULTILINE;
                    442:                        if (st1 == -1 || !(mode & SHOW_SIGNALLED) || WIFEXITED(st1))
                    443:                                continue;
                    444:                        if (WIFSTOPPED(st1) || ((st1 = WTERMSIG(st1) & 0x7f)
                    445:                            && st1 != SIGINT && st1 != SIGPIPE))
                    446:                                mode |= SHOW_ISSIG;
                    447:
                    448:                } while (ps++, --procno);
                    449:                procno = jp->nprocs;
                    450:        }
                    451:
                    452:        if (mode & SHOW_SIGNALLED && !(mode & SHOW_ISSIG)) {
                    453:                if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) {
1.87      kre       454:                        VTRACE(DBG_JOBS, ("showjob: freeing job %d\n",
                    455:                            jp - jobtab + 1));
1.55      christos  456:                        freejob(jp);
                    457:                }
                    458:                return;
                    459:        }
                    460:
                    461:        for (ps = jp->ps; --procno >= 0; ps++) {        /* for each process */
                    462:                if (ps == jp->ps)
1.56      agc       463:                        fmtstr(s, 16, "[%ld] %c ",
                    464:                                (long)(jp - jobtab + 1),
1.55      christos  465: #if JOBS
                    466:                                jp == jobtab + curjob ? '+' :
                    467:                                curjob != -1 && jp == jobtab +
                    468:                                            jobtab[curjob].prev_job ? '-' :
                    469: #endif
                    470:                                ' ');
                    471:                else
                    472:                        fmtstr(s, 16, "      " );
                    473:                col = strlen(s);
                    474:                if (mode & SHOW_PID) {
                    475:                        fmtstr(s + col, 16, "%ld ", (long)ps->pid);
                    476:                             col += strlen(s + col);
                    477:                }
                    478:                if (ps->status == -1) {
                    479:                        scopy("Running", s + col);
                    480:                } else if (WIFEXITED(ps->status)) {
                    481:                        st = WEXITSTATUS(ps->status);
                    482:                        if (st)
                    483:                                fmtstr(s + col, 16, "Done(%d)", st);
                    484:                        else
                    485:                                fmtstr(s + col, 16, "Done");
                    486:                } else {
                    487: #if JOBS
1.91      kre       488:                        if (WIFSTOPPED(ps->status))
1.55      christos  489:                                st = WSTOPSIG(ps->status);
                    490:                        else /* WIFSIGNALED(ps->status) */
                    491: #endif
                    492:                                st = WTERMSIG(ps->status);
                    493:                        st &= 0x7f;
                    494:                        if (st < NSIG && sys_siglist[st])
                    495:                                scopyn(sys_siglist[st], s + col, 32);
                    496:                        else
                    497:                                fmtstr(s + col, 16, "Signal %d", st);
                    498:                        if (WCOREDUMP(ps->status)) {
                    499:                                col += strlen(s + col);
                    500:                                scopyn(" (core dumped)", s + col,  64 - col);
                    501:                        }
                    502:                }
                    503:                col += strlen(s + col);
                    504:                outstr(s, out);
                    505:                do {
                    506:                        outc(' ', out);
                    507:                        col++;
                    508:                } while (col < 30);
                    509:                outstr(ps->cmd, out);
                    510:                if (mode & SHOW_MULTILINE) {
                    511:                        if (procno > 0) {
                    512:                                outc(' ', out);
                    513:                                outc('|', out);
                    514:                        }
                    515:                } else {
                    516:                        while (--procno >= 0)
                    517:                                outfmt(out, " | %s", (++ps)->cmd );
                    518:                }
                    519:                outc('\n', out);
                    520:        }
                    521:        flushout(out);
                    522:        jp->changed = 0;
                    523:        if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE))
                    524:                freejob(jp);
                    525: }
                    526:
1.1       cgd       527:
                    528: int
1.55      christos  529: jobscmd(int argc, char **argv)
1.13      cgd       530: {
1.55      christos  531:        int mode, m;
                    532:
                    533:        mode = 0;
                    534:        while ((m = nextopt("lp")))
                    535:                if (m == 'l')
                    536:                        mode = SHOW_PID;
                    537:                else
                    538:                        mode = SHOW_PGID;
1.91      kre       539:        if (!iflag)
                    540:                mode |= SHOW_NO_FREE;
1.55      christos  541:        if (*argptr)
                    542:                do
                    543:                        showjob(out1, getjob(*argptr,0), mode);
                    544:                while (*++argptr);
                    545:        else
                    546:                showjobs(out1, mode);
1.1       cgd       547:        return 0;
                    548: }
                    549:
                    550:
                    551: /*
                    552:  * Print a list of jobs.  If "change" is nonzero, only print jobs whose
                    553:  * statuses have changed since the last call to showjobs.
                    554:  *
                    555:  * If the shell is interrupted in the process of creating a job, the
                    556:  * result may be a job structure containing zero processes.  Such structures
                    557:  * will be freed here.
                    558:  */
                    559:
                    560: void
1.55      christos  561: showjobs(struct output *out, int mode)
1.13      cgd       562: {
1.1       cgd       563:        int jobno;
                    564:        struct job *jp;
1.47      christos  565:        int silent = 0, gotpid;
1.1       cgd       566:
1.87      kre       567:        CTRACE(DBG_JOBS, ("showjobs(%x) called\n", mode));
1.47      christos  568:
                    569:        /* If not even one one job changed, there is nothing to do */
1.91      kre       570:        gotpid = dowait(WSILENT, NULL);
                    571:        while (dowait(WSILENT, NULL) > 0)
1.47      christos  572:                continue;
                    573: #ifdef JOBS
                    574:        /*
                    575:         * Check if we are not in our foreground group, and if not
                    576:         * put us in it.
                    577:         */
1.54      christos  578:        if (mflag && gotpid != -1 && tcgetpgrp(ttyfd) != getpid()) {
1.47      christos  579:                if (tcsetpgrp(ttyfd, getpid()) == -1)
                    580:                        error("Cannot set tty process group (%s) at %d",
                    581:                            strerror(errno), __LINE__);
1.87      kre       582:                VTRACE(DBG_JOBS|DBG_INPUT, ("repaired tty process group\n"));
1.47      christos  583:                silent = 1;
                    584:        }
                    585: #endif
1.55      christos  586:        if (jobs_invalid)
                    587:                return;
                    588:
1.1       cgd       589:        for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
1.55      christos  590:                if (!jp->used)
1.1       cgd       591:                        continue;
                    592:                if (jp->nprocs == 0) {
                    593:                        freejob(jp);
                    594:                        continue;
                    595:                }
1.55      christos  596:                if ((mode & SHOW_CHANGED) && !jp->changed)
1.1       cgd       597:                        continue;
1.47      christos  598:                if (silent && jp->changed) {
                    599:                        jp->changed = 0;
                    600:                        continue;
                    601:                }
1.55      christos  602:                showjob(out, jp, mode);
1.1       cgd       603:        }
                    604: }
                    605:
                    606: /*
                    607:  * Mark a job structure as unused.
                    608:  */
                    609:
                    610: STATIC void
1.55      christos  611: freejob(struct job *jp)
                    612: {
1.1       cgd       613:        INTOFF;
1.41      christos  614:        if (jp->ps != &jp->ps0) {
1.1       cgd       615:                ckfree(jp->ps);
1.41      christos  616:                jp->ps = &jp->ps0;
                    617:        }
                    618:        jp->nprocs = 0;
1.1       cgd       619:        jp->used = 0;
                    620: #if JOBS
1.55      christos  621:        set_curjob(jp, 0);
1.1       cgd       622: #endif
                    623:        INTON;
                    624: }
                    625:
1.90      kre       626: /*
                    627:  * Extract the status of a completed job (for $?)
                    628:  */
                    629: STATIC int
                    630: jobstatus(const struct job *jp, int raw)
                    631: {
                    632:        int status = 0;
                    633:        int retval;
                    634:
                    635:        if (pipefail && jp->nprocs) {
                    636:                int i;
                    637:
                    638:                for (i = 0; i < jp->nprocs; i++)
                    639:                        if (jp->ps[i].status != 0)
                    640:                                status = jp->ps[i].status;
                    641:        } else
                    642:                status = jp->ps[jp->nprocs ? jp->nprocs - 1 : 0].status;
                    643:
                    644:        if (raw)
                    645:                return status;
                    646:
                    647:        if (WIFEXITED(status))
                    648:                retval = WEXITSTATUS(status);
                    649: #if JOBS
                    650:        else if (WIFSTOPPED(status))
                    651:                retval = WSTOPSIG(status) + 128;
                    652: #endif
                    653:        else {
                    654:                /* XXX: limits number of signals */
                    655:                retval = WTERMSIG(status) + 128;
                    656:        }
                    657:
                    658:        return retval;
                    659: }
                    660:
1.1       cgd       661:
                    662:
                    663: int
1.55      christos  664: waitcmd(int argc, char **argv)
1.13      cgd       665: {
1.1       cgd       666:        struct job *job;
1.90      kre       667:        int retval;
1.1       cgd       668:        struct job *jp;
                    669:
1.55      christos  670:        nextopt("");
                    671:
1.91      kre       672:        /*
                    673:         * If we have forked, and not yet created any new jobs, then
                    674:         * we have no children, whatever jobtab claims,
                    675:         * so simply return in that case.
                    676:         *
                    677:         * The return code is 127 if we had any pid args (none are found)
                    678:         * but 0 for plain old "wait".
                    679:         */
                    680:        if (jobs_invalid)
                    681:                return *argptr ? 127 : 0;
                    682:
1.55      christos  683:        if (!*argptr) {
                    684:                /* wait for all jobs */
                    685:                jp = jobtab;
                    686:                for (;;) {
                    687:                        if (jp >= jobtab + njobs) {
                    688:                                /* no running procs */
                    689:                                return 0;
                    690:                        }
                    691:                        if (!jp->used || jp->state != JOBRUNNING) {
                    692:                                jp++;
                    693:                                continue;
                    694:                        }
1.68      christos  695:                        if (dowait(WBLOCK, NULL) == -1)
1.75      christos  696:                               return 128 + lastsig();
1.55      christos  697:                        jp = jobtab;
                    698:                }
1.1       cgd       699:        }
1.55      christos  700:
1.63      lukem     701:        retval = 127;           /* XXXGCC: -Wuninitialized */
1.55      christos  702:        for (; *argptr; argptr++) {
                    703:                job = getjob(*argptr, 1);
                    704:                if (!job) {
                    705:                        retval = 127;
                    706:                        continue;
                    707:                }
                    708:                /* loop until process terminated or stopped */
                    709:                while (job->state == JOBRUNNING) {
1.68      christos  710:                        if (dowait(WBLOCK|WNOFREE, job) == -1)
1.75      christos  711:                               return 128 + lastsig();
1.55      christos  712:                }
1.90      kre       713:                retval = jobstatus(job, 0);
1.55      christos  714:                if (!iflag)
                    715:                        freejob(job);
1.1       cgd       716:        }
1.55      christos  717:        return retval;
1.1       cgd       718: }
                    719:
                    720:
                    721:
1.13      cgd       722: int
1.55      christos  723: jobidcmd(int argc, char **argv)
1.13      cgd       724: {
1.1       cgd       725:        struct job *jp;
                    726:        int i;
1.92    ! kre       727:        int pg = 0, onep = 0, job = 0;
        !           728:
        !           729:        while ((i = nextopt("gjp"))) {
        !           730:                switch (i) {
        !           731:                case 'g':       pg = 1;         break;
        !           732:                case 'j':       job = 1;        break;
        !           733:                case 'p':       onep = 1;       break;
        !           734:                }
        !           735:        }
        !           736:        CTRACE(DBG_JOBS, ("jobidcmd%s%s%s%s %s\n", pg ? " -g" : "",
        !           737:            onep ? " -p" : "", job ? " -j" : "", jobs_invalid ? " [inv]" : "",
        !           738:            *argptr ? *argptr : "<implicit %%>"));
        !           739:        if (pg + onep + job > 1)
        !           740:                error("-g -j and -p options cannot be combined");
        !           741:
        !           742:        if (argptr[0] && argptr[1])
        !           743:                error("usage: jobid [-g|-p|-r] jobid");
1.1       cgd       744:
1.55      christos  745:        jp = getjob(*argptr, 0);
1.92    ! kre       746:        if (job) {
        !           747:                out1fmt("%%%jd\n", (size_t)(jp - jobtab + 1));
        !           748:                return 0;
        !           749:        }
        !           750:        if (pg) {
        !           751:                if (jp->pgrp != 0) {
        !           752:                        out1fmt("%ld\n", (long)jp->pgrp);
        !           753:                        return 0;
        !           754:                }
        !           755:                return 1;
        !           756:        }
        !           757:        if (onep) {
        !           758:                i = jp->nprocs - 1;
        !           759:                if (i < 0)
        !           760:                        return 1;
        !           761:                out1fmt("%ld\n", (long)jp->ps[i].pid);
        !           762:                return 0;
        !           763:        }
1.1       cgd       764:        for (i = 0 ; i < jp->nprocs ; ) {
1.27      christos  765:                out1fmt("%ld", (long)jp->ps[i].pid);
1.55      christos  766:                out1c(++i < jp->nprocs ? ' ' : '\n');
1.1       cgd       767:        }
                    768:        return 0;
                    769: }
                    770:
1.55      christos  771: int
                    772: getjobpgrp(const char *name)
                    773: {
                    774:        struct job *jp;
1.1       cgd       775:
1.91      kre       776:        if (jobs_invalid)
                    777:                error("No such job: %s", name);
1.55      christos  778:        jp = getjob(name, 1);
                    779:        if (jp == 0)
                    780:                return 0;
                    781:        return -jp->ps[0].pid;
                    782: }
1.1       cgd       783:
                    784: /*
                    785:  * Convert a job name to a job structure.
                    786:  */
                    787:
                    788: STATIC struct job *
1.55      christos  789: getjob(const char *name, int noerror)
                    790: {
                    791:        int jobno = -1;
1.21      tls       792:        struct job *jp;
1.1       cgd       793:        int pid;
                    794:        int i;
1.55      christos  795:        const char *err_msg = "No such job: %s";
1.91      kre       796:
1.1       cgd       797:        if (name == NULL) {
                    798: #if JOBS
1.55      christos  799:                jobno = curjob;
1.1       cgd       800: #endif
1.55      christos  801:                err_msg = "No current job";
1.1       cgd       802:        } else if (name[0] == '%') {
1.55      christos  803:                if (is_number(name + 1)) {
                    804:                        jobno = number(name + 1) - 1;
                    805:                } else if (!name[2]) {
                    806:                        switch (name[1]) {
1.1       cgd       807: #if JOBS
1.55      christos  808:                        case 0:
                    809:                        case '+':
                    810:                        case '%':
                    811:                                jobno = curjob;
                    812:                                err_msg = "No current job";
                    813:                                break;
                    814:                        case '-':
                    815:                                jobno = curjob;
                    816:                                if (jobno != -1)
                    817:                                        jobno = jobtab[jobno].prev_job;
                    818:                                err_msg = "No previous job";
                    819:                                break;
1.1       cgd       820: #endif
1.55      christos  821:                        default:
                    822:                                goto check_pattern;
                    823:                        }
1.1       cgd       824:                } else {
1.55      christos  825:                        struct job *found;
                    826:     check_pattern:
                    827:                        found = NULL;
1.1       cgd       828:                        for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
1.55      christos  829:                                if (!jp->used || jp->nprocs <= 0)
                    830:                                        continue;
                    831:                                if ((name[1] == '?'
                    832:                                        && strstr(jp->ps[0].cmd, name + 2))
                    833:                                    || prefix(name + 1, jp->ps[0].cmd)) {
                    834:                                        if (found) {
                    835:                                                err_msg = "%s: ambiguous";
                    836:                                                found = 0;
                    837:                                                break;
                    838:                                        }
1.1       cgd       839:                                        found = jp;
                    840:                                }
                    841:                        }
                    842:                        if (found)
                    843:                                return found;
                    844:                }
1.55      christos  845:
1.1       cgd       846:        } else if (is_number(name)) {
                    847:                pid = number(name);
                    848:                for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
                    849:                        if (jp->used && jp->nprocs > 0
                    850:                         && jp->ps[jp->nprocs - 1].pid == pid)
                    851:                                return jp;
                    852:                }
                    853:        }
1.55      christos  854:
1.91      kre       855:        if (jobno >= 0 && jobno < njobs) {
1.55      christos  856:                jp = jobtab + jobno;
                    857:                if (jp->used)
                    858:                        return jp;
                    859:        }
                    860:        if (!noerror)
                    861:                error(err_msg, name);
                    862:        return 0;
1.1       cgd       863: }
                    864:
                    865:
                    866:
                    867: /*
                    868:  * Return a new job structure,
                    869:  */
                    870:
                    871: struct job *
1.55      christos  872: makejob(union node *node, int nprocs)
1.13      cgd       873: {
1.1       cgd       874:        int i;
                    875:        struct job *jp;
                    876:
1.55      christos  877:        if (jobs_invalid) {
                    878:                for (i = njobs, jp = jobtab ; --i >= 0 ; jp++) {
                    879:                        if (jp->used)
                    880:                                freejob(jp);
                    881:                }
                    882:                jobs_invalid = 0;
                    883:        }
                    884:
1.1       cgd       885:        for (i = njobs, jp = jobtab ; ; jp++) {
                    886:                if (--i < 0) {
                    887:                        INTOFF;
                    888:                        if (njobs == 0) {
                    889:                                jobtab = ckmalloc(4 * sizeof jobtab[0]);
                    890:                        } else {
                    891:                                jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
1.12      mycroft   892:                                memcpy(jp, jobtab, njobs * sizeof jp[0]);
1.17      pk        893:                                /* Relocate `ps' pointers */
                    894:                                for (i = 0; i < njobs; i++)
                    895:                                        if (jp[i].ps == &jobtab[i].ps0)
                    896:                                                jp[i].ps = &jp[i].ps0;
1.1       cgd       897:                                ckfree(jobtab);
                    898:                                jobtab = jp;
                    899:                        }
                    900:                        jp = jobtab + njobs;
1.70      joerg     901:                        for (i = 4 ; --i >= 0 ; )
                    902:                                jobtab[njobs++].used = 0;
1.1       cgd       903:                        INTON;
                    904:                        break;
                    905:                }
                    906:                if (jp->used == 0)
                    907:                        break;
                    908:        }
                    909:        INTOFF;
1.55      christos  910:        jp->state = JOBRUNNING;
1.1       cgd       911:        jp->used = 1;
                    912:        jp->changed = 0;
                    913:        jp->nprocs = 0;
1.92    ! kre       914:        jp->pgrp = 0;
1.1       cgd       915: #if JOBS
                    916:        jp->jobctl = jobctl;
1.55      christos  917:        set_curjob(jp, 1);
1.1       cgd       918: #endif
                    919:        if (nprocs > 1) {
                    920:                jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
                    921:        } else {
                    922:                jp->ps = &jp->ps0;
                    923:        }
                    924:        INTON;
1.87      kre       925:        VTRACE(DBG_JOBS, ("makejob(0x%lx, %d) returns %%%d\n",
                    926:            (long)node, nprocs, jp - jobtab + 1));
1.1       cgd       927:        return jp;
1.19      christos  928: }
1.1       cgd       929:
                    930:
                    931: /*
1.37      lukem     932:  * Fork off a subshell.  If we are doing job control, give the subshell its
1.1       cgd       933:  * own process group.  Jp is a job structure that the job is to be added to.
                    934:  * N is the command that will be evaluated by the child.  Both jp and n may
                    935:  * be NULL.  The mode parameter can be one of the following:
                    936:  *     FORK_FG - Fork off a foreground process.
                    937:  *     FORK_BG - Fork off a background process.
                    938:  *     FORK_NOJOB - Like FORK_FG, but don't give the process its own
                    939:  *                  process group even if job control is on.
                    940:  *
                    941:  * When job control is turned off, background processes have their standard
                    942:  * input redirected to /dev/null (except for the second and later processes
                    943:  * in a pipeline).
                    944:  */
                    945:
                    946: int
1.55      christos  947: forkshell(struct job *jp, union node *n, int mode)
1.13      cgd       948: {
1.72      christos  949:        pid_t pid;
                    950:        int serrno;
1.1       cgd       951:
1.87      kre       952:        CTRACE(DBG_JOBS, ("forkshell(%%%d, %p, %d) called\n",
                    953:            jp - jobtab, n, mode));
                    954:
1.47      christos  955:        switch ((pid = fork())) {
                    956:        case -1:
1.72      christos  957:                serrno = errno;
1.87      kre       958:                VTRACE(DBG_JOBS, ("Fork failed, errno=%d\n", serrno));
1.1       cgd       959:                INTON;
1.72      christos  960:                error("Cannot fork (%s)", strerror(serrno));
1.47      christos  961:                break;
                    962:        case 0:
1.85      kre       963:                SHELL_FORKED();
1.51      christos  964:                forkchild(jp, n, mode, 0);
1.47      christos  965:                return 0;
                    966:        default:
1.51      christos  967:                return forkparent(jp, n, mode, pid);
1.1       cgd       968:        }
1.47      christos  969: }
                    970:
                    971: int
1.55      christos  972: forkparent(struct job *jp, union node *n, int mode, pid_t pid)
1.47      christos  973: {
1.53      mycroft   974:        int pgrp;
1.1       cgd       975:
1.53      mycroft   976:        if (rootshell && mode != FORK_NOJOB && mflag) {
                    977:                if (jp == NULL || jp->nprocs == 0)
                    978:                        pgrp = pid;
                    979:                else
                    980:                        pgrp = jp->ps[0].pid;
1.92    ! kre       981:                jp->pgrp = pgrp;
1.53      mycroft   982:                /* This can fail because we are doing it in the child also */
                    983:                (void)setpgid(pid, pgrp);
                    984:        }
1.1       cgd       985:        if (mode == FORK_BG)
                    986:                backgndpid = pid;               /* set $! */
                    987:        if (jp) {
                    988:                struct procstat *ps = &jp->ps[jp->nprocs++];
                    989:                ps->pid = pid;
                    990:                ps->status = -1;
1.55      christos  991:                ps->cmd[0] = 0;
                    992:                if (/* iflag && rootshell && */ n)
                    993:                        commandtext(ps, n);
1.1       cgd       994:        }
1.89      kre       995:        CTRACE(DBG_JOBS, ("In parent shell: child = %d (mode %d)\n",pid,mode));
1.1       cgd       996:        return pid;
                    997: }
                    998:
1.47      christos  999: void
1.55      christos 1000: forkchild(struct job *jp, union node *n, int mode, int vforked)
1.47      christos 1001: {
                   1002:        int wasroot;
                   1003:        int pgrp;
                   1004:        const char *devnull = _PATH_DEVNULL;
                   1005:        const char *nullerr = "Can't open %s";
                   1006:
1.51      christos 1007:        wasroot = rootshell;
1.91      kre      1008:        CTRACE(DBG_JOBS, ("Child shell %d %sforked from %d (mode %d)\n",
                   1009:            getpid(), vforked?"v":"", getppid(), mode));
1.55      christos 1010:        if (!vforked)
1.51      christos 1011:                rootshell = 0;
1.55      christos 1012:
1.47      christos 1013:        closescript(vforked);
                   1014:        clear_traps(vforked);
                   1015: #if JOBS
                   1016:        if (!vforked)
                   1017:                jobctl = 0;             /* do job control only in root shell */
                   1018:        if (wasroot && mode != FORK_NOJOB && mflag) {
                   1019:                if (jp == NULL || jp->nprocs == 0)
                   1020:                        pgrp = getpid();
                   1021:                else
                   1022:                        pgrp = jp->ps[0].pid;
1.53      mycroft  1023:                /* This can fail because we are doing it in the parent also */
                   1024:                (void)setpgid(0, pgrp);
1.47      christos 1025:                if (mode == FORK_FG) {
                   1026:                        if (tcsetpgrp(ttyfd, pgrp) == -1)
                   1027:                                error("Cannot set tty process group (%s) at %d",
                   1028:                                    strerror(errno), __LINE__);
                   1029:                }
                   1030:                setsignal(SIGTSTP, vforked);
                   1031:                setsignal(SIGTTOU, vforked);
                   1032:        } else if (mode == FORK_BG) {
                   1033:                ignoresig(SIGINT, vforked);
                   1034:                ignoresig(SIGQUIT, vforked);
                   1035:                if ((jp == NULL || jp->nprocs == 0) &&
                   1036:                    ! fd0_redirected_p ()) {
                   1037:                        close(0);
                   1038:                        if (open(devnull, O_RDONLY) != 0)
                   1039:                                error(nullerr, devnull);
                   1040:                }
                   1041:        }
                   1042: #else
                   1043:        if (mode == FORK_BG) {
                   1044:                ignoresig(SIGINT, vforked);
                   1045:                ignoresig(SIGQUIT, vforked);
                   1046:                if ((jp == NULL || jp->nprocs == 0) &&
                   1047:                    ! fd0_redirected_p ()) {
                   1048:                        close(0);
                   1049:                        if (open(devnull, O_RDONLY) != 0)
                   1050:                                error(nullerr, devnull);
                   1051:                }
                   1052:        }
                   1053: #endif
                   1054:        if (wasroot && iflag) {
                   1055:                setsignal(SIGINT, vforked);
                   1056:                setsignal(SIGQUIT, vforked);
                   1057:                setsignal(SIGTERM, vforked);
                   1058:        }
1.55      christos 1059:
                   1060:        if (!vforked)
                   1061:                jobs_invalid = 1;
1.47      christos 1062: }
                   1063:
1.1       cgd      1064: /*
                   1065:  * Wait for job to finish.
                   1066:  *
                   1067:  * Under job control we have the problem that while a child process is
                   1068:  * running interrupts generated by the user are sent to the child but not
                   1069:  * to the shell.  This means that an infinite loop started by an inter-
                   1070:  * active user may be hard to kill.  With job control turned off, an
                   1071:  * interactive user may place an interactive program inside a loop.  If
                   1072:  * the interactive program catches interrupts, the user doesn't want
                   1073:  * these interrupts to also abort the loop.  The approach we take here
                   1074:  * is to have the shell ignore interrupt signals while waiting for a
                   1075:  * forground process to terminate, and then send itself an interrupt
                   1076:  * signal if the child process was terminated by an interrupt signal.
                   1077:  * Unfortunately, some programs want to do a bit of cleanup and then
                   1078:  * exit on interrupt; unless these processes terminate themselves by
                   1079:  * sending a signal to themselves (instead of calling exit) they will
                   1080:  * confuse this approach.
                   1081:  */
                   1082:
                   1083: int
1.55      christos 1084: waitforjob(struct job *jp)
                   1085: {
1.1       cgd      1086: #if JOBS
1.10      jtc      1087:        int mypgrp = getpgrp();
1.1       cgd      1088: #endif
                   1089:        int status;
                   1090:        int st;
                   1091:
                   1092:        INTOFF;
1.87      kre      1093:        VTRACE(DBG_JOBS, ("waitforjob(%%%d) called\n", jp - jobtab + 1));
1.55      christos 1094:        while (jp->state == JOBRUNNING) {
1.68      christos 1095:                dowait(WBLOCK, jp);
1.1       cgd      1096:        }
                   1097: #if JOBS
                   1098:        if (jp->jobctl) {
1.47      christos 1099:                if (tcsetpgrp(ttyfd, mypgrp) == -1)
                   1100:                        error("Cannot set tty process group (%s) at %d",
                   1101:                            strerror(errno), __LINE__);
1.1       cgd      1102:        }
1.55      christos 1103:        if (jp->state == JOBSTOPPED && curjob != jp - jobtab)
                   1104:                set_curjob(jp, 2);
1.1       cgd      1105: #endif
1.90      kre      1106:        status = jobstatus(jp, 1);
                   1107:
1.1       cgd      1108:        /* convert to 8 bits */
1.23      christos 1109:        if (WIFEXITED(status))
                   1110:                st = WEXITSTATUS(status);
1.1       cgd      1111: #if JOBS
1.23      christos 1112:        else if (WIFSTOPPED(status))
                   1113:                st = WSTOPSIG(status) + 128;
1.1       cgd      1114: #endif
                   1115:        else
1.23      christos 1116:                st = WTERMSIG(status) + 128;
1.87      kre      1117:
                   1118:        VTRACE(DBG_JOBS, ("waitforjob: job %d, nproc %d, status %d, st %x\n",
                   1119:                jp - jobtab + 1, jp->nprocs, status, st));
1.32      mycroft  1120: #if JOBS
                   1121:        if (jp->jobctl) {
                   1122:                /*
                   1123:                 * This is truly gross.
                   1124:                 * If we're doing job control, then we did a TIOCSPGRP which
                   1125:                 * caused us (the shell) to no longer be in the controlling
                   1126:                 * session -- so we wouldn't have seen any ^C/SIGINT.  So, we
                   1127:                 * intuit from the subprocess exit status whether a SIGINT
1.40      wiz      1128:                 * occurred, and if so interrupt ourselves.  Yuck.  - mycroft
1.32      mycroft  1129:                 */
                   1130:                if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
                   1131:                        raise(SIGINT);
                   1132:        }
                   1133: #endif
1.1       cgd      1134:        if (! JOBS || jp->state == JOBDONE)
                   1135:                freejob(jp);
                   1136:        INTON;
                   1137:        return st;
                   1138: }
                   1139:
                   1140:
                   1141:
                   1142: /*
                   1143:  * Wait for a process to terminate.
                   1144:  */
                   1145:
                   1146: STATIC int
1.68      christos 1147: dowait(int flags, struct job *job)
1.13      cgd      1148: {
1.1       cgd      1149:        int pid;
                   1150:        int status;
                   1151:        struct procstat *sp;
                   1152:        struct job *jp;
                   1153:        struct job *thisjob;
                   1154:        int done;
                   1155:        int stopped;
                   1156:
1.87      kre      1157:        VTRACE(DBG_JOBS|DBG_PROCS, ("dowait(%x) called\n", flags));
1.1       cgd      1158:        do {
1.68      christos 1159:                pid = waitproc(flags & WBLOCK, job, &status);
1.87      kre      1160:                VTRACE(DBG_JOBS|DBG_PROCS, ("wait returns pid %d, status %#x\n",
                   1161:                    pid, status));
1.74      christos 1162:        } while (pid == -1 && errno == EINTR && pendingsigs == 0);
1.1       cgd      1163:        if (pid <= 0)
                   1164:                return pid;
                   1165:        INTOFF;
                   1166:        thisjob = NULL;
                   1167:        for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
                   1168:                if (jp->used) {
                   1169:                        done = 1;
                   1170:                        stopped = 1;
                   1171:                        for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
                   1172:                                if (sp->pid == -1)
                   1173:                                        continue;
1.91      kre      1174:                                if (sp->pid == pid &&
                   1175:                                  (sp->status==-1 || WIFSTOPPED(sp->status))) {
1.87      kre      1176:                                        VTRACE(DBG_JOBS | DBG_PROCS,
                   1177:                        ("Job %d: changing status of proc %d from %#x to %#x\n",
                   1178:                                                jp - jobtab + 1, pid,
                   1179:                                                sp->status, status));
1.91      kre      1180:                                        if (WIFCONTINUED(status)) {
                   1181:                                                if (sp->status != -1)
                   1182:                                                        jp->changed = 1;
                   1183:                                                sp->status = -1;
                   1184:                                                jp->state = 0;
                   1185:                                        } else
                   1186:                                                sp->status = status;
1.1       cgd      1187:                                        thisjob = jp;
                   1188:                                }
                   1189:                                if (sp->status == -1)
                   1190:                                        stopped = 0;
1.23      christos 1191:                                else if (WIFSTOPPED(sp->status))
1.1       cgd      1192:                                        done = 0;
                   1193:                        }
                   1194:                        if (stopped) {          /* stopped or done */
1.55      christos 1195:                                int state = done ? JOBDONE : JOBSTOPPED;
1.91      kre      1196:
1.1       cgd      1197:                                if (jp->state != state) {
1.87      kre      1198:                                        VTRACE(DBG_JOBS,
                   1199:                                ("Job %d: changing state from %d to %d\n",
                   1200:                                            jp - jobtab + 1, jp->state, state));
1.1       cgd      1201:                                        jp->state = state;
                   1202: #if JOBS
1.55      christos 1203:                                        if (done)
                   1204:                                                set_curjob(jp, 0);
1.1       cgd      1205: #endif
                   1206:                                }
                   1207:                        }
                   1208:                }
                   1209:        }
1.23      christos 1210:
1.91      kre      1211:        if (thisjob && (thisjob->state != JOBRUNNING || thisjob->changed)) {
1.55      christos 1212:                int mode = 0;
1.91      kre      1213:
1.55      christos 1214:                if (!rootshell || !iflag)
                   1215:                        mode = SHOW_SIGNALLED;
1.68      christos 1216:                if ((job == thisjob && (flags & WNOFREE) == 0) ||
1.91      kre      1217:                    job != thisjob)
1.55      christos 1218:                        mode = SHOW_SIGNALLED | SHOW_NO_FREE;
1.91      kre      1219:                if (mode && (flags & WSILENT) == 0)
1.55      christos 1220:                        showjob(out2, thisjob, mode);
                   1221:                else {
1.87      kre      1222:                        VTRACE(DBG_JOBS,
                   1223:                            ("Not printing status, rootshell=%d, job=%p\n",
                   1224:                            rootshell, job));
1.55      christos 1225:                        thisjob->changed = 1;
1.1       cgd      1226:                }
                   1227:        }
1.55      christos 1228:
1.47      christos 1229:        INTON;
1.1       cgd      1230:        return pid;
                   1231: }
                   1232:
                   1233:
                   1234:
                   1235: /*
                   1236:  * Do a wait system call.  If job control is compiled in, we accept
                   1237:  * stopped processes.  If block is zero, we return a value of zero
                   1238:  * rather than blocking.
                   1239:  *
                   1240:  * System V doesn't have a non-blocking wait system call.  It does
1.65      snj      1241:  * have a SIGCLD signal that is sent to a process when one of its
1.1       cgd      1242:  * children dies.  The obvious way to use SIGCLD would be to install
                   1243:  * a handler for SIGCLD which simply bumped a counter when a SIGCLD
                   1244:  * was received, and have waitproc bump another counter when it got
                   1245:  * the status of a process.  Waitproc would then know that a wait
                   1246:  * system call would not block if the two counters were different.
                   1247:  * This approach doesn't work because if a process has children that
                   1248:  * have not been waited for, System V will send it a SIGCLD when it
                   1249:  * installs a signal handler for SIGCLD.  What this means is that when
                   1250:  * a child exits, the shell will be sent SIGCLD signals continuously
                   1251:  * until is runs out of stack space, unless it does a wait call before
                   1252:  * restoring the signal handler.  The code below takes advantage of
                   1253:  * this (mis)feature by installing a signal handler for SIGCLD and
                   1254:  * then checking to see whether it was called.  If there are any
                   1255:  * children to be waited for, it will be.
                   1256:  *
                   1257:  * If neither SYSV nor BSD is defined, we don't implement nonblocking
                   1258:  * waits at all.  In this case, the user will not be informed when
                   1259:  * a background process until the next time she runs a real program
                   1260:  * (as opposed to running a builtin command or just typing return),
                   1261:  * and the jobs command may give out of date information.
                   1262:  */
                   1263:
                   1264: #ifdef SYSV
                   1265: STATIC int gotsigchild;
                   1266:
                   1267: STATIC int onsigchild() {
                   1268:        gotsigchild = 1;
                   1269: }
                   1270: #endif
                   1271:
                   1272:
                   1273: STATIC int
1.55      christos 1274: waitproc(int block, struct job *jp, int *status)
1.13      cgd      1275: {
1.1       cgd      1276: #ifdef BSD
1.38      christos 1277:        int flags = 0;
1.1       cgd      1278:
                   1279: #if JOBS
1.91      kre      1280:        if (mflag || (jp != NULL && jp->jobctl))
                   1281:                flags |= WUNTRACED | WCONTINUED;
1.1       cgd      1282: #endif
                   1283:        if (block == 0)
                   1284:                flags |= WNOHANG;
1.91      kre      1285:        VTRACE(DBG_WAIT, ("waitproc: doing waitpid(flags=%#x)\n", flags));
1.64      tv       1286:        return waitpid(-1, status, flags);
1.1       cgd      1287: #else
                   1288: #ifdef SYSV
                   1289:        int (*save)();
                   1290:
                   1291:        if (block == 0) {
                   1292:                gotsigchild = 0;
                   1293:                save = signal(SIGCLD, onsigchild);
                   1294:                signal(SIGCLD, save);
                   1295:                if (gotsigchild == 0)
                   1296:                        return 0;
                   1297:        }
                   1298:        return wait(status);
                   1299: #else
                   1300:        if (block == 0)
                   1301:                return 0;
                   1302:        return wait(status);
                   1303: #endif
                   1304: #endif
                   1305: }
                   1306:
1.8       jtc      1307: /*
                   1308:  * return 1 if there are stopped jobs, otherwise 0
                   1309:  */
                   1310: int job_warning = 0;
                   1311: int
1.55      christos 1312: stoppedjobs(void)
1.8       jtc      1313: {
1.21      tls      1314:        int jobno;
                   1315:        struct job *jp;
1.8       jtc      1316:
1.55      christos 1317:        if (job_warning || jobs_invalid)
1.8       jtc      1318:                return (0);
                   1319:        for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) {
                   1320:                if (jp->used == 0)
                   1321:                        continue;
                   1322:                if (jp->state == JOBSTOPPED) {
                   1323:                        out2str("You have stopped jobs.\n");
                   1324:                        job_warning = 2;
                   1325:                        return (1);
                   1326:                }
                   1327:        }
1.1       cgd      1328:
1.8       jtc      1329:        return (0);
                   1330: }
1.1       cgd      1331:
                   1332: /*
                   1333:  * Return a string identifying a command (to be printed by the
1.55      christos 1334:  * jobs command).
1.1       cgd      1335:  */
                   1336:
                   1337: STATIC char *cmdnextc;
                   1338: STATIC int cmdnleft;
                   1339:
1.55      christos 1340: void
                   1341: commandtext(struct procstat *ps, union node *n)
                   1342: {
                   1343:        int len;
1.1       cgd      1344:
1.55      christos 1345:        cmdnextc = ps->cmd;
1.61      dsl      1346:        if (iflag || mflag || sizeof ps->cmd < 100)
1.55      christos 1347:                len = sizeof(ps->cmd);
                   1348:        else
                   1349:                len = sizeof(ps->cmd) / 10;
                   1350:        cmdnleft = len;
1.1       cgd      1351:        cmdtxt(n);
1.55      christos 1352:        if (cmdnleft <= 0) {
                   1353:                char *p = ps->cmd + len - 4;
                   1354:                p[0] = '.';
                   1355:                p[1] = '.';
                   1356:                p[2] = '.';
                   1357:                p[3] = 0;
                   1358:        } else
                   1359:                *cmdnextc = '\0';
1.87      kre      1360:
                   1361:        VTRACE(DBG_JOBS,
                   1362:            ("commandtext: ps->cmd %x, end %x, left %d\n\t\"%s\"\n",
                   1363:            ps->cmd, cmdnextc, cmdnleft, ps->cmd));
1.1       cgd      1364: }
                   1365:
                   1366:
                   1367: STATIC void
1.55      christos 1368: cmdtxt(union node *n)
                   1369: {
1.1       cgd      1370:        union node *np;
                   1371:        struct nodelist *lp;
1.31      christos 1372:        const char *p;
1.1       cgd      1373:        int i;
                   1374:
1.55      christos 1375:        if (n == NULL || cmdnleft <= 0)
1.8       jtc      1376:                return;
1.1       cgd      1377:        switch (n->type) {
                   1378:        case NSEMI:
                   1379:                cmdtxt(n->nbinary.ch1);
                   1380:                cmdputs("; ");
                   1381:                cmdtxt(n->nbinary.ch2);
                   1382:                break;
                   1383:        case NAND:
                   1384:                cmdtxt(n->nbinary.ch1);
                   1385:                cmdputs(" && ");
                   1386:                cmdtxt(n->nbinary.ch2);
                   1387:                break;
                   1388:        case NOR:
                   1389:                cmdtxt(n->nbinary.ch1);
                   1390:                cmdputs(" || ");
                   1391:                cmdtxt(n->nbinary.ch2);
                   1392:                break;
1.83      kre      1393:        case NDNOT:
                   1394:                cmdputs("! ");
                   1395:                /* FALLTHROUGH */
1.81      kre      1396:        case NNOT:
                   1397:                cmdputs("! ");
                   1398:                cmdtxt(n->nnot.com);
                   1399:                break;
1.1       cgd      1400:        case NPIPE:
                   1401:                for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
                   1402:                        cmdtxt(lp->n);
                   1403:                        if (lp->next)
                   1404:                                cmdputs(" | ");
                   1405:                }
1.79      kre      1406:                if (n->npipe.backgnd)
                   1407:                        cmdputs(" &");
1.1       cgd      1408:                break;
                   1409:        case NSUBSHELL:
                   1410:                cmdputs("(");
                   1411:                cmdtxt(n->nredir.n);
                   1412:                cmdputs(")");
                   1413:                break;
                   1414:        case NREDIR:
                   1415:        case NBACKGND:
                   1416:                cmdtxt(n->nredir.n);
                   1417:                break;
                   1418:        case NIF:
                   1419:                cmdputs("if ");
                   1420:                cmdtxt(n->nif.test);
                   1421:                cmdputs("; then ");
                   1422:                cmdtxt(n->nif.ifpart);
1.55      christos 1423:                if (n->nif.elsepart) {
                   1424:                        cmdputs("; else ");
                   1425:                        cmdtxt(n->nif.elsepart);
                   1426:                }
                   1427:                cmdputs("; fi");
1.1       cgd      1428:                break;
                   1429:        case NWHILE:
                   1430:                cmdputs("while ");
                   1431:                goto until;
                   1432:        case NUNTIL:
                   1433:                cmdputs("until ");
1.80      kre      1434:  until:
1.1       cgd      1435:                cmdtxt(n->nbinary.ch1);
                   1436:                cmdputs("; do ");
                   1437:                cmdtxt(n->nbinary.ch2);
                   1438:                cmdputs("; done");
                   1439:                break;
                   1440:        case NFOR:
                   1441:                cmdputs("for ");
                   1442:                cmdputs(n->nfor.var);
1.55      christos 1443:                cmdputs(" in ");
                   1444:                cmdlist(n->nfor.args, 1);
                   1445:                cmdputs("; do ");
                   1446:                cmdtxt(n->nfor.body);
                   1447:                cmdputs("; done");
1.1       cgd      1448:                break;
                   1449:        case NCASE:
                   1450:                cmdputs("case ");
                   1451:                cmdputs(n->ncase.expr->narg.text);
1.55      christos 1452:                cmdputs(" in ");
                   1453:                for (np = n->ncase.cases; np; np = np->nclist.next) {
                   1454:                        cmdtxt(np->nclist.pattern);
                   1455:                        cmdputs(") ");
                   1456:                        cmdtxt(np->nclist.body);
1.82      kre      1457:                        switch (n->type) {      /* switch (not if) for later */
                   1458:                        case NCLISTCONT:
                   1459:                                cmdputs(";& ");
                   1460:                                break;
                   1461:                        default:
                   1462:                                cmdputs(";; ");
                   1463:                                break;
                   1464:                        }
1.55      christos 1465:                }
                   1466:                cmdputs("esac");
1.1       cgd      1467:                break;
                   1468:        case NDEFUN:
                   1469:                cmdputs(n->narg.text);
1.55      christos 1470:                cmdputs("() { ... }");
1.1       cgd      1471:                break;
                   1472:        case NCMD:
1.55      christos 1473:                cmdlist(n->ncmd.args, 1);
                   1474:                cmdlist(n->ncmd.redirect, 0);
1.79      kre      1475:                if (n->ncmd.backgnd)
                   1476:                        cmdputs(" &");
1.1       cgd      1477:                break;
                   1478:        case NARG:
                   1479:                cmdputs(n->narg.text);
                   1480:                break;
                   1481:        case NTO:
                   1482:                p = ">";  i = 1;  goto redir;
1.46      christos 1483:        case NCLOBBER:
                   1484:                p = ">|";  i = 1;  goto redir;
1.1       cgd      1485:        case NAPPEND:
                   1486:                p = ">>";  i = 1;  goto redir;
                   1487:        case NTOFD:
                   1488:                p = ">&";  i = 1;  goto redir;
                   1489:        case NFROM:
                   1490:                p = "<";  i = 0;  goto redir;
                   1491:        case NFROMFD:
                   1492:                p = "<&";  i = 0;  goto redir;
1.29      christos 1493:        case NFROMTO:
                   1494:                p = "<>";  i = 0;  goto redir;
1.80      kre      1495:  redir:
1.77      kre      1496:                if (n->nfile.fd != i)
                   1497:                        cmdputi(n->nfile.fd);
1.1       cgd      1498:                cmdputs(p);
                   1499:                if (n->type == NTOFD || n->type == NFROMFD) {
1.77      kre      1500:                        if (n->ndup.dupfd < 0)
                   1501:                                cmdputs("-");
                   1502:                        else
                   1503:                                cmdputi(n->ndup.dupfd);
1.1       cgd      1504:                } else {
                   1505:                        cmdtxt(n->nfile.fname);
                   1506:                }
                   1507:                break;
                   1508:        case NHERE:
                   1509:        case NXHERE:
                   1510:                cmdputs("<<...");
                   1511:                break;
                   1512:        default:
                   1513:                cmdputs("???");
                   1514:                break;
                   1515:        }
                   1516: }
                   1517:
1.55      christos 1518: STATIC void
                   1519: cmdlist(union node *np, int sep)
                   1520: {
                   1521:        for (; np; np = np->narg.next) {
                   1522:                if (!sep)
                   1523:                        cmdputs(" ");
                   1524:                cmdtxt(np);
                   1525:                if (sep && np->narg.next)
                   1526:                        cmdputs(" ");
                   1527:        }
                   1528: }
1.1       cgd      1529:
                   1530:
                   1531: STATIC void
1.55      christos 1532: cmdputs(const char *s)
                   1533: {
                   1534:        const char *p, *str = 0;
                   1535:        char c, cc[2] = " ";
                   1536:        char *nextc;
                   1537:        int nleft;
1.1       cgd      1538:        int subtype = 0;
1.55      christos 1539:        int quoted = 0;
                   1540:        static char vstype[16][4] = { "", "}", "-", "+", "?", "=",
1.84      kre      1541:                                        "#", "##", "%", "%%", "}" };
1.1       cgd      1542:
                   1543:        p = s;
1.55      christos 1544:        nextc = cmdnextc;
                   1545:        nleft = cmdnleft;
                   1546:        while (nleft > 0 && (c = *p++) != 0) {
                   1547:                switch (c) {
1.86      kre      1548:                case CTLNONL:
                   1549:                        c = '\0';
                   1550:                        break;
1.55      christos 1551:                case CTLESC:
                   1552:                        c = *p++;
                   1553:                        break;
                   1554:                case CTLVAR:
1.1       cgd      1555:                        subtype = *p++;
1.86      kre      1556:                        if (subtype & VSLINENO) {       /* undo LINENO hack */
1.84      kre      1557:                                if ((subtype & VSTYPE) == VSLENGTH)
1.86      kre      1558:                                        str = "${#LINENO";      /*}*/
1.84      kre      1559:                                else
1.86      kre      1560:                                        str = "${LINENO";       /*}*/
1.84      kre      1561:                                while (is_digit(*p))
                   1562:                                        p++;
                   1563:                        } else if ((subtype & VSTYPE) == VSLENGTH)
1.86      kre      1564:                                str = "${#"; /*}*/
1.55      christos 1565:                        else
1.86      kre      1566:                                str = "${"; /*}*/
1.55      christos 1567:                        if (!(subtype & VSQUOTE) != !(quoted & 1)) {
                   1568:                                quoted ^= 1;
                   1569:                                c = '"';
1.84      kre      1570:                        } else {
1.55      christos 1571:                                c = *str++;
1.84      kre      1572:                        }
1.55      christos 1573:                        break;
1.86      kre      1574:                case CTLENDVAR:         /*{*/
1.84      kre      1575:                        c = '}';
                   1576:                        if (quoted & 1)
                   1577:                                str = "\"";
1.55      christos 1578:                        quoted >>= 1;
1.1       cgd      1579:                        subtype = 0;
1.55      christos 1580:                        break;
                   1581:                case CTLBACKQ:
                   1582:                        c = '$';
                   1583:                        str = "(...)";
                   1584:                        break;
                   1585:                case CTLBACKQ+CTLQUOTE:
                   1586:                        c = '"';
                   1587:                        str = "$(...)\"";
                   1588:                        break;
                   1589:                case CTLARI:
                   1590:                        c = '$';
1.86      kre      1591:                        if (*p == ' ')
                   1592:                                p++;
                   1593:                        str = "((";     /*))*/
1.55      christos 1594:                        break;
1.86      kre      1595:                case CTLENDARI:         /*((*/
1.55      christos 1596:                        c = ')';
                   1597:                        str = ")";
                   1598:                        break;
                   1599:                case CTLQUOTEMARK:
                   1600:                        quoted ^= 1;
                   1601:                        c = '"';
                   1602:                        break;
1.84      kre      1603:                case CTLQUOTEEND:
                   1604:                        quoted >>= 1;
                   1605:                        c = '"';
                   1606:                        break;
1.55      christos 1607:                case '=':
                   1608:                        if (subtype == 0)
                   1609:                                break;
                   1610:                        str = vstype[subtype & VSTYPE];
                   1611:                        if (subtype & VSNUL)
                   1612:                                c = ':';
                   1613:                        else
1.86      kre      1614:                                c = *str++;             /*{*/
1.55      christos 1615:                        if (c != '}')
                   1616:                                quoted <<= 1;
1.84      kre      1617:                        else if (*p == CTLENDVAR)
                   1618:                                c = *str++;
                   1619:                        subtype = 0;
1.55      christos 1620:                        break;
                   1621:                case '\'':
                   1622:                case '\\':
                   1623:                case '"':
                   1624:                case '$':
                   1625:                        /* These can only happen inside quotes */
                   1626:                        cc[0] = c;
                   1627:                        str = cc;
                   1628:                        c = '\\';
                   1629:                        break;
                   1630:                default:
1.1       cgd      1631:                        break;
                   1632:                }
1.84      kre      1633:                if (c != '\0') do {     /* c == 0 implies nothing in str */
1.55      christos 1634:                        *nextc++ = c;
                   1635:                } while (--nleft > 0 && str && (c = *str++));
                   1636:                str = 0;
                   1637:        }
                   1638:        if ((quoted & 1) && nleft) {
                   1639:                *nextc++ = '"';
                   1640:                nleft--;
1.1       cgd      1641:        }
1.55      christos 1642:        cmdnleft = nleft;
                   1643:        cmdnextc = nextc;
1.1       cgd      1644: }

CVSweb <webmaster@jp.NetBSD.org>