jobs.c revision 218105
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 4. Neither the name of the University nor the names of its contributors
171556Srgrimes *    may be used to endorse or promote products derived from this software
181556Srgrimes *    without specific prior written permission.
191556Srgrimes *
201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301556Srgrimes * SUCH DAMAGE.
311556Srgrimes */
321556Srgrimes
331556Srgrimes#ifndef lint
3436150Scharnier#if 0
3536150Scharnierstatic char sccsid[] = "@(#)jobs.c	8.5 (Berkeley) 5/4/95";
3636150Scharnier#endif
371556Srgrimes#endif /* not lint */
3899110Sobrien#include <sys/cdefs.h>
3999110Sobrien__FBSDID("$FreeBSD: head/bin/sh/jobs.c 218105 2011-01-30 22:57:52Z jilles $");
401556Srgrimes
41213775Sjhb#include <sys/ioctl.h>
42213775Sjhb#include <sys/param.h>
43213775Sjhb#include <sys/resource.h>
44213775Sjhb#include <sys/time.h>
45213775Sjhb#include <sys/wait.h>
46213775Sjhb#include <errno.h>
4717987Speter#include <fcntl.h>
48213775Sjhb#include <paths.h>
4917987Speter#include <signal.h>
50213925Sjilles#include <stddef.h>
51213775Sjhb#include <stdlib.h>
5217987Speter#include <unistd.h>
5317987Speter
541556Srgrimes#include "shell.h"
551556Srgrimes#if JOBS
5617987Speter#include <termios.h>
571556Srgrimes#undef CEOF			/* syntax.h redefines this */
581556Srgrimes#endif
5917987Speter#include "redir.h"
6017987Speter#include "show.h"
611556Srgrimes#include "main.h"
621556Srgrimes#include "parser.h"
631556Srgrimes#include "nodes.h"
641556Srgrimes#include "jobs.h"
651556Srgrimes#include "options.h"
661556Srgrimes#include "trap.h"
671556Srgrimes#include "syntax.h"
681556Srgrimes#include "input.h"
691556Srgrimes#include "output.h"
701556Srgrimes#include "memalloc.h"
711556Srgrimes#include "error.h"
721556Srgrimes#include "mystring.h"
731556Srgrimes
741556Srgrimes
75213760Sobrienstatic struct job *jobtab;	/* array of jobs */
76213760Sobrienstatic int njobs;		/* size of array */
7728346SsteveMKINIT pid_t backgndpid = -1;	/* pid of last background process */
78209600SjillesMKINIT struct job *bgjob = NULL; /* last background process */
791556Srgrimes#if JOBS
80213760Sobrienstatic struct job *jobmru;	/* most recently used job list */
81213760Sobrienstatic pid_t initialpgrp;	/* pgrp of shell on invocation */
821556Srgrimes#endif
8338536Scracauerint in_waitcmd = 0;		/* are we in waitcmd()? */
8438950Scracauerint in_dowait = 0;		/* are we in dowait()? */
8538536Scracauervolatile sig_atomic_t breakwaitcmd = 0;	/* should wait be terminated? */
8699762Stjrstatic int ttyfd = -1;
871556Srgrimes
8820425Ssteve#if JOBS
89213811Sobrienstatic void restartjob(struct job *);
9020425Ssteve#endif
91213811Sobrienstatic void freejob(struct job *);
92213811Sobrienstatic struct job *getjob(char *);
93213811Sobrienstatic pid_t dowait(int, struct job *);
94213811Sobrienstatic pid_t waitproc(int, int *);
95213811Sobrienstatic void checkzombies(void);
96213811Sobrienstatic void cmdtxt(union node *);
97213811Sobrienstatic void cmdputs(const char *);
9897659Stjr#if JOBS
99213811Sobrienstatic void setcurjob(struct job *);
100213811Sobrienstatic void deljob(struct job *);
101213811Sobrienstatic struct job *getcurjob(struct job *);
10297659Stjr#endif
103216217Sjillesstatic void printjobcmd(struct job *);
104216217Sjillesstatic void showjob(struct job *, int);
1051556Srgrimes
1061556Srgrimes
1071556Srgrimes/*
1081556Srgrimes * Turn job control on and off.
1091556Srgrimes */
1101556Srgrimes
1111556SrgrimesMKINIT int jobctl;
1121556Srgrimes
11320425Ssteve#if JOBS
1141556Srgrimesvoid
11590111Simpsetjobctl(int on)
11617987Speter{
11799762Stjr	int i;
1181556Srgrimes
1191556Srgrimes	if (on == jobctl || rootshell == 0)
1201556Srgrimes		return;
1211556Srgrimes	if (on) {
12299762Stjr		if (ttyfd != -1)
12399762Stjr			close(ttyfd);
12499762Stjr		if ((ttyfd = open(_PATH_TTY, O_RDWR)) < 0) {
12599762Stjr			i = 0;
12699762Stjr			while (i <= 2 && !isatty(i))
12799762Stjr				i++;
128109927Stjr			if (i > 2 || (ttyfd = fcntl(i, F_DUPFD, 10)) < 0)
12999762Stjr				goto out;
13099762Stjr		}
131109927Stjr		if (ttyfd < 10) {
132109927Stjr			/*
133109927Stjr			 * Keep our TTY file descriptor out of the way of
134109927Stjr			 * the user's redirections.
135109927Stjr			 */
136109927Stjr			if ((i = fcntl(ttyfd, F_DUPFD, 10)) < 0) {
137109927Stjr				close(ttyfd);
138109927Stjr				ttyfd = -1;
139109927Stjr				goto out;
140109927Stjr			}
141109927Stjr			close(ttyfd);
142109927Stjr			ttyfd = i;
143109927Stjr		}
144103223Snectar		if (fcntl(ttyfd, F_SETFD, FD_CLOEXEC) < 0) {
14599762Stjr			close(ttyfd);
14699762Stjr			ttyfd = -1;
14799762Stjr			goto out;
14899762Stjr		}
1491556Srgrimes		do { /* while we are in the background */
15099762Stjr			initialpgrp = tcgetpgrp(ttyfd);
15120425Ssteve			if (initialpgrp < 0) {
152199629Sjillesout:				out2fmt_flush("sh: can't access tty; job control turned off\n");
1531556Srgrimes				mflag = 0;
1541556Srgrimes				return;
1551556Srgrimes			}
156216400Sjilles			if (initialpgrp != getpgrp()) {
157216400Sjilles				kill(0, SIGTTIN);
1581556Srgrimes				continue;
1591556Srgrimes			}
1601556Srgrimes		} while (0);
1611556Srgrimes		setsignal(SIGTSTP);
1621556Srgrimes		setsignal(SIGTTOU);
1631556Srgrimes		setsignal(SIGTTIN);
16417987Speter		setpgid(0, rootpid);
16599762Stjr		tcsetpgrp(ttyfd, rootpid);
1661556Srgrimes	} else { /* turning job control off */
16717987Speter		setpgid(0, initialpgrp);
16899762Stjr		tcsetpgrp(ttyfd, initialpgrp);
16999762Stjr		close(ttyfd);
17099762Stjr		ttyfd = -1;
1711556Srgrimes		setsignal(SIGTSTP);
1721556Srgrimes		setsignal(SIGTTOU);
1731556Srgrimes		setsignal(SIGTTIN);
1741556Srgrimes	}
1751556Srgrimes	jobctl = on;
1761556Srgrimes}
17720425Ssteve#endif
1781556Srgrimes
1791556Srgrimes
1801556Srgrimes#ifdef mkinit
18128346SsteveINCLUDE <sys/types.h>
18217987SpeterINCLUDE <stdlib.h>
1831556Srgrimes
1841556SrgrimesSHELLPROC {
1851556Srgrimes	backgndpid = -1;
186209600Sjilles	bgjob = NULL;
1871556Srgrimes#if JOBS
1881556Srgrimes	jobctl = 0;
1891556Srgrimes#endif
1901556Srgrimes}
1911556Srgrimes
1921556Srgrimes#endif
1931556Srgrimes
1941556Srgrimes
1951556Srgrimes
1961556Srgrimes#if JOBS
19717987Speterint
19890111Simpfgcmd(int argc __unused, char **argv)
19917987Speter{
2001556Srgrimes	struct job *jp;
201100308Stjr	pid_t pgrp;
2021556Srgrimes	int status;
2031556Srgrimes
2041556Srgrimes	jp = getjob(argv[1]);
2051556Srgrimes	if (jp->jobctl == 0)
2061556Srgrimes		error("job not created under job control");
207216217Sjilles	printjobcmd(jp);
20896933Stjr	flushout(&output);
2091556Srgrimes	pgrp = jp->ps[0].pid;
21099762Stjr	tcsetpgrp(ttyfd, pgrp);
2111556Srgrimes	restartjob(jp);
212100305Stjr	jp->foreground = 1;
2131556Srgrimes	INTOFF;
21445916Scracauer	status = waitforjob(jp, (int *)NULL);
2151556Srgrimes	INTON;
2161556Srgrimes	return status;
2171556Srgrimes}
2181556Srgrimes
2191556Srgrimes
22017987Speterint
22190111Simpbgcmd(int argc, char **argv)
22217987Speter{
2231556Srgrimes	struct job *jp;
2241556Srgrimes
2251556Srgrimes	do {
2261556Srgrimes		jp = getjob(*++argv);
2271556Srgrimes		if (jp->jobctl == 0)
2281556Srgrimes			error("job not created under job control");
22996933Stjr		if (jp->state == JOBDONE)
23096933Stjr			continue;
2311556Srgrimes		restartjob(jp);
232100305Stjr		jp->foreground = 0;
233216400Sjilles		out1fmt("[%td] ", jp - jobtab + 1);
234216217Sjilles		printjobcmd(jp);
2351556Srgrimes	} while (--argc > 1);
2361556Srgrimes	return 0;
2371556Srgrimes}
2381556Srgrimes
2391556Srgrimes
240213811Sobrienstatic void
24190111Simprestartjob(struct job *jp)
24217987Speter{
2431556Srgrimes	struct procstat *ps;
2441556Srgrimes	int i;
2451556Srgrimes
2461556Srgrimes	if (jp->state == JOBDONE)
2471556Srgrimes		return;
24897660Stjr	setcurjob(jp);
2491556Srgrimes	INTOFF;
250216400Sjilles	kill(-jp->ps[0].pid, SIGCONT);
2511556Srgrimes	for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
25226104Ssteve		if (WIFSTOPPED(ps->status)) {
2531556Srgrimes			ps->status = -1;
2541556Srgrimes			jp->state = 0;
2551556Srgrimes		}
2561556Srgrimes	}
2571556Srgrimes	INTON;
2581556Srgrimes}
2591556Srgrimes#endif
2601556Srgrimes
2611556Srgrimes
2621556Srgrimesint
26397669Stjrjobscmd(int argc, char *argv[])
26417987Speter{
26597669Stjr	char *id;
266163085Sstefanf	int ch, mode;
26797669Stjr
26897669Stjr	optind = optreset = 1;
269100663Stjr	opterr = 0;
270163085Sstefanf	mode = SHOWJOBS_DEFAULT;
271163085Sstefanf	while ((ch = getopt(argc, argv, "lps")) != -1) {
27297669Stjr		switch (ch) {
27397669Stjr		case 'l':
274163085Sstefanf			mode = SHOWJOBS_VERBOSE;
27597669Stjr			break;
276163085Sstefanf		case 'p':
277163085Sstefanf			mode = SHOWJOBS_PGIDS;
278163085Sstefanf			break;
27997669Stjr		case 's':
280163085Sstefanf			mode = SHOWJOBS_PIDS;
28197669Stjr			break;
28297669Stjr		case '?':
28397669Stjr		default:
28497669Stjr			error("unknown option: -%c", optopt);
28597669Stjr		}
28697669Stjr	}
28797669Stjr	argc -= optind;
28897669Stjr	argv += optind;
28997669Stjr
29097669Stjr	if (argc == 0)
291163085Sstefanf		showjobs(0, mode);
29297669Stjr	else
29397669Stjr		while ((id = *argv++) != NULL)
294216217Sjilles			showjob(getjob(id), mode);
29597669Stjr
29697669Stjr	return (0);
2971556Srgrimes}
2981556Srgrimes
299213811Sobrienstatic void
300216217Sjillesprintjobcmd(struct job *jp)
30197663Stjr{
302216217Sjilles	struct procstat *ps;
303216217Sjilles	int i;
304216217Sjilles
305216217Sjilles	for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
306216217Sjilles		out1str(ps->cmd);
307216217Sjilles		if (i > 0)
308216217Sjilles			out1str(" | ");
309216217Sjilles	}
310216217Sjilles	out1c('\n');
311216217Sjilles}
312216217Sjilles
313216217Sjillesstatic void
314216217Sjillesshowjob(struct job *jp, int mode)
315216217Sjilles{
31697663Stjr	char s[64];
317216217Sjilles	char statestr[64];
31897663Stjr	struct procstat *ps;
31997669Stjr	struct job *j;
32097669Stjr	int col, curr, i, jobno, prev, procno;
32197669Stjr	char c;
3221556Srgrimes
323163085Sstefanf	procno = (mode == SHOWJOBS_PGIDS) ? 1 : jp->nprocs;
32497663Stjr	jobno = jp - jobtab + 1;
32597669Stjr	curr = prev = 0;
32697669Stjr#if JOBS
32797669Stjr	if ((j = getcurjob(NULL)) != NULL) {
32897669Stjr		curr = j - jobtab + 1;
32997669Stjr		if ((j = getcurjob(j)) != NULL)
33097669Stjr			prev = j - jobtab + 1;
33197669Stjr	}
33297669Stjr#endif
333216217Sjilles	ps = jp->ps + jp->nprocs - 1;
334216217Sjilles	if (jp->state == 0) {
335216217Sjilles		strcpy(statestr, "Running");
336216217Sjilles#if JOBS
337216217Sjilles	} else if (jp->state == JOBSTOPPED) {
338216217Sjilles		while (!WIFSTOPPED(ps->status) && ps > jp->ps)
339216217Sjilles			ps--;
340216217Sjilles		if (WIFSTOPPED(ps->status))
341216217Sjilles			i = WSTOPSIG(ps->status);
342216217Sjilles		else
343216217Sjilles			i = -1;
344216217Sjilles		if (i > 0 && i < sys_nsig && sys_siglist[i])
345216217Sjilles			strcpy(statestr, sys_siglist[i]);
346216217Sjilles		else
347216217Sjilles			strcpy(statestr, "Suspended");
348216217Sjilles#endif
349216217Sjilles	} else if (WIFEXITED(ps->status)) {
350216217Sjilles		if (WEXITSTATUS(ps->status) == 0)
351216217Sjilles			strcpy(statestr, "Done");
352216217Sjilles		else
353216220Sjilles			fmtstr(statestr, 64, "Done(%d)",
354216217Sjilles			    WEXITSTATUS(ps->status));
355216217Sjilles	} else {
356216217Sjilles		i = WTERMSIG(ps->status);
357216217Sjilles		if (i > 0 && i < sys_nsig && sys_siglist[i])
358216217Sjilles			strcpy(statestr, sys_siglist[i]);
359216217Sjilles		else
360216217Sjilles			fmtstr(statestr, 64, "Signal %d", i);
361216217Sjilles		if (WCOREDUMP(ps->status))
362216217Sjilles			strcat(statestr, " (core dumped)");
363216217Sjilles	}
364216217Sjilles
36597663Stjr	for (ps = jp->ps ; ; ps++) {	/* for each process */
366163085Sstefanf		if (mode == SHOWJOBS_PIDS || mode == SHOWJOBS_PGIDS) {
367216199Sjilles			out1fmt("%d\n", (int)ps->pid);
36897669Stjr			goto skip;
36997669Stjr		}
370216217Sjilles		if (mode != SHOWJOBS_VERBOSE && ps != jp->ps)
37197669Stjr			goto skip;
37297819Stjr		if (jobno == curr && ps == jp->ps)
37397669Stjr			c = '+';
37497819Stjr		else if (jobno == prev && ps == jp->ps)
37597669Stjr			c = '-';
37697669Stjr		else
37797669Stjr			c = ' ';
37897663Stjr		if (ps == jp->ps)
37997669Stjr			fmtstr(s, 64, "[%d] %c ", jobno, c);
38097663Stjr		else
38197816Stjr			fmtstr(s, 64, "    %c ", c);
38297663Stjr		out1str(s);
38397663Stjr		col = strlen(s);
384163085Sstefanf		if (mode == SHOWJOBS_VERBOSE) {
385100308Stjr			fmtstr(s, 64, "%d ", (int)ps->pid);
38697669Stjr			out1str(s);
38797669Stjr			col += strlen(s);
38897669Stjr		}
389216217Sjilles		if (ps == jp->ps) {
390216217Sjilles			out1str(statestr);
391216217Sjilles			col += strlen(statestr);
39297663Stjr		}
39397663Stjr		do {
39497663Stjr			out1c(' ');
39597663Stjr			col++;
39697663Stjr		} while (col < 30);
397216217Sjilles		if (mode == SHOWJOBS_VERBOSE) {
398216217Sjilles			out1str(ps->cmd);
399216217Sjilles			out1c('\n');
400216217Sjilles		} else
401216217Sjilles			printjobcmd(jp);
40297669Stjrskip:		if (--procno <= 0)
40397663Stjr			break;
40497663Stjr	}
40597663Stjr}
40697663Stjr
4071556Srgrimes/*
4081556Srgrimes * Print a list of jobs.  If "change" is nonzero, only print jobs whose
4091556Srgrimes * statuses have changed since the last call to showjobs.
4101556Srgrimes *
4111556Srgrimes * If the shell is interrupted in the process of creating a job, the
4121556Srgrimes * result may be a job structure containing zero processes.  Such structures
4131556Srgrimes * will be freed here.
4141556Srgrimes */
4151556Srgrimes
4161556Srgrimesvoid
417163085Sstefanfshowjobs(int change, int mode)
41817987Speter{
4191556Srgrimes	int jobno;
4201556Srgrimes	struct job *jp;
4211556Srgrimes
4221556Srgrimes	TRACE(("showjobs(%d) called\n", change));
423208489Sjilles	checkzombies();
4241556Srgrimes	for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
4251556Srgrimes		if (! jp->used)
4261556Srgrimes			continue;
4271556Srgrimes		if (jp->nprocs == 0) {
4281556Srgrimes			freejob(jp);
4291556Srgrimes			continue;
4301556Srgrimes		}
4311556Srgrimes		if (change && ! jp->changed)
4321556Srgrimes			continue;
433216217Sjilles		showjob(jp, mode);
4341556Srgrimes		jp->changed = 0;
435209600Sjilles		/* Hack: discard jobs for which $! has not been referenced
436209600Sjilles		 * in interactive mode when they terminate.
437209600Sjilles		 */
438209600Sjilles		if (jp->state == JOBDONE && !jp->remembered &&
439209600Sjilles				(iflag || jp != bgjob)) {
4401556Srgrimes			freejob(jp);
4411556Srgrimes		}
4421556Srgrimes	}
4431556Srgrimes}
4441556Srgrimes
4451556Srgrimes
4461556Srgrimes/*
4471556Srgrimes * Mark a job structure as unused.
4481556Srgrimes */
4491556Srgrimes
450213811Sobrienstatic void
45190111Simpfreejob(struct job *jp)
45290111Simp{
4531556Srgrimes	struct procstat *ps;
4541556Srgrimes	int i;
4551556Srgrimes
4561556Srgrimes	INTOFF;
457209600Sjilles	if (bgjob == jp)
458209600Sjilles		bgjob = NULL;
4591556Srgrimes	for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
4601556Srgrimes		if (ps->cmd != nullstr)
4611556Srgrimes			ckfree(ps->cmd);
4621556Srgrimes	}
4631556Srgrimes	if (jp->ps != &jp->ps0)
4641556Srgrimes		ckfree(jp->ps);
4651556Srgrimes	jp->used = 0;
4661556Srgrimes#if JOBS
46797659Stjr	deljob(jp);
4681556Srgrimes#endif
4691556Srgrimes	INTON;
4701556Srgrimes}
4711556Srgrimes
4721556Srgrimes
4731556Srgrimes
4741556Srgrimesint
47590111Simpwaitcmd(int argc, char **argv)
47617987Speter{
4771556Srgrimes	struct job *job;
47826104Ssteve	int status, retval;
4791556Srgrimes	struct job *jp;
4801556Srgrimes
4811556Srgrimes	if (argc > 1) {
4821556Srgrimes		job = getjob(argv[1]);
4831556Srgrimes	} else {
4841556Srgrimes		job = NULL;
4851556Srgrimes	}
48638536Scracauer
48738536Scracauer	/*
48838536Scracauer	 * Loop until a process is terminated or stopped, or a SIGINT is
48938536Scracauer	 * received.
49038536Scracauer	 */
49138536Scracauer
49238521Scracauer	in_waitcmd++;
49338536Scracauer	do {
4941556Srgrimes		if (job != NULL) {
4951556Srgrimes			if (job->state) {
4961556Srgrimes				status = job->ps[job->nprocs - 1].status;
49726104Ssteve				if (WIFEXITED(status))
49826104Ssteve					retval = WEXITSTATUS(status);
4991556Srgrimes#if JOBS
50026104Ssteve				else if (WIFSTOPPED(status))
50126104Ssteve					retval = WSTOPSIG(status) + 128;
5021556Srgrimes#endif
5031556Srgrimes				else
50426104Ssteve					retval = WTERMSIG(status) + 128;
505209600Sjilles				if (! iflag || ! job->changed)
5061556Srgrimes					freejob(job);
507209600Sjilles				else {
508209600Sjilles					job->remembered = 0;
509209600Sjilles					if (job == bgjob)
510209600Sjilles						bgjob = NULL;
511209600Sjilles				}
51238536Scracauer				in_waitcmd--;
51326104Ssteve				return retval;
5141556Srgrimes			}
5151556Srgrimes		} else {
516209600Sjilles			for (jp = jobtab ; jp < jobtab + njobs; jp++)
517209600Sjilles				if (jp->used && jp->state == JOBDONE) {
518209600Sjilles					if (! iflag || ! jp->changed)
519209600Sjilles						freejob(jp);
520209600Sjilles					else {
521209600Sjilles						jp->remembered = 0;
522209600Sjilles						if (jp == bgjob)
523209600Sjilles							bgjob = NULL;
524209600Sjilles					}
525209600Sjilles				}
5261556Srgrimes			for (jp = jobtab ; ; jp++) {
5271556Srgrimes				if (jp >= jobtab + njobs) {	/* no running procs */
52838536Scracauer					in_waitcmd--;
5291556Srgrimes					return 0;
5301556Srgrimes				}
5311556Srgrimes				if (jp->used && jp->state == 0)
5321556Srgrimes					break;
5331556Srgrimes			}
5341556Srgrimes		}
53538521Scracauer	} while (dowait(1, (struct job *)NULL) != -1);
53638521Scracauer	in_waitcmd--;
53738521Scracauer
53838521Scracauer	return 0;
5391556Srgrimes}
5401556Srgrimes
5411556Srgrimes
5421556Srgrimes
54317987Speterint
54490111Simpjobidcmd(int argc __unused, char **argv)
54517987Speter{
5461556Srgrimes	struct job *jp;
5471556Srgrimes	int i;
5481556Srgrimes
5491556Srgrimes	jp = getjob(argv[1]);
5501556Srgrimes	for (i = 0 ; i < jp->nprocs ; ) {
551100308Stjr		out1fmt("%d", (int)jp->ps[i].pid);
5521556Srgrimes		out1c(++i < jp->nprocs? ' ' : '\n');
5531556Srgrimes	}
5541556Srgrimes	return 0;
5551556Srgrimes}
5561556Srgrimes
5571556Srgrimes
5581556Srgrimes
5591556Srgrimes/*
5601556Srgrimes * Convert a job name to a job structure.
5611556Srgrimes */
5621556Srgrimes
563213811Sobrienstatic struct job *
56490111Simpgetjob(char *name)
56590111Simp{
5661556Srgrimes	int jobno;
56797688Stjr	struct job *found, *jp;
568100308Stjr	pid_t pid;
5691556Srgrimes	int i;
5701556Srgrimes
5711556Srgrimes	if (name == NULL) {
5721556Srgrimes#if JOBS
57397659Stjrcurrentjob:	if ((jp = getcurjob(NULL)) == NULL)
5741556Srgrimes			error("No current job");
57597659Stjr		return (jp);
5761556Srgrimes#else
5771556Srgrimes		error("No current job");
5781556Srgrimes#endif
5791556Srgrimes	} else if (name[0] == '%') {
5801556Srgrimes		if (is_digit(name[1])) {
5811556Srgrimes			jobno = number(name + 1);
5821556Srgrimes			if (jobno > 0 && jobno <= njobs
5831556Srgrimes			 && jobtab[jobno - 1].used != 0)
5841556Srgrimes				return &jobtab[jobno - 1];
5851556Srgrimes#if JOBS
5861556Srgrimes		} else if (name[1] == '%' && name[2] == '\0') {
5871556Srgrimes			goto currentjob;
58897688Stjr		} else if (name[1] == '+' && name[2] == '\0') {
58997688Stjr			goto currentjob;
59097688Stjr		} else if (name[1] == '-' && name[2] == '\0') {
59197688Stjr			if ((jp = getcurjob(NULL)) == NULL ||
59297688Stjr			    (jp = getcurjob(jp)) == NULL)
59397688Stjr				error("No previous job");
59497688Stjr			return (jp);
5951556Srgrimes#endif
59697688Stjr		} else if (name[1] == '?') {
59797688Stjr			found = NULL;
59897688Stjr			for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
59997688Stjr				if (jp->used && jp->nprocs > 0
60097688Stjr				 && strstr(jp->ps[0].cmd, name + 2) != NULL) {
60197688Stjr					if (found)
60297688Stjr						error("%s: ambiguous", name);
60397688Stjr					found = jp;
60497688Stjr				}
60597688Stjr			}
60697688Stjr			if (found != NULL)
60797688Stjr				return (found);
6081556Srgrimes		} else {
60997688Stjr			found = NULL;
6101556Srgrimes			for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
6111556Srgrimes				if (jp->used && jp->nprocs > 0
6121556Srgrimes				 && prefix(name + 1, jp->ps[0].cmd)) {
6131556Srgrimes					if (found)
6141556Srgrimes						error("%s: ambiguous", name);
6151556Srgrimes					found = jp;
6161556Srgrimes				}
6171556Srgrimes			}
6181556Srgrimes			if (found)
6191556Srgrimes				return found;
6201556Srgrimes		}
6211556Srgrimes	} else if (is_number(name)) {
622100308Stjr		pid = (pid_t)number(name);
6231556Srgrimes		for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
6241556Srgrimes			if (jp->used && jp->nprocs > 0
6251556Srgrimes			 && jp->ps[jp->nprocs - 1].pid == pid)
6261556Srgrimes				return jp;
6271556Srgrimes		}
6281556Srgrimes	}
6291556Srgrimes	error("No such job: %s", name);
63017987Speter	/*NOTREACHED*/
63117987Speter	return NULL;
6321556Srgrimes}
6331556Srgrimes
6341556Srgrimes
635216629Sjillespid_t
636216629Sjillesgetjobpgrp(char *name)
637216629Sjilles{
638216629Sjilles	struct job *jp;
6391556Srgrimes
640216629Sjilles	jp = getjob(name);
641216629Sjilles	return -jp->ps[0].pid;
642216629Sjilles}
643216629Sjilles
6441556Srgrimes/*
6451556Srgrimes * Return a new job structure,
6461556Srgrimes */
6471556Srgrimes
6481556Srgrimesstruct job *
64990111Simpmakejob(union node *node __unused, int nprocs)
65017987Speter{
6511556Srgrimes	int i;
6521556Srgrimes	struct job *jp;
6531556Srgrimes
6541556Srgrimes	for (i = njobs, jp = jobtab ; ; jp++) {
6551556Srgrimes		if (--i < 0) {
6561556Srgrimes			INTOFF;
6571556Srgrimes			if (njobs == 0) {
6581556Srgrimes				jobtab = ckmalloc(4 * sizeof jobtab[0]);
65997664Stjr#if JOBS
66097659Stjr				jobmru = NULL;
66197664Stjr#endif
6621556Srgrimes			} else {
6631556Srgrimes				jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
66417987Speter				memcpy(jp, jobtab, njobs * sizeof jp[0]);
66597659Stjr#if JOBS
66697659Stjr				/* Relocate `next' pointers and list head */
66799760Stjr				if (jobmru != NULL)
66899760Stjr					jobmru = &jp[jobmru - jobtab];
66997659Stjr				for (i = 0; i < njobs; i++)
67097659Stjr					if (jp[i].next != NULL)
67197659Stjr						jp[i].next = &jp[jp[i].next -
67297659Stjr						    jobtab];
67397659Stjr#endif
674209600Sjilles				if (bgjob != NULL)
675209600Sjilles					bgjob = &jp[bgjob - jobtab];
67620425Ssteve				/* Relocate `ps' pointers */
67720425Ssteve				for (i = 0; i < njobs; i++)
67820425Ssteve					if (jp[i].ps == &jobtab[i].ps0)
67920425Ssteve						jp[i].ps = &jp[i].ps0;
6801556Srgrimes				ckfree(jobtab);
6811556Srgrimes				jobtab = jp;
6821556Srgrimes			}
6831556Srgrimes			jp = jobtab + njobs;
6841556Srgrimes			for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0);
6851556Srgrimes			INTON;
6861556Srgrimes			break;
6871556Srgrimes		}
6881556Srgrimes		if (jp->used == 0)
6891556Srgrimes			break;
6901556Srgrimes	}
6911556Srgrimes	INTOFF;
6921556Srgrimes	jp->state = 0;
6931556Srgrimes	jp->used = 1;
6941556Srgrimes	jp->changed = 0;
6951556Srgrimes	jp->nprocs = 0;
696100305Stjr	jp->foreground = 0;
697209600Sjilles	jp->remembered = 0;
6981556Srgrimes#if JOBS
6991556Srgrimes	jp->jobctl = jobctl;
70097659Stjr	jp->next = NULL;
7011556Srgrimes#endif
7021556Srgrimes	if (nprocs > 1) {
7031556Srgrimes		jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
7041556Srgrimes	} else {
7051556Srgrimes		jp->ps = &jp->ps0;
7061556Srgrimes	}
7071556Srgrimes	INTON;
708213775Sjhb	TRACE(("makejob(%p, %d) returns %%%td\n", (void *)node, nprocs,
70917987Speter	    jp - jobtab + 1));
7101556Srgrimes	return jp;
7118855Srgrimes}
7121556Srgrimes
71397659Stjr#if JOBS
714213811Sobrienstatic void
71597659Stjrsetcurjob(struct job *cj)
71697659Stjr{
71797659Stjr	struct job *jp, *prev;
7181556Srgrimes
71997659Stjr	for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) {
72097659Stjr		if (jp == cj) {
72197659Stjr			if (prev != NULL)
72297659Stjr				prev->next = jp->next;
72397659Stjr			else
72497659Stjr				jobmru = jp->next;
72597659Stjr			jp->next = jobmru;
72697659Stjr			jobmru = cj;
72797659Stjr			return;
72897659Stjr		}
72997659Stjr	}
73097659Stjr	cj->next = jobmru;
73197659Stjr	jobmru = cj;
73297659Stjr}
73397659Stjr
734213811Sobrienstatic void
73597659Stjrdeljob(struct job *j)
73697659Stjr{
73797659Stjr	struct job *jp, *prev;
73897659Stjr
73997659Stjr	for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) {
74097659Stjr		if (jp == j) {
74197659Stjr			if (prev != NULL)
74297659Stjr				prev->next = jp->next;
74397659Stjr			else
74497659Stjr				jobmru = jp->next;
74597659Stjr			return;
74697659Stjr		}
74797659Stjr	}
74897659Stjr}
74997659Stjr
7501556Srgrimes/*
75197659Stjr * Return the most recently used job that isn't `nj', and preferably one
75297659Stjr * that is stopped.
75397659Stjr */
754213811Sobrienstatic struct job *
75597659Stjrgetcurjob(struct job *nj)
75697659Stjr{
75797659Stjr	struct job *jp;
75897659Stjr
75997659Stjr	/* Try to find a stopped one.. */
76097659Stjr	for (jp = jobmru; jp != NULL; jp = jp->next)
76197659Stjr		if (jp->used && jp != nj && jp->state == JOBSTOPPED)
76297659Stjr			return (jp);
76397659Stjr	/* Otherwise the most recently used job that isn't `nj' */
76497659Stjr	for (jp = jobmru; jp != NULL; jp = jp->next)
76597659Stjr		if (jp->used && jp != nj)
76697659Stjr			return (jp);
76797659Stjr
76897659Stjr	return (NULL);
76997659Stjr}
77097659Stjr
77197659Stjr#endif
77297659Stjr
77397659Stjr/*
7741556Srgrimes * Fork of a subshell.  If we are doing job control, give the subshell its
7751556Srgrimes * own process group.  Jp is a job structure that the job is to be added to.
7761556Srgrimes * N is the command that will be evaluated by the child.  Both jp and n may
7771556Srgrimes * be NULL.  The mode parameter can be one of the following:
7781556Srgrimes *	FORK_FG - Fork off a foreground process.
7791556Srgrimes *	FORK_BG - Fork off a background process.
7801556Srgrimes *	FORK_NOJOB - Like FORK_FG, but don't give the process its own
7811556Srgrimes *		     process group even if job control is on.
7821556Srgrimes *
7831556Srgrimes * When job control is turned off, background processes have their standard
7841556Srgrimes * input redirected to /dev/null (except for the second and later processes
7851556Srgrimes * in a pipeline).
7861556Srgrimes */
7871556Srgrimes
788100308Stjrpid_t
78990111Simpforkshell(struct job *jp, union node *n, int mode)
79017987Speter{
791100308Stjr	pid_t pid;
792100308Stjr	pid_t pgrp;
7931556Srgrimes
794213775Sjhb	TRACE(("forkshell(%%%td, %p, %d) called\n", jp - jobtab, (void *)n,
79517987Speter	    mode));
7961556Srgrimes	INTOFF;
797216208Sjilles	if (mode == FORK_BG && (jp == NULL || jp->nprocs == 0))
798208489Sjilles		checkzombies();
799112341Stjr	flushall();
8001556Srgrimes	pid = fork();
8011556Srgrimes	if (pid == -1) {
8021556Srgrimes		TRACE(("Fork failed, errno=%d\n", errno));
8031556Srgrimes		INTON;
80453891Scracauer		error("Cannot fork: %s", strerror(errno));
8051556Srgrimes	}
8061556Srgrimes	if (pid == 0) {
8071556Srgrimes		struct job *p;
8081556Srgrimes		int wasroot;
8091556Srgrimes		int i;
8101556Srgrimes
811100308Stjr		TRACE(("Child shell %d\n", (int)getpid()));
8121556Srgrimes		wasroot = rootshell;
8131556Srgrimes		rootshell = 0;
814200998Sjilles		handler = &main_handler;
8151556Srgrimes		closescript();
8161556Srgrimes		INTON;
8171556Srgrimes		clear_traps();
8181556Srgrimes#if JOBS
8191556Srgrimes		jobctl = 0;		/* do job control only in root shell */
8201556Srgrimes		if (wasroot && mode != FORK_NOJOB && mflag) {
8211556Srgrimes			if (jp == NULL || jp->nprocs == 0)
8221556Srgrimes				pgrp = getpid();
8231556Srgrimes			else
8241556Srgrimes				pgrp = jp->ps[0].pid;
82521352Ssteve			if (setpgid(0, pgrp) == 0 && mode == FORK_FG) {
8261556Srgrimes				/*** this causes superfluous TIOCSPGRPS ***/
82799762Stjr				if (tcsetpgrp(ttyfd, pgrp) < 0)
82820425Ssteve					error("tcsetpgrp failed, errno=%d", errno);
8291556Srgrimes			}
8301556Srgrimes			setsignal(SIGTSTP);
8311556Srgrimes			setsignal(SIGTTOU);
8321556Srgrimes		} else if (mode == FORK_BG) {
8331556Srgrimes			ignoresig(SIGINT);
8341556Srgrimes			ignoresig(SIGQUIT);
8351556Srgrimes			if ((jp == NULL || jp->nprocs == 0) &&
8361556Srgrimes			    ! fd0_redirected_p ()) {
8371556Srgrimes				close(0);
83869793Sobrien				if (open(_PATH_DEVNULL, O_RDONLY) != 0)
83969793Sobrien					error("Can't open %s: %s",
84069793Sobrien					    _PATH_DEVNULL, strerror(errno));
8411556Srgrimes			}
8421556Srgrimes		}
8431556Srgrimes#else
8441556Srgrimes		if (mode == FORK_BG) {
8451556Srgrimes			ignoresig(SIGINT);
8461556Srgrimes			ignoresig(SIGQUIT);
8471556Srgrimes			if ((jp == NULL || jp->nprocs == 0) &&
8481556Srgrimes			    ! fd0_redirected_p ()) {
8491556Srgrimes				close(0);
85069793Sobrien				if (open(_PATH_DEVNULL, O_RDONLY) != 0)
851155301Sschweikh					error("Can't open %s: %s",
85269793Sobrien					    _PATH_DEVNULL, strerror(errno));
8531556Srgrimes			}
8541556Srgrimes		}
8551556Srgrimes#endif
856102051Stjr		INTOFF;
857102051Stjr		for (i = njobs, p = jobtab ; --i >= 0 ; p++)
858102051Stjr			if (p->used)
859102051Stjr				freejob(p);
860102051Stjr		INTON;
8611556Srgrimes		if (wasroot && iflag) {
8621556Srgrimes			setsignal(SIGINT);
8631556Srgrimes			setsignal(SIGQUIT);
8641556Srgrimes			setsignal(SIGTERM);
8651556Srgrimes		}
8661556Srgrimes		return pid;
8671556Srgrimes	}
8681556Srgrimes	if (rootshell && mode != FORK_NOJOB && mflag) {
8691556Srgrimes		if (jp == NULL || jp->nprocs == 0)
8701556Srgrimes			pgrp = pid;
8711556Srgrimes		else
8721556Srgrimes			pgrp = jp->ps[0].pid;
87317987Speter		setpgid(pid, pgrp);
8741556Srgrimes	}
875209600Sjilles	if (mode == FORK_BG) {
876209600Sjilles		if (bgjob != NULL && bgjob->state == JOBDONE &&
877209600Sjilles		    !bgjob->remembered && !iflag)
878209600Sjilles			freejob(bgjob);
8791556Srgrimes		backgndpid = pid;		/* set $! */
880209600Sjilles		bgjob = jp;
881209600Sjilles	}
8821556Srgrimes	if (jp) {
8831556Srgrimes		struct procstat *ps = &jp->ps[jp->nprocs++];
8841556Srgrimes		ps->pid = pid;
8851556Srgrimes		ps->status = -1;
8861556Srgrimes		ps->cmd = nullstr;
8871556Srgrimes		if (iflag && rootshell && n)
8881556Srgrimes			ps->cmd = commandtext(n);
889100305Stjr		jp->foreground = mode == FORK_FG;
89097659Stjr#if JOBS
89197659Stjr		setcurjob(jp);
89297659Stjr#endif
8931556Srgrimes	}
8941556Srgrimes	INTON;
895100308Stjr	TRACE(("In parent shell:  child = %d\n", (int)pid));
8961556Srgrimes	return pid;
8971556Srgrimes}
8981556Srgrimes
8991556Srgrimes
9001556Srgrimes
9011556Srgrimes/*
9021556Srgrimes * Wait for job to finish.
9031556Srgrimes *
9041556Srgrimes * Under job control we have the problem that while a child process is
9051556Srgrimes * running interrupts generated by the user are sent to the child but not
9061556Srgrimes * to the shell.  This means that an infinite loop started by an inter-
9071556Srgrimes * active user may be hard to kill.  With job control turned off, an
9081556Srgrimes * interactive user may place an interactive program inside a loop.  If
9091556Srgrimes * the interactive program catches interrupts, the user doesn't want
9101556Srgrimes * these interrupts to also abort the loop.  The approach we take here
9111556Srgrimes * is to have the shell ignore interrupt signals while waiting for a
91246684Skris * foreground process to terminate, and then send itself an interrupt
9131556Srgrimes * signal if the child process was terminated by an interrupt signal.
9141556Srgrimes * Unfortunately, some programs want to do a bit of cleanup and then
9151556Srgrimes * exit on interrupt; unless these processes terminate themselves by
9161556Srgrimes * sending a signal to themselves (instead of calling exit) they will
9171556Srgrimes * confuse this approach.
9181556Srgrimes */
9191556Srgrimes
9201556Srgrimesint
92190111Simpwaitforjob(struct job *jp, int *origstatus)
92245916Scracauer{
9231556Srgrimes#if JOBS
924100308Stjr	pid_t mypgrp = getpgrp();
925208881Sjilles	int propagate_int = jp->jobctl && jp->foreground;
9261556Srgrimes#endif
9271556Srgrimes	int status;
9281556Srgrimes	int st;
9291556Srgrimes
9301556Srgrimes	INTOFF;
931213775Sjhb	TRACE(("waitforjob(%%%td) called\n", jp - jobtab + 1));
93238950Scracauer	while (jp->state == 0)
93338950Scracauer		if (dowait(1, jp) == -1)
93438950Scracauer			dotrap();
9351556Srgrimes#if JOBS
9361556Srgrimes	if (jp->jobctl) {
93799762Stjr		if (tcsetpgrp(ttyfd, mypgrp) < 0)
93820425Ssteve			error("tcsetpgrp failed, errno=%d\n", errno);
9391556Srgrimes	}
9401556Srgrimes	if (jp->state == JOBSTOPPED)
94197659Stjr		setcurjob(jp);
9421556Srgrimes#endif
9431556Srgrimes	status = jp->ps[jp->nprocs - 1].status;
94445916Scracauer	if (origstatus != NULL)
94545916Scracauer		*origstatus = status;
9461556Srgrimes	/* convert to 8 bits */
94726104Ssteve	if (WIFEXITED(status))
94826104Ssteve		st = WEXITSTATUS(status);
9491556Srgrimes#if JOBS
95026104Ssteve	else if (WIFSTOPPED(status))
95126104Ssteve		st = WSTOPSIG(status) + 128;
9521556Srgrimes#endif
9531556Srgrimes	else
95426104Ssteve		st = WTERMSIG(status) + 128;
9551556Srgrimes	if (! JOBS || jp->state == JOBDONE)
9561556Srgrimes		freejob(jp);
95738521Scracauer	if (int_pending()) {
958216400Sjilles		if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGINT)
95938521Scracauer			CLEAR_PENDING_INT;
96038521Scracauer	}
961208881Sjilles#if JOBS
962208881Sjilles	else if (rootshell && iflag && propagate_int &&
963208881Sjilles			WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
964208881Sjilles		kill(getpid(), SIGINT);
965208881Sjilles#endif
9661556Srgrimes	INTON;
9671556Srgrimes	return st;
9681556Srgrimes}
9691556Srgrimes
9701556Srgrimes
9711556Srgrimes
9721556Srgrimes/*
9731556Srgrimes * Wait for a process to terminate.
9741556Srgrimes */
9751556Srgrimes
976213811Sobrienstatic pid_t
97790111Simpdowait(int block, struct job *job)
97817987Speter{
979100308Stjr	pid_t pid;
9801556Srgrimes	int status;
9811556Srgrimes	struct procstat *sp;
9821556Srgrimes	struct job *jp;
9831556Srgrimes	struct job *thisjob;
9841556Srgrimes	int done;
9851556Srgrimes	int stopped;
98626104Ssteve	int sig;
987216217Sjilles	int coredump;
9881556Srgrimes
98938950Scracauer	in_dowait++;
9901556Srgrimes	TRACE(("dowait(%d) called\n", block));
9911556Srgrimes	do {
9921556Srgrimes		pid = waitproc(block, &status);
993100308Stjr		TRACE(("wait returns %d, status=%d\n", (int)pid, status));
99472086Scracauer	} while ((pid == -1 && errno == EINTR && breakwaitcmd == 0) ||
995125501Scracauer		 (pid > 0 && WIFSTOPPED(status) && !iflag));
99638950Scracauer	in_dowait--;
997153417Smaxim	if (pid == -1 && errno == ECHILD && job != NULL)
998153417Smaxim		job->state = JOBDONE;
99938521Scracauer	if (breakwaitcmd != 0) {
100038521Scracauer		breakwaitcmd = 0;
1001138312Smaxim		if (pid <= 0)
1002138312Smaxim			return -1;
100338521Scracauer	}
10041556Srgrimes	if (pid <= 0)
10051556Srgrimes		return pid;
10061556Srgrimes	INTOFF;
10071556Srgrimes	thisjob = NULL;
10081556Srgrimes	for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
1009216208Sjilles		if (jp->used && jp->nprocs > 0) {
10101556Srgrimes			done = 1;
10111556Srgrimes			stopped = 1;
10121556Srgrimes			for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
10131556Srgrimes				if (sp->pid == -1)
10141556Srgrimes					continue;
10151556Srgrimes				if (sp->pid == pid) {
101626104Ssteve					TRACE(("Changing status of proc %d from 0x%x to 0x%x\n",
1017100308Stjr						   (int)pid, sp->status,
1018100308Stjr						   status));
10191556Srgrimes					sp->status = status;
10201556Srgrimes					thisjob = jp;
10211556Srgrimes				}
10221556Srgrimes				if (sp->status == -1)
10231556Srgrimes					stopped = 0;
102426104Ssteve				else if (WIFSTOPPED(sp->status))
10251556Srgrimes					done = 0;
10261556Srgrimes			}
10271556Srgrimes			if (stopped) {		/* stopped or done */
10281556Srgrimes				int state = done? JOBDONE : JOBSTOPPED;
10291556Srgrimes				if (jp->state != state) {
1030213775Sjhb					TRACE(("Job %td: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
10311556Srgrimes					jp->state = state;
1032209600Sjilles					if (jp != job) {
1033209600Sjilles						if (done && !jp->remembered &&
1034209600Sjilles						    !iflag && jp != bgjob)
1035209600Sjilles							freejob(jp);
10361556Srgrimes#if JOBS
1037209600Sjilles						else if (done)
1038209600Sjilles							deljob(jp);
10391556Srgrimes#endif
1040209600Sjilles					}
10411556Srgrimes				}
10421556Srgrimes			}
10431556Srgrimes		}
10441556Srgrimes	}
10451556Srgrimes	INTON;
1046216217Sjilles	if (!thisjob || thisjob->state == 0)
1047216217Sjilles		;
1048216217Sjilles	else if ((!rootshell || !iflag || thisjob == job) &&
1049216217Sjilles	    thisjob->foreground && thisjob->state != JOBSTOPPED) {
1050216217Sjilles		sig = 0;
1051216217Sjilles		coredump = 0;
1052216217Sjilles		for (sp = thisjob->ps; sp < thisjob->ps + thisjob->nprocs; sp++)
1053216217Sjilles			if (WIFSIGNALED(sp->status)) {
1054216217Sjilles				sig = WTERMSIG(sp->status);
1055216217Sjilles				coredump = WCOREDUMP(sp->status);
1056216217Sjilles			}
1057216217Sjilles		if (sig > 0 && sig != SIGINT && sig != SIGPIPE) {
1058216217Sjilles			if (sig < sys_nsig && sys_siglist[sig])
1059218105Sjilles				out2str(sys_siglist[sig]);
106026104Ssteve			else
1061218105Sjilles				outfmt(out2, "Signal %d", sig);
1062216217Sjilles			if (coredump)
1063218105Sjilles				out2str(" (core dumped)");
1064218105Sjilles			out2c('\n');
1065218105Sjilles			flushout(out2);
10661556Srgrimes		}
10671556Srgrimes	} else {
1068109627Stjr		TRACE(("Not printing status, rootshell=%d, job=%p\n", rootshell, job));
1069216217Sjilles		thisjob->changed = 1;
10701556Srgrimes	}
10711556Srgrimes	return pid;
10721556Srgrimes}
10731556Srgrimes
10741556Srgrimes
10751556Srgrimes
10761556Srgrimes/*
10771556Srgrimes * Do a wait system call.  If job control is compiled in, we accept
10781556Srgrimes * stopped processes.  If block is zero, we return a value of zero
10791556Srgrimes * rather than blocking.
10801556Srgrimes */
1081213811Sobrienstatic pid_t
108290111Simpwaitproc(int block, int *status)
108317987Speter{
10841556Srgrimes	int flags;
10851556Srgrimes
10861556Srgrimes#if JOBS
10871556Srgrimes	flags = WUNTRACED;
10881556Srgrimes#else
10891556Srgrimes	flags = 0;
10901556Srgrimes#endif
10911556Srgrimes	if (block == 0)
10921556Srgrimes		flags |= WNOHANG;
10931556Srgrimes	return wait3(status, flags, (struct rusage *)NULL);
10941556Srgrimes}
10951556Srgrimes
10961556Srgrimes/*
10971556Srgrimes * return 1 if there are stopped jobs, otherwise 0
10981556Srgrimes */
10991556Srgrimesint job_warning = 0;
11001556Srgrimesint
110190111Simpstoppedjobs(void)
11021556Srgrimes{
110325222Ssteve	int jobno;
110425222Ssteve	struct job *jp;
11051556Srgrimes
11061556Srgrimes	if (job_warning)
11071556Srgrimes		return (0);
11081556Srgrimes	for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) {
11091556Srgrimes		if (jp->used == 0)
11101556Srgrimes			continue;
11111556Srgrimes		if (jp->state == JOBSTOPPED) {
1112199629Sjilles			out2fmt_flush("You have stopped jobs.\n");
11131556Srgrimes			job_warning = 2;
11141556Srgrimes			return (1);
11151556Srgrimes		}
11161556Srgrimes	}
11171556Srgrimes
11181556Srgrimes	return (0);
11191556Srgrimes}
11201556Srgrimes
1121208489Sjilles
1122213811Sobrienstatic void
1123208489Sjillescheckzombies(void)
1124208489Sjilles{
1125208489Sjilles	while (njobs > 0 && dowait(0, NULL) > 0)
1126208489Sjilles		;
1127208489Sjilles}
1128208489Sjilles
1129208489Sjilles
1130209600Sjillesint
1131209600Sjillesbackgndpidset(void)
1132209600Sjilles{
1133209600Sjilles	return backgndpid != -1;
1134209600Sjilles}
1135209600Sjilles
1136209600Sjilles
1137209600Sjillespid_t
1138209600Sjillesbackgndpidval(void)
1139209600Sjilles{
1140209600Sjilles	if (bgjob != NULL)
1141209600Sjilles		bgjob->remembered = 1;
1142209600Sjilles	return backgndpid;
1143209600Sjilles}
1144209600Sjilles
11451556Srgrimes/*
11461556Srgrimes * Return a string identifying a command (to be printed by the
11471556Srgrimes * jobs command.
11481556Srgrimes */
11491556Srgrimes
1150213760Sobrienstatic char *cmdnextc;
1151213760Sobrienstatic int cmdnleft;
11521556Srgrimes#define MAXCMDTEXT	200
11531556Srgrimes
11541556Srgrimeschar *
115590111Simpcommandtext(union node *n)
115690111Simp{
11571556Srgrimes	char *name;
11581556Srgrimes
11591556Srgrimes	cmdnextc = name = ckmalloc(MAXCMDTEXT);
11601556Srgrimes	cmdnleft = MAXCMDTEXT - 4;
11611556Srgrimes	cmdtxt(n);
11621556Srgrimes	*cmdnextc = '\0';
11631556Srgrimes	return name;
11641556Srgrimes}
11651556Srgrimes
11661556Srgrimes
1167213811Sobrienstatic void
116890111Simpcmdtxt(union node *n)
116990111Simp{
11701556Srgrimes	union node *np;
11711556Srgrimes	struct nodelist *lp;
1172201053Sjilles	const char *p;
11731556Srgrimes	int i;
11741556Srgrimes	char s[2];
11751556Srgrimes
11761556Srgrimes	if (n == NULL)
11771556Srgrimes		return;
11781556Srgrimes	switch (n->type) {
11791556Srgrimes	case NSEMI:
11801556Srgrimes		cmdtxt(n->nbinary.ch1);
11811556Srgrimes		cmdputs("; ");
11821556Srgrimes		cmdtxt(n->nbinary.ch2);
11831556Srgrimes		break;
11841556Srgrimes	case NAND:
11851556Srgrimes		cmdtxt(n->nbinary.ch1);
11861556Srgrimes		cmdputs(" && ");
11871556Srgrimes		cmdtxt(n->nbinary.ch2);
11881556Srgrimes		break;
11891556Srgrimes	case NOR:
11901556Srgrimes		cmdtxt(n->nbinary.ch1);
11911556Srgrimes		cmdputs(" || ");
11921556Srgrimes		cmdtxt(n->nbinary.ch2);
11931556Srgrimes		break;
11941556Srgrimes	case NPIPE:
11951556Srgrimes		for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
11961556Srgrimes			cmdtxt(lp->n);
11971556Srgrimes			if (lp->next)
11981556Srgrimes				cmdputs(" | ");
11991556Srgrimes		}
12001556Srgrimes		break;
12011556Srgrimes	case NSUBSHELL:
12021556Srgrimes		cmdputs("(");
12031556Srgrimes		cmdtxt(n->nredir.n);
12041556Srgrimes		cmdputs(")");
12051556Srgrimes		break;
12061556Srgrimes	case NREDIR:
12071556Srgrimes	case NBACKGND:
12081556Srgrimes		cmdtxt(n->nredir.n);
12091556Srgrimes		break;
12101556Srgrimes	case NIF:
12111556Srgrimes		cmdputs("if ");
12121556Srgrimes		cmdtxt(n->nif.test);
12131556Srgrimes		cmdputs("; then ");
12141556Srgrimes		cmdtxt(n->nif.ifpart);
12151556Srgrimes		cmdputs("...");
12161556Srgrimes		break;
12171556Srgrimes	case NWHILE:
12181556Srgrimes		cmdputs("while ");
12191556Srgrimes		goto until;
12201556Srgrimes	case NUNTIL:
12211556Srgrimes		cmdputs("until ");
12221556Srgrimesuntil:
12231556Srgrimes		cmdtxt(n->nbinary.ch1);
12241556Srgrimes		cmdputs("; do ");
12251556Srgrimes		cmdtxt(n->nbinary.ch2);
12261556Srgrimes		cmdputs("; done");
12271556Srgrimes		break;
12281556Srgrimes	case NFOR:
12291556Srgrimes		cmdputs("for ");
12301556Srgrimes		cmdputs(n->nfor.var);
12311556Srgrimes		cmdputs(" in ...");
12321556Srgrimes		break;
12331556Srgrimes	case NCASE:
12341556Srgrimes		cmdputs("case ");
12351556Srgrimes		cmdputs(n->ncase.expr->narg.text);
12361556Srgrimes		cmdputs(" in ...");
12371556Srgrimes		break;
12381556Srgrimes	case NDEFUN:
12391556Srgrimes		cmdputs(n->narg.text);
12401556Srgrimes		cmdputs("() ...");
12411556Srgrimes		break;
12421556Srgrimes	case NCMD:
12431556Srgrimes		for (np = n->ncmd.args ; np ; np = np->narg.next) {
12441556Srgrimes			cmdtxt(np);
12451556Srgrimes			if (np->narg.next)
12461556Srgrimes				cmdputs(" ");
12471556Srgrimes		}
12481556Srgrimes		for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
12491556Srgrimes			cmdputs(" ");
12501556Srgrimes			cmdtxt(np);
12511556Srgrimes		}
12521556Srgrimes		break;
12531556Srgrimes	case NARG:
12541556Srgrimes		cmdputs(n->narg.text);
12551556Srgrimes		break;
12561556Srgrimes	case NTO:
12571556Srgrimes		p = ">";  i = 1;  goto redir;
12581556Srgrimes	case NAPPEND:
12591556Srgrimes		p = ">>";  i = 1;  goto redir;
12601556Srgrimes	case NTOFD:
12611556Srgrimes		p = ">&";  i = 1;  goto redir;
126296922Stjr	case NCLOBBER:
126396922Stjr		p = ">|"; i = 1; goto redir;
12641556Srgrimes	case NFROM:
12651556Srgrimes		p = "<";  i = 0;  goto redir;
126666612Sbrian	case NFROMTO:
126766612Sbrian		p = "<>";  i = 0;  goto redir;
12681556Srgrimes	case NFROMFD:
12691556Srgrimes		p = "<&";  i = 0;  goto redir;
12701556Srgrimesredir:
12711556Srgrimes		if (n->nfile.fd != i) {
12721556Srgrimes			s[0] = n->nfile.fd + '0';
12731556Srgrimes			s[1] = '\0';
12741556Srgrimes			cmdputs(s);
12751556Srgrimes		}
12761556Srgrimes		cmdputs(p);
12771556Srgrimes		if (n->type == NTOFD || n->type == NFROMFD) {
127899634Stjr			if (n->ndup.dupfd >= 0)
127999634Stjr				s[0] = n->ndup.dupfd + '0';
128099634Stjr			else
128199634Stjr				s[0] = '-';
12821556Srgrimes			s[1] = '\0';
12831556Srgrimes			cmdputs(s);
12841556Srgrimes		} else {
12851556Srgrimes			cmdtxt(n->nfile.fname);
12861556Srgrimes		}
12871556Srgrimes		break;
12881556Srgrimes	case NHERE:
12891556Srgrimes	case NXHERE:
12901556Srgrimes		cmdputs("<<...");
12911556Srgrimes		break;
12921556Srgrimes	default:
12931556Srgrimes		cmdputs("???");
12941556Srgrimes		break;
12951556Srgrimes	}
12961556Srgrimes}
12971556Srgrimes
12981556Srgrimes
12991556Srgrimes
1300213811Sobrienstatic void
1301201053Sjillescmdputs(const char *s)
130290111Simp{
1303201053Sjilles	const char *p;
1304201053Sjilles	char *q;
130525222Ssteve	char c;
13061556Srgrimes	int subtype = 0;
13071556Srgrimes
13081556Srgrimes	if (cmdnleft <= 0)
13091556Srgrimes		return;
13101556Srgrimes	p = s;
13111556Srgrimes	q = cmdnextc;
13121556Srgrimes	while ((c = *p++) != '\0') {
13131556Srgrimes		if (c == CTLESC)
13141556Srgrimes			*q++ = *p++;
13151556Srgrimes		else if (c == CTLVAR) {
13161556Srgrimes			*q++ = '$';
13171556Srgrimes			if (--cmdnleft > 0)
13181556Srgrimes				*q++ = '{';
13191556Srgrimes			subtype = *p++;
1320216246Sjilles			if ((subtype & VSTYPE) == VSLENGTH && --cmdnleft > 0)
1321216246Sjilles				*q++ = '#';
13221556Srgrimes		} else if (c == '=' && subtype != 0) {
1323216246Sjilles			*q = "}-+?=##%%\0X"[(subtype & VSTYPE) - VSNORMAL];
1324216246Sjilles			if (*q)
1325216246Sjilles				q++;
1326216246Sjilles			else
1327216246Sjilles				cmdnleft++;
1328216246Sjilles			if (((subtype & VSTYPE) == VSTRIMLEFTMAX ||
1329216246Sjilles			    (subtype & VSTYPE) == VSTRIMRIGHTMAX) &&
1330216246Sjilles			    --cmdnleft > 0)
1331216246Sjilles				*q = q[-1], q++;
13321556Srgrimes			subtype = 0;
13331556Srgrimes		} else if (c == CTLENDVAR) {
13341556Srgrimes			*q++ = '}';
1335216246Sjilles		} else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE) {
1336216246Sjilles			cmdnleft -= 5;
1337216246Sjilles			if (cmdnleft > 0) {
1338216246Sjilles				*q++ = '$';
1339216246Sjilles				*q++ = '(';
1340216246Sjilles				*q++ = '.';
1341216246Sjilles				*q++ = '.';
1342216246Sjilles				*q++ = '.';
1343216246Sjilles				*q++ = ')';
1344216246Sjilles			}
1345216246Sjilles		} else if (c == CTLARI) {
1346216246Sjilles			cmdnleft -= 2;
1347216246Sjilles			if (cmdnleft > 0) {
1348216246Sjilles				*q++ = '$';
1349216246Sjilles				*q++ = '(';
1350216246Sjilles				*q++ = '(';
1351216246Sjilles			}
1352216246Sjilles			p++;
1353216246Sjilles		} else if (c == CTLENDARI) {
1354216246Sjilles			if (--cmdnleft > 0) {
1355216246Sjilles				*q++ = ')';
1356216246Sjilles				*q++ = ')';
1357216246Sjilles			}
1358216246Sjilles		} else if (c == CTLQUOTEMARK || c == CTLQUOTEEND)
1359216246Sjilles			cmdnleft++; /* ignore */
13601556Srgrimes		else
13611556Srgrimes			*q++ = c;
13621556Srgrimes		if (--cmdnleft <= 0) {
13631556Srgrimes			*q++ = '.';
13641556Srgrimes			*q++ = '.';
13651556Srgrimes			*q++ = '.';
13661556Srgrimes			break;
13671556Srgrimes		}
13681556Srgrimes	}
13691556Srgrimes	cmdnextc = q;
13701556Srgrimes}
1371