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