| 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');
 | 
|---|
| 261 |         output_flushall();
 | 
|---|
| 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');
 | 
|---|
| 341 |                 output_flushall();
 | 
|---|
| 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
 | 
|---|
| 451 |                         if (WIFSTOPPED(ps->status))
 | 
|---|
| 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 | }
 | 
|---|