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