xref: /netbsd-src/bin/sh/jobs.c (revision d1c86d363ded009f6c33bcf689f64a39b49f56b7)
1*d1c86d36Skre /*	$NetBSD: jobs.c,v 1.123 2024/10/09 13:43:32 kre Exp $	*/
249f0ad86Scgd 
361f28255Scgd /*-
437ed7877Sjtc  * Copyright (c) 1991, 1993
537ed7877Sjtc  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * This code is derived from software contributed to Berkeley by
861f28255Scgd  * Kenneth Almquist.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
18b5b29542Sagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
35cd799663Schristos #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
3749f0ad86Scgd #if 0
3807bae7edSchristos static char sccsid[] = "@(#)jobs.c	8.5 (Berkeley) 5/4/95";
3949f0ad86Scgd #else
40*d1c86d36Skre __RCSID("$NetBSD: jobs.c,v 1.123 2024/10/09 13:43:32 kre Exp $");
4149f0ad86Scgd #endif
4261f28255Scgd #endif /* not lint */
4361f28255Scgd 
444e9fc30dSkre #include <stdio.h>
4507bae7edSchristos #include <fcntl.h>
4607bae7edSchristos #include <signal.h>
4707bae7edSchristos #include <errno.h>
4807bae7edSchristos #include <unistd.h>
4907bae7edSchristos #include <stdlib.h>
50618cca24Sfair #include <paths.h>
5107bae7edSchristos #include <sys/types.h>
5207bae7edSchristos #include <sys/param.h>
5307bae7edSchristos #ifdef BSD
5407bae7edSchristos #include <sys/wait.h>
5507bae7edSchristos #include <sys/time.h>
5607bae7edSchristos #include <sys/resource.h>
5707bae7edSchristos #endif
58a20d7942Schristos #include <sys/ioctl.h>
5907bae7edSchristos 
6061f28255Scgd #include "shell.h"
6161f28255Scgd #if JOBS
62a20d7942Schristos #if OLD_TTY_DRIVER
6361f28255Scgd #include "sgtty.h"
64a20d7942Schristos #else
65a20d7942Schristos #include <termios.h>
66a20d7942Schristos #endif
6761f28255Scgd #undef CEOF			/* syntax.h redefines this */
6861f28255Scgd #endif
6907bae7edSchristos #include "redir.h"
7007bae7edSchristos #include "show.h"
7161f28255Scgd #include "main.h"
7261f28255Scgd #include "parser.h"
7361f28255Scgd #include "nodes.h"
7461f28255Scgd #include "jobs.h"
754e9fc30dSkre #include "var.h"
7661f28255Scgd #include "options.h"
774fc4fe2eSchristos #include "builtins.h"
7861f28255Scgd #include "trap.h"
7961f28255Scgd #include "syntax.h"
8061f28255Scgd #include "input.h"
8161f28255Scgd #include "output.h"
8261f28255Scgd #include "memalloc.h"
8361f28255Scgd #include "error.h"
8461f28255Scgd #include "mystring.h"
8561f28255Scgd 
8661f28255Scgd 
879572a5c2Skre #ifndef	WCONTINUED
889572a5c2Skre #define	WCONTINUED 0		/* So we can compile on old systems */
899572a5c2Skre #endif
909572a5c2Skre #ifndef	WIFCONTINUED
919572a5c2Skre #define	WIFCONTINUED(x)	(0)		/* ditto */
929572a5c2Skre #endif
939572a5c2Skre 
949572a5c2Skre 
95c02b3bbdSchristos static struct job *jobtab;		/* array of jobs */
96c02b3bbdSchristos static int njobs;			/* size of array */
97c02b3bbdSchristos static int jobs_invalid;		/* set in child */
98c02b3bbdSchristos MKINIT pid_t backgndpid = -1;	/* pid of last background process */
9961f28255Scgd #if JOBS
10061f28255Scgd int initialpgrp;		/* pgrp of shell on invocation */
101c02b3bbdSchristos static int curjob = -1;		/* current job */
10261f28255Scgd #endif
103e6ab6d48Schristos static int ttyfd = -1;
10461f28255Scgd 
105c02b3bbdSchristos STATIC void restartjob(struct job *);
106c02b3bbdSchristos STATIC void freejob(struct job *);
107c02b3bbdSchristos STATIC struct job *getjob(const char *, int);
1084e9fc30dSkre STATIC int dowait(int, struct job *, struct job **);
10997f12feaSchristos #define WBLOCK	1
11097f12feaSchristos #define WNOFREE 2
1119572a5c2Skre #define WSILENT 4
112bffe5190Skre STATIC int jobstatus(const struct job *, int);
113c02b3bbdSchristos STATIC int waitproc(int, struct job *, int *);
114601249a2Skre STATIC int cmdtxt(union node *, int);
115c02b3bbdSchristos STATIC void cmdlist(union node *, int);
116c02b3bbdSchristos STATIC void cmdputs(const char *);
1170fe4e128Skre inline static void cmdputi(int);
11861f28255Scgd 
11921f00868Skre #define	JNUM(j)	((int)((j) != NULL ? ((j) - jobtab) + 1 : 0))
12021f00868Skre 
12172c2627fSdholland #ifdef SYSV
12272c2627fSdholland STATIC int onsigchild(void);
12372c2627fSdholland #endif
12472c2627fSdholland 
125e6ab6d48Schristos #ifdef OLD_TTY_DRIVER
126c02b3bbdSchristos static pid_t tcgetpgrp(int fd);
127c02b3bbdSchristos static int tcsetpgrp(int fd, pid_t pgrp);
128e6ab6d48Schristos 
129e6ab6d48Schristos static pid_t
130c02b3bbdSchristos tcgetpgrp(int fd)
131e6ab6d48Schristos {
132e6ab6d48Schristos 	pid_t pgrp;
133e6ab6d48Schristos 	if (ioctl(fd, TIOCGPGRP, (char *)&pgrp) == -1)
134e6ab6d48Schristos 		return -1;
135e6ab6d48Schristos 	else
136e6ab6d48Schristos 		return pgrp;
137e6ab6d48Schristos }
138e6ab6d48Schristos 
139e6ab6d48Schristos static int
140c02b3bbdSchristos tcsetpgrp(int fd, pid_tpgrp)
141e6ab6d48Schristos {
142e6ab6d48Schristos 	return ioctl(fd, TIOCSPGRP, (char *)&pgrp);
143e6ab6d48Schristos }
144e6ab6d48Schristos #endif
14561f28255Scgd 
14651c4dfe4Skre static void
14751c4dfe4Skre ttyfd_change(int from, int to)
14851c4dfe4Skre {
14951c4dfe4Skre 	if (ttyfd == from)
15051c4dfe4Skre 		ttyfd = to;
15151c4dfe4Skre }
15251c4dfe4Skre 
15361f28255Scgd /*
15461f28255Scgd  * Turn job control on and off.
15561f28255Scgd  *
15661f28255Scgd  * Note:  This code assumes that the third arg to ioctl is a character
15761f28255Scgd  * pointer, which is true on Berkeley systems but not System V.  Since
15861f28255Scgd  * System V doesn't have job control yet, this isn't a problem now.
15961f28255Scgd  */
16061f28255Scgd 
16161f28255Scgd MKINIT int jobctl;
16261f28255Scgd 
16361f28255Scgd void
164c02b3bbdSchristos setjobctl(int on)
1655dad1439Scgd {
16637ed7877Sjtc #ifdef OLD_TTY_DRIVER
16761f28255Scgd 	int ldisc;
16837ed7877Sjtc #endif
16961f28255Scgd 
17061f28255Scgd 	if (on == jobctl || rootshell == 0)
17161f28255Scgd 		return;
17261f28255Scgd 	if (on) {
173e6ab6d48Schristos #if defined(FIOCLEX) || defined(FD_CLOEXEC)
17466b91257Schristos 		int i;
17551c4dfe4Skre 
176e6ab6d48Schristos 		if (ttyfd != -1)
17751c4dfe4Skre 			sh_close(ttyfd);
178de525ac6Schristos 		if ((ttyfd = open("/dev/tty", O_RDWR)) == -1) {
179de525ac6Schristos 			for (i = 0; i < 3; i++) {
180de525ac6Schristos 				if (isatty(i) && (ttyfd = dup(i)) != -1)
181de525ac6Schristos 					break;
182de525ac6Schristos 			}
183de525ac6Schristos 			if (i == 3)
184e6ab6d48Schristos 				goto out;
185de525ac6Schristos 		}
1861fad4bb6Schristos 		ttyfd = to_upper_fd(ttyfd);	/* Move to a high fd */
18751c4dfe4Skre 		register_sh_fd(ttyfd, ttyfd_change);
188e6ab6d48Schristos #else
189e6ab6d48Schristos 		out2str("sh: Need FIOCLEX or FD_CLOEXEC to support job control");
190e6ab6d48Schristos 		goto out;
191e6ab6d48Schristos #endif
192e6ab6d48Schristos 		if ((initialpgrp = tcgetpgrp(ttyfd)) < 0) {
193e6ab6d48Schristos  out:
19437ed7877Sjtc 			out2str("sh: can't access tty; job control turned off\n");
19537ed7877Sjtc 			mflag = 0;
19661f28255Scgd 			return;
19761f28255Scgd 		}
19861f28255Scgd 		if (initialpgrp == -1)
199df81ab82Sjtc 			initialpgrp = getpgrp();
200074335e1Srillig 		else if (initialpgrp != getpgrp())
201e6ab6d48Schristos 			killpg(0, SIGTTIN);
202e6ab6d48Schristos 
20337ed7877Sjtc #ifdef OLD_TTY_DRIVER
204e6ab6d48Schristos 		if (ioctl(ttyfd, TIOCGETD, (char *)&ldisc) < 0
205e6ab6d48Schristos 		    || ldisc != NTTYDISC) {
20637ed7877Sjtc 			out2str("sh: need new tty driver to run job control; job control turned off\n");
20737ed7877Sjtc 			mflag = 0;
20861f28255Scgd 			return;
20961f28255Scgd 		}
21037ed7877Sjtc #endif
211edcb4544Schristos 		setsignal(SIGTSTP, 0);
212edcb4544Schristos 		setsignal(SIGTTOU, 0);
213edcb4544Schristos 		setsignal(SIGTTIN, 0);
214964f98e7Stv 		if (getpgrp() != rootpid && setpgid(0, rootpid) == -1)
215edcb4544Schristos 			error("Cannot set process group (%s) at %d",
216edcb4544Schristos 			    strerror(errno), __LINE__);
217edcb4544Schristos 		if (tcsetpgrp(ttyfd, rootpid) == -1)
218edcb4544Schristos 			error("Cannot set tty process group (%s) at %d",
219edcb4544Schristos 			    strerror(errno), __LINE__);
22061f28255Scgd 	} else { /* turning job control off */
221964f98e7Stv 		if (getpgrp() != initialpgrp && setpgid(0, initialpgrp) == -1)
222edcb4544Schristos 			error("Cannot set process group (%s) at %d",
223edcb4544Schristos 			    strerror(errno), __LINE__);
224edcb4544Schristos 		if (tcsetpgrp(ttyfd, initialpgrp) == -1)
225edcb4544Schristos 			error("Cannot set tty process group (%s) at %d",
226edcb4544Schristos 			    strerror(errno), __LINE__);
22751c4dfe4Skre 		sh_close(ttyfd);
228e6ab6d48Schristos 		ttyfd = -1;
229edcb4544Schristos 		setsignal(SIGTSTP, 0);
230edcb4544Schristos 		setsignal(SIGTTOU, 0);
231edcb4544Schristos 		setsignal(SIGTTIN, 0);
23261f28255Scgd 	}
23361f28255Scgd 	jobctl = on;
23461f28255Scgd }
23561f28255Scgd 
23661f28255Scgd 
23761f28255Scgd #ifdef mkinit
23807bae7edSchristos INCLUDE <stdlib.h>
23961f28255Scgd 
24061f28255Scgd SHELLPROC {
24161f28255Scgd 	backgndpid = -1;
24261f28255Scgd #if JOBS
24361f28255Scgd 	jobctl = 0;
24461f28255Scgd #endif
24561f28255Scgd }
24661f28255Scgd 
24761f28255Scgd #endif
24861f28255Scgd 
24961f28255Scgd 
25061f28255Scgd 
25161f28255Scgd #if JOBS
252658a58d0Sdsl static int
253658a58d0Sdsl do_fgcmd(const char *arg_ptr)
2545dad1439Scgd {
25561f28255Scgd 	struct job *jp;
256edcb4544Schristos 	int i;
25761f28255Scgd 	int status;
25861f28255Scgd 
2599572a5c2Skre 	if (jobs_invalid)
2609572a5c2Skre 		error("No current jobs");
261658a58d0Sdsl 	jp = getjob(arg_ptr, 0);
26261f28255Scgd 	if (jp->jobctl == 0)
26361f28255Scgd 		error("job not created under job control");
264c02b3bbdSchristos 	out1fmt("%s", jp->ps[0].cmd);
265c02b3bbdSchristos 	for (i = 1; i < jp->nprocs; i++)
266c02b3bbdSchristos 		out1fmt(" | %s", jp->ps[i].cmd );
267c02b3bbdSchristos 	out1c('\n');
268c02b3bbdSchristos 	flushall();
269edcb4544Schristos 
2701c627bdfSkre 	if (tcsetpgrp(ttyfd, jp->pgrp) == -1) {
271edcb4544Schristos 		error("Cannot set tty process group (%s) at %d",
272edcb4544Schristos 		    strerror(errno), __LINE__);
273edcb4544Schristos 	}
27461f28255Scgd 	INTOFF;
275e8999de4Skre 	restartjob(jp);
27661f28255Scgd 	status = waitforjob(jp);
27761f28255Scgd 	INTON;
27861f28255Scgd 	return status;
27961f28255Scgd }
28061f28255Scgd 
281658a58d0Sdsl int
282658a58d0Sdsl fgcmd(int argc, char **argv)
283658a58d0Sdsl {
284658a58d0Sdsl 	nextopt("");
285658a58d0Sdsl 	return do_fgcmd(*argptr);
286658a58d0Sdsl }
287658a58d0Sdsl 
288658a58d0Sdsl int
289658a58d0Sdsl fgcmd_percent(int argc, char **argv)
290658a58d0Sdsl {
291658a58d0Sdsl 	nextopt("");
292658a58d0Sdsl 	return do_fgcmd(*argv);
293658a58d0Sdsl }
294658a58d0Sdsl 
295c02b3bbdSchristos static void
296c02b3bbdSchristos set_curjob(struct job *jp, int mode)
297c02b3bbdSchristos {
298c02b3bbdSchristos 	struct job *jp1, *jp2;
299c02b3bbdSchristos 	int i, ji;
300c02b3bbdSchristos 
301c02b3bbdSchristos 	ji = jp - jobtab;
302c02b3bbdSchristos 
303c02b3bbdSchristos 	/* first remove from list */
304c02b3bbdSchristos 	if (ji == curjob)
305c02b3bbdSchristos 		curjob = jp->prev_job;
306c02b3bbdSchristos 	else {
307c02b3bbdSchristos 		for (i = 0; i < njobs; i++) {
308c02b3bbdSchristos 			if (jobtab[i].prev_job != ji)
309c02b3bbdSchristos 				continue;
310c02b3bbdSchristos 			jobtab[i].prev_job = jp->prev_job;
311c02b3bbdSchristos 			break;
312c02b3bbdSchristos 		}
313c02b3bbdSchristos 	}
314c02b3bbdSchristos 
315c02b3bbdSchristos 	/* Then re-insert in correct position */
316c02b3bbdSchristos 	switch (mode) {
317c02b3bbdSchristos 	case 0:	/* job being deleted */
318c02b3bbdSchristos 		jp->prev_job = -1;
319c02b3bbdSchristos 		break;
320c02b3bbdSchristos 	case 1:	/* newly created job or backgrounded job,
321c02b3bbdSchristos 		   put after all stopped jobs. */
322c02b3bbdSchristos 		if (curjob != -1 && jobtab[curjob].state == JOBSTOPPED) {
323c02b3bbdSchristos 			for (jp1 = jobtab + curjob; ; jp1 = jp2) {
324c02b3bbdSchristos 				if (jp1->prev_job == -1)
325c02b3bbdSchristos 					break;
326c02b3bbdSchristos 				jp2 = jobtab + jp1->prev_job;
327c02b3bbdSchristos 				if (jp2->state != JOBSTOPPED)
328c02b3bbdSchristos 					break;
329c02b3bbdSchristos 			}
330c02b3bbdSchristos 			jp->prev_job = jp1->prev_job;
331c02b3bbdSchristos 			jp1->prev_job = ji;
332c02b3bbdSchristos 			break;
333c02b3bbdSchristos 		}
334c02b3bbdSchristos 		/* FALLTHROUGH */
335c02b3bbdSchristos 	case 2:	/* newly stopped job - becomes curjob */
336c02b3bbdSchristos 		jp->prev_job = curjob;
337c02b3bbdSchristos 		curjob = ji;
338c02b3bbdSchristos 		break;
339c02b3bbdSchristos 	}
340c02b3bbdSchristos }
34161f28255Scgd 
3425dad1439Scgd int
343c02b3bbdSchristos bgcmd(int argc, char **argv)
3445dad1439Scgd {
34561f28255Scgd 	struct job *jp;
346c02b3bbdSchristos 	int i;
34761f28255Scgd 
348c02b3bbdSchristos 	nextopt("");
3499572a5c2Skre 	if (jobs_invalid)
3509572a5c2Skre 		error("No current jobs");
35161f28255Scgd 	do {
352c02b3bbdSchristos 		jp = getjob(*argptr, 0);
35361f28255Scgd 		if (jp->jobctl == 0)
35461f28255Scgd 			error("job not created under job control");
355c02b3bbdSchristos 		set_curjob(jp, 1);
35621f00868Skre 		out1fmt("[%d] %s", JNUM(jp), jp->ps[0].cmd);
357c02b3bbdSchristos 		for (i = 1; i < jp->nprocs; i++)
358c02b3bbdSchristos 			out1fmt(" | %s", jp->ps[i].cmd );
359c02b3bbdSchristos 		out1c('\n');
360c02b3bbdSchristos 		flushall();
36161f28255Scgd 		restartjob(jp);
362c02b3bbdSchristos 	} while (*argptr && *++argptr);
36361f28255Scgd 	return 0;
36461f28255Scgd }
36561f28255Scgd 
36661f28255Scgd 
36761f28255Scgd STATIC void
368c02b3bbdSchristos restartjob(struct job *jp)
36961f28255Scgd {
37061f28255Scgd 	struct procstat *ps;
371ccf5ffdbSkre 	int i, e;
37261f28255Scgd 
37361f28255Scgd 	if (jp->state == JOBDONE)
37461f28255Scgd 		return;
3751c627bdfSkre 	if (jp->pgrp == 0)
3761c627bdfSkre 		error("Job [%d] does not have a process group", JNUM(jp));
3771c627bdfSkre 
37861f28255Scgd 	INTOFF;
379ccf5ffdbSkre 	for (e = i = 0; i < jp->nprocs; i++) {
3807a2f8a05Skre 		/*
3817a2f8a05Skre 		 * Don't touch a process we already waited for and collected
3827a2f8a05Skre 		 * exit status, that pid may have been reused for something
3837a2f8a05Skre 		 * else - even another of our jobs
3847a2f8a05Skre 		 */
3857a2f8a05Skre 		if (jp->ps[i].status != -1 && !WIFSTOPPED(jp->ps[i].status))
3867a2f8a05Skre 			continue;
3877a2f8a05Skre 
3887a2f8a05Skre 		/*
3897a2f8a05Skre 		 * Otherwise tell it to continue, if it worked, we're done
3907a2f8a05Skre 		 * (we signal the whole process group)
3917a2f8a05Skre 		 */
3921c627bdfSkre 		if (killpg(jp->pgrp, SIGCONT) != -1)
393edcb4544Schristos 			break;
394ccf5ffdbSkre 		e = errno;
3951c627bdfSkre 		break;		/* no point trying again */
396ccf5ffdbSkre 	}
3971c627bdfSkre 
3981c627bdfSkre 	if (e != 0)
3991c627bdfSkre 		error("Cannot continue job (%s)", strerror(e));
4001c627bdfSkre 	else if (i >= jp->nprocs)
4011c627bdfSkre 		error("Job [%d] has no stopped processes", JNUM(jp));
4027a2f8a05Skre 
4037a2f8a05Skre 	/*
4047a2f8a05Skre 	 * Now change state of all stopped processes in the job to running
4057a2f8a05Skre 	 * If there were any, the job is now running as well.
4067a2f8a05Skre 	 */
40761f28255Scgd 	for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
4087f2a2717Schristos 		if (WIFSTOPPED(ps->status)) {
409f697d47eSkre 			VTRACE(DBG_JOBS, (
41021f00868Skre 			   "restartjob: [%d] pid %d status change"
411f697d47eSkre 			   " from %#x (stopped) to -1 (running)\n",
41221f00868Skre 			   JNUM(jp), ps->pid, ps->status));
41361f28255Scgd 			ps->status = -1;
414c02b3bbdSchristos 			jp->state = JOBRUNNING;
41561f28255Scgd 		}
41661f28255Scgd 	}
41761f28255Scgd 	INTON;
41861f28255Scgd }
41961f28255Scgd #endif
42061f28255Scgd 
4210fe4e128Skre inline static void
4223c6d76cdSkre cmdputi(int n)
4233c6d76cdSkre {
4243c6d76cdSkre 	char str[20];
4253c6d76cdSkre 
4263c6d76cdSkre 	fmtstr(str, sizeof str, "%d", n);
4273c6d76cdSkre 	cmdputs(str);
4283c6d76cdSkre }
4293c6d76cdSkre 
430c02b3bbdSchristos static void
431c02b3bbdSchristos showjob(struct output *out, struct job *jp, int mode)
432c02b3bbdSchristos {
433c02b3bbdSchristos 	int procno;
434c02b3bbdSchristos 	int st;
435c02b3bbdSchristos 	struct procstat *ps;
436c02b3bbdSchristos 	int col;
437c02b3bbdSchristos 	char s[64];
438c02b3bbdSchristos 
439c02b3bbdSchristos #if JOBS
440c02b3bbdSchristos 	if (mode & SHOW_PGID) {
4411c627bdfSkre 		/* output only the process group ID (lead process ID) */
442f55c8670Skre 		outfmt(out, "%ld\n",
443f55c8670Skre 		    jp->pgrp != 0 ? (long)jp->pgrp : (long)jp->ps->pid);
444c02b3bbdSchristos 		return;
445c02b3bbdSchristos 	}
446c02b3bbdSchristos #endif
447c02b3bbdSchristos 
448c02b3bbdSchristos 	procno = jp->nprocs;
449c02b3bbdSchristos 	if (!procno)
450c02b3bbdSchristos 		return;
451c02b3bbdSchristos 
452c02b3bbdSchristos 	if (mode & SHOW_PID)
453c02b3bbdSchristos 		mode |= SHOW_MULTILINE;
454c02b3bbdSchristos 
455c02b3bbdSchristos 	if ((procno > 1 && !(mode & SHOW_MULTILINE))
456c02b3bbdSchristos 	    || (mode & SHOW_SIGNALLED)) {
457c02b3bbdSchristos 		/* See if we have more than one status to report */
458c02b3bbdSchristos 		ps = jp->ps;
459c02b3bbdSchristos 		st = ps->status;
460c02b3bbdSchristos 		do {
461c02b3bbdSchristos 			int st1 = ps->status;
462c02b3bbdSchristos 			if (st1 != st)
463c02b3bbdSchristos 				/* yes - need multi-line output */
464c02b3bbdSchristos 				mode |= SHOW_MULTILINE;
465c02b3bbdSchristos 			if (st1 == -1 || !(mode & SHOW_SIGNALLED) || WIFEXITED(st1))
466c02b3bbdSchristos 				continue;
467c02b3bbdSchristos 			if (WIFSTOPPED(st1) || ((st1 = WTERMSIG(st1) & 0x7f)
468c02b3bbdSchristos 			    && st1 != SIGINT && st1 != SIGPIPE))
469c02b3bbdSchristos 				mode |= SHOW_ISSIG;
470c02b3bbdSchristos 
471c02b3bbdSchristos 		} while (ps++, --procno);
472c02b3bbdSchristos 		procno = jp->nprocs;
473c02b3bbdSchristos 	}
474c02b3bbdSchristos 
475c02b3bbdSchristos 	if (mode & SHOW_SIGNALLED && !(mode & SHOW_ISSIG)) {
476c02b3bbdSchristos 		if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) {
47716bbc7c6Skre 			VTRACE(DBG_JOBS, ("showjob: freeing job %d\n",
47821f00868Skre 			    JNUM(jp)));
479c02b3bbdSchristos 			freejob(jp);
480c02b3bbdSchristos 		}
481c02b3bbdSchristos 		return;
482c02b3bbdSchristos 	}
483c02b3bbdSchristos 
484c02b3bbdSchristos 	for (ps = jp->ps; --procno >= 0; ps++) {	/* for each process */
485c02b3bbdSchristos 		if (ps == jp->ps)
48621f00868Skre 			fmtstr(s, 16, "[%d] %c ",
48721f00868Skre 				JNUM(jp),
488c02b3bbdSchristos #if JOBS
48913689a6cSkre 				jp - jobtab == curjob ?
49013689a6cSkre 									  '+' :
49113689a6cSkre 				curjob != -1 &&
49213689a6cSkre 				    jp - jobtab == jobtab[curjob].prev_job ?
49313689a6cSkre 									  '-' :
494c02b3bbdSchristos #endif
495c02b3bbdSchristos 				' ');
496c02b3bbdSchristos 		else
497c02b3bbdSchristos 			fmtstr(s, 16, "      " );
498c02b3bbdSchristos 		col = strlen(s);
499c02b3bbdSchristos 		if (mode & SHOW_PID) {
500c02b3bbdSchristos 			fmtstr(s + col, 16, "%ld ", (long)ps->pid);
501c02b3bbdSchristos 			     col += strlen(s + col);
502c02b3bbdSchristos 		}
503c02b3bbdSchristos 		if (ps->status == -1) {
504c02b3bbdSchristos 			scopy("Running", s + col);
505c02b3bbdSchristos 		} else if (WIFEXITED(ps->status)) {
506c02b3bbdSchristos 			st = WEXITSTATUS(ps->status);
507c02b3bbdSchristos 			if (st)
508c02b3bbdSchristos 				fmtstr(s + col, 16, "Done(%d)", st);
509c02b3bbdSchristos 			else
510c02b3bbdSchristos 				fmtstr(s + col, 16, "Done");
511c02b3bbdSchristos 		} else {
512c02b3bbdSchristos #if JOBS
513c02b3bbdSchristos 			if (WIFSTOPPED(ps->status))
514c02b3bbdSchristos 				st = WSTOPSIG(ps->status);
515c02b3bbdSchristos 			else /* WIFSIGNALED(ps->status) */
516c02b3bbdSchristos #endif
517c02b3bbdSchristos 				st = WTERMSIG(ps->status);
518185226c2Skre 			scopyn(strsignal(st), s + col, 32);
519c02b3bbdSchristos 			if (WCOREDUMP(ps->status)) {
520c02b3bbdSchristos 				col += strlen(s + col);
521c02b3bbdSchristos 				scopyn(" (core dumped)", s + col,  64 - col);
522c02b3bbdSchristos 			}
523c02b3bbdSchristos 		}
524c02b3bbdSchristos 		col += strlen(s + col);
525c02b3bbdSchristos 		outstr(s, out);
526c02b3bbdSchristos 		do {
527c02b3bbdSchristos 			outc(' ', out);
528c02b3bbdSchristos 			col++;
529c02b3bbdSchristos 		} while (col < 30);
530c02b3bbdSchristos 		outstr(ps->cmd, out);
531c02b3bbdSchristos 		if (mode & SHOW_MULTILINE) {
532c02b3bbdSchristos 			if (procno > 0) {
533c02b3bbdSchristos 				outc(' ', out);
534c02b3bbdSchristos 				outc('|', out);
535c02b3bbdSchristos 			}
536c02b3bbdSchristos 		} else {
537c02b3bbdSchristos 			while (--procno >= 0)
538c02b3bbdSchristos 				outfmt(out, " | %s", (++ps)->cmd );
539c02b3bbdSchristos 		}
540c02b3bbdSchristos 		outc('\n', out);
541c02b3bbdSchristos 	}
542c02b3bbdSchristos 	flushout(out);
5434e9fc30dSkre 	jp->flags &= ~JOBCHANGED;
544c02b3bbdSchristos 	if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE))
545c02b3bbdSchristos 		freejob(jp);
546c02b3bbdSchristos }
547c02b3bbdSchristos 
54861f28255Scgd int
549c02b3bbdSchristos jobscmd(int argc, char **argv)
5505dad1439Scgd {
551c02b3bbdSchristos 	int mode, m;
552c02b3bbdSchristos 
553c02b3bbdSchristos 	mode = 0;
5544e2a4778Schristos 	while ((m = nextopt("lpZ")))
5554e2a4778Schristos 		switch (m) {
5564e2a4778Schristos 		case 'l':
557c02b3bbdSchristos 			mode = SHOW_PID;
5584e2a4778Schristos 			break;
5594e2a4778Schristos 		case 'p':
560c02b3bbdSchristos 			mode = SHOW_PGID;
5614e2a4778Schristos 			break;
5624e2a4778Schristos 		case 'Z':
5634e2a4778Schristos 			mode = SHOW_PROCTITLE;
5644e2a4778Schristos 			break;
5654e2a4778Schristos 		}
5664e2a4778Schristos 
5674e2a4778Schristos 	if (mode == SHOW_PROCTITLE) {
568777df0a3Skre 		if (*argptr && **argptr)
5694e2a4778Schristos 			setproctitle("%s", *argptr);
570777df0a3Skre 		else
571777df0a3Skre 			setproctitle(NULL);
5724e2a4778Schristos 		return 0;
5734e2a4778Schristos 	}
574ccf5ffdbSkre 
575ccf5ffdbSkre 	if (!iflag && !posix)
5769572a5c2Skre 		mode |= SHOW_NO_FREE;
577ccf5ffdbSkre 
578ccf5ffdbSkre 	if (*argptr) {
579c02b3bbdSchristos 		do
580c02b3bbdSchristos 			showjob(out1, getjob(*argptr,0), mode);
581c02b3bbdSchristos 		while (*++argptr);
582ccf5ffdbSkre 	} else
583c02b3bbdSchristos 		showjobs(out1, mode);
58461f28255Scgd 	return 0;
58561f28255Scgd }
58661f28255Scgd 
58761f28255Scgd 
58861f28255Scgd /*
58961f28255Scgd  * Print a list of jobs.  If "change" is nonzero, only print jobs whose
59061f28255Scgd  * statuses have changed since the last call to showjobs.
59161f28255Scgd  *
59261f28255Scgd  * If the shell is interrupted in the process of creating a job, the
59361f28255Scgd  * result may be a job structure containing zero processes.  Such structures
59461f28255Scgd  * will be freed here.
59561f28255Scgd  */
59661f28255Scgd 
59761f28255Scgd void
598c02b3bbdSchristos showjobs(struct output *out, int mode)
5995dad1439Scgd {
60061f28255Scgd 	int jobno;
60161f28255Scgd 	struct job *jp;
602edcb4544Schristos 	int silent = 0, gotpid;
60361f28255Scgd 
60416bbc7c6Skre 	CTRACE(DBG_JOBS, ("showjobs(%x) called\n", mode));
605edcb4544Schristos 
6067a2f8a05Skre 	/*  Collect everything pending in the kernel */
6077a2f8a05Skre 	if ((gotpid = dowait(WSILENT, NULL, NULL)) > 0)
6084e9fc30dSkre 		while (dowait(WSILENT, NULL, NULL) > 0)
609edcb4544Schristos 			continue;
610edcb4544Schristos #ifdef JOBS
611edcb4544Schristos 	/*
612edcb4544Schristos 	 * Check if we are not in our foreground group, and if not
613edcb4544Schristos 	 * put us in it.
614edcb4544Schristos 	 */
615f3dfd6e6Schristos 	if (mflag && gotpid != -1 && tcgetpgrp(ttyfd) != getpid()) {
616edcb4544Schristos 		if (tcsetpgrp(ttyfd, getpid()) == -1)
617edcb4544Schristos 			error("Cannot set tty process group (%s) at %d",
618edcb4544Schristos 			    strerror(errno), __LINE__);
61916bbc7c6Skre 		VTRACE(DBG_JOBS|DBG_INPUT, ("repaired tty process group\n"));
620edcb4544Schristos 		silent = 1;
621edcb4544Schristos 	}
622edcb4544Schristos #endif
623c02b3bbdSchristos 
62461f28255Scgd 	for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
62561f28255Scgd 		if (!jp->used)
62661f28255Scgd 			continue;
62761f28255Scgd 		if (jp->nprocs == 0) {
628feb6abd7Skre 			if (!jobs_invalid)
62961f28255Scgd 				freejob(jp);
63061f28255Scgd 			continue;
63161f28255Scgd 		}
6324e9fc30dSkre 		if ((mode & SHOW_CHANGED) && !(jp->flags & JOBCHANGED))
63361f28255Scgd 			continue;
6344e9fc30dSkre 		if (silent && (jp->flags & JOBCHANGED)) {
6354e9fc30dSkre 			jp->flags &= ~JOBCHANGED;
636edcb4544Schristos 			continue;
637edcb4544Schristos 		}
638c02b3bbdSchristos 		showjob(out, jp, mode);
63961f28255Scgd 	}
64061f28255Scgd }
64161f28255Scgd 
64261f28255Scgd /*
64361f28255Scgd  * Mark a job structure as unused.
64461f28255Scgd  */
64561f28255Scgd 
64661f28255Scgd STATIC void
647c02b3bbdSchristos freejob(struct job *jp)
64861f28255Scgd {
64961f28255Scgd 	INTOFF;
650bac9369aSchristos 	if (jp->ps != &jp->ps0) {
65161f28255Scgd 		ckfree(jp->ps);
652bac9369aSchristos 		jp->ps = &jp->ps0;
653bac9369aSchristos 	}
654bac9369aSchristos 	jp->nprocs = 0;
65561f28255Scgd 	jp->used = 0;
65661f28255Scgd #if JOBS
657c02b3bbdSchristos 	set_curjob(jp, 0);
65861f28255Scgd #endif
65961f28255Scgd 	INTON;
66061f28255Scgd }
66161f28255Scgd 
662bffe5190Skre /*
663bffe5190Skre  * Extract the status of a completed job (for $?)
664bffe5190Skre  */
665bffe5190Skre STATIC int
666bffe5190Skre jobstatus(const struct job *jp, int raw)
667bffe5190Skre {
668bffe5190Skre 	int status = 0;
669bffe5190Skre 	int retval;
670bffe5190Skre 
671f53fd6e9Skre 	if ((jp->flags & JPIPEFAIL) && jp->nprocs) {
672bffe5190Skre 		int i;
673bffe5190Skre 
674bffe5190Skre 		for (i = 0; i < jp->nprocs; i++)
675bffe5190Skre 			if (jp->ps[i].status != 0)
676bffe5190Skre 				status = jp->ps[i].status;
677bffe5190Skre 	} else
678bffe5190Skre 		status = jp->ps[jp->nprocs ? jp->nprocs - 1 : 0].status;
679bffe5190Skre 
680bffe5190Skre 	if (raw)
681bffe5190Skre 		return status;
682bffe5190Skre 
683bffe5190Skre 	if (WIFEXITED(status))
684bffe5190Skre 		retval = WEXITSTATUS(status);
685bffe5190Skre #if JOBS
686bffe5190Skre 	else if (WIFSTOPPED(status))
687bffe5190Skre 		retval = WSTOPSIG(status) + 128;
688bffe5190Skre #endif
689bffe5190Skre 	else {
690bffe5190Skre 		/* XXX: limits number of signals */
691bffe5190Skre 		retval = WTERMSIG(status) + 128;
692bffe5190Skre 	}
693bffe5190Skre 
694bffe5190Skre 	return retval;
695bffe5190Skre }
696bffe5190Skre 
69761f28255Scgd 
69861f28255Scgd 
69961f28255Scgd int
700c02b3bbdSchristos waitcmd(int argc, char **argv)
7015dad1439Scgd {
7024e9fc30dSkre 	struct job *job, *last;
703bffe5190Skre 	int retval;
70461f28255Scgd 	struct job *jp;
7054e9fc30dSkre 	int i;
7064e9fc30dSkre 	int any = 0;
7074e9fc30dSkre 	int found;
70845f1e871Skre 	int oldwait = 0;
7094e9fc30dSkre 	char *pid = NULL, *fpid;
7104e9fc30dSkre 	char **arg;
7114e9fc30dSkre 	char idstring[20];
71261f28255Scgd 
7134e9fc30dSkre 	while ((i = nextopt("np:")) != '\0') {
7144e9fc30dSkre 		switch (i) {
7154e9fc30dSkre 		case 'n':
7164e9fc30dSkre 			any = 1;
7174e9fc30dSkre 			break;
7184e9fc30dSkre 		case 'p':
7194e9fc30dSkre 			if (pid)
7204e9fc30dSkre 				error("more than one -p unsupported");
7214e9fc30dSkre 			pid = optionarg;
7224e9fc30dSkre 			break;
7234e9fc30dSkre 		}
7244e9fc30dSkre 	}
7254e9fc30dSkre 
72645f1e871Skre 	if (!any && *argptr == 0)
72745f1e871Skre 		oldwait = 1;
72845f1e871Skre 
7294e9fc30dSkre 	if (pid != NULL) {
7304e9fc30dSkre 		if (!validname(pid, '\0', NULL))
7314e9fc30dSkre 			error("invalid name: -p '%s'", pid);
7324e9fc30dSkre 		if (unsetvar(pid, 0))
7334e9fc30dSkre 			error("%s readonly", pid);
7344e9fc30dSkre 	}
735c02b3bbdSchristos 
7369572a5c2Skre 	/*
7379572a5c2Skre 	 * If we have forked, and not yet created any new jobs, then
7389572a5c2Skre 	 * we have no children, whatever jobtab claims,
7399572a5c2Skre 	 * so simply return in that case.
7409572a5c2Skre 	 *
7419572a5c2Skre 	 * The return code is 127 if we had any pid args (none are found)
7424e9fc30dSkre 	 * or if we had -n (nothing exited), but 0 for plain old "wait".
7439572a5c2Skre 	 */
7444e9fc30dSkre 	if (jobs_invalid) {
7454e9fc30dSkre 		CTRACE(DBG_WAIT, ("builtin wait%s%s in child, invalid jobtab\n",
7464e9fc30dSkre 		    any ? " -n" : "", *argptr ? " pid..." : ""));
74745f1e871Skre 		return oldwait ? 0 : 127;
748c02b3bbdSchristos 	}
749c02b3bbdSchristos 
750da460725Skre 	/*
751da460725Skre 	 * clear stray flags left from previous waitcmd
752da460725Skre 	 * or set them instead if anything will do ("wait -n")
753da460725Skre 	 */
7544e9fc30dSkre 	for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
755da460725Skre 		if (any && *argptr == NULL)
756da460725Skre 			jp->flags |= JOBWANTED;
757da460725Skre 		else
7584e9fc30dSkre 			jp->flags &= ~JOBWANTED;
7594e9fc30dSkre 		jp->ref = NULL;
7604e9fc30dSkre 	}
7614e9fc30dSkre 
7624e9fc30dSkre 	CTRACE(DBG_WAIT,
7634e9fc30dSkre 	    ("builtin wait%s%s\n", any ? " -n" : "", *argptr ? " pid..." : ""));
7644e9fc30dSkre 
7654e9fc30dSkre 	/*
7664e9fc30dSkre 	 * First, validate the jobnum args, count how many refer to
7674e9fc30dSkre 	 * (different) running jobs, and if we had -n, and found that one has
7684e9fc30dSkre 	 * already finished, we return that one.   Otherwise remember
7694e9fc30dSkre 	 * which ones we are looking for (JOBWANTED).
7704e9fc30dSkre 	 */
7714e9fc30dSkre 	found = 0;
7724e9fc30dSkre 	last = NULL;
7734e9fc30dSkre 	for (arg = argptr; *arg; arg++) {
7744e9fc30dSkre 		last = jp = getjob(*arg, 1);
7754e9fc30dSkre 		if (!jp)
776c02b3bbdSchristos 			continue;
7774e9fc30dSkre 		if (jp->ref == NULL)
7784e9fc30dSkre 			jp->ref = *arg;
7794e9fc30dSkre 		if (any && jp->state == JOBDONE) {
7804e9fc30dSkre 			/*
7814e9fc30dSkre 			 * We just want any of them, and this one is
7824e9fc30dSkre 			 * ready for consumption, bon apetit ...
7834e9fc30dSkre 			 */
7844e9fc30dSkre 			retval = jobstatus(jp, 0);
7854e9fc30dSkre 			if (pid)
7864e9fc30dSkre 				setvar(pid, *arg, 0);
78761f28255Scgd 			if (!iflag)
7884e9fc30dSkre 				freejob(jp);
7894e9fc30dSkre 			CTRACE(DBG_WAIT, ("wait -n found %s already done: %d\n",			    *arg, retval));
7907f2a2717Schristos 			return retval;
79161f28255Scgd 		}
7924e9fc30dSkre 		if (!(jp->flags & JOBWANTED)) {
7934e9fc30dSkre 			/*
7944e9fc30dSkre 			 * It is possible to list the same job several
7954e9fc30dSkre 			 * times - the obvious "wait 1 1 1" or
7964e9fc30dSkre 			 * "wait %% %2 102" where job 2 is current and pid 102
7974e9fc30dSkre 			 * However many times it is requested, it is found once.
7984e9fc30dSkre 			 */
7994e9fc30dSkre 			found++;
8004e9fc30dSkre 			jp->flags |= JOBWANTED;
8014e9fc30dSkre 		}
8024e9fc30dSkre 		job = jp;
8034e9fc30dSkre 	}
80461f28255Scgd 
8054e9fc30dSkre 	VTRACE(DBG_WAIT, ("wait %s%s%sfound %d candidates (last %s)\n",
8064e9fc30dSkre 	    any ? "-n " : "", *argptr ? *argptr : "",
8074e9fc30dSkre 	    argptr[0] && argptr[1] ? "... " : " ", found,
8088821fc2cSkre 	    job && job->used ? (job->ref ? job->ref : "<no-arg>") : "none"));
8094e9fc30dSkre 
8104e9fc30dSkre 	/*
8114e9fc30dSkre 	 * If we were given a list of jobnums:
8124e9fc30dSkre 	 * and none of those exist, then we're done.
8134e9fc30dSkre 	 */
8144e9fc30dSkre 	if (*argptr && found == 0)
8154e9fc30dSkre 		return 127;
8164e9fc30dSkre 
8174e9fc30dSkre 	/*
8184e9fc30dSkre 	 * Otherwise we need to wait for something to complete
8194e9fc30dSkre 	 * When it does, we check and see if it is one of the
8204e9fc30dSkre 	 * jobs we're waiting on, and if so, we clean it up.
8214e9fc30dSkre 	 * If we had -n, then we're done, otherwise we do it all again
8224e9fc30dSkre 	 * until all we had listed are done, of if there were no
8234e9fc30dSkre 	 * jobnum args, all are done.
8244e9fc30dSkre 	 */
8254e9fc30dSkre 
8264e9fc30dSkre 	retval = any || *argptr ? 127 : 0;
8274e9fc30dSkre 	fpid = NULL;
8284e9fc30dSkre 	for (;;) {
8294e9fc30dSkre 		VTRACE(DBG_WAIT, ("wait waiting (%d remain): ", found));
830da460725Skre 		job = NULL;
8314e9fc30dSkre 		for (jp = jobtab, i = njobs; --i >= 0; jp++) {
8324e9fc30dSkre 			if (jp->used && jp->flags & JOBWANTED &&
833da460725Skre 			    jp->state == JOBDONE) {
834da460725Skre 				job = jp;
8354e9fc30dSkre 				break;
8364e9fc30dSkre 			}
837da460725Skre 			if (jp->used && jp->state == JOBRUNNING)
838da460725Skre 				job = jp;
839da460725Skre 		}
840da460725Skre 		if (i < 0 && job == NULL) {
8414e9fc30dSkre 			CTRACE(DBG_WAIT, ("nothing running (ret: %d) fpid %s\n",
8424e9fc30dSkre 			    retval, fpid ? fpid : "unset"));
8434e9fc30dSkre 			if (pid && fpid)
8444e9fc30dSkre 				setvar(pid, fpid, 0);
8454e9fc30dSkre 			return retval;
8464e9fc30dSkre 		}
847da460725Skre 		jp = job;
8484e9fc30dSkre 		VTRACE(DBG_WAIT, ("found @%d/%d state: %d\n", njobs-i, njobs,
8494e9fc30dSkre 		    jp->state));
8504e9fc30dSkre 
8514e9fc30dSkre 		/*
8524e9fc30dSkre 		 * There is at least 1 job running, so we can
8537a2f8a05Skre 		 * safely wait() (blocking) for something to exit.
8544e9fc30dSkre 		 */
8554e9fc30dSkre 		if (jp->state == JOBRUNNING) {
8564e9fc30dSkre 			job = NULL;
8574e9fc30dSkre 			if ((i = dowait(WBLOCK|WNOFREE, NULL, &job)) == -1)
8584e9fc30dSkre 			       return 128 + lastsig();
8594e9fc30dSkre 
8607a2f8a05Skre 			/*
8617a2f8a05Skre 			 * This happens if an interloper has died
8627a2f8a05Skre 			 * (eg: a child of the executable that exec'd us)
8637a2f8a05Skre 			 * Simply go back and start all over again
8647a2f8a05Skre 			 * (this is rare).
8657a2f8a05Skre 			 */
8667a2f8a05Skre 			if (job == NULL)
8677aa4a7e2Skre 				continue;
8687aa4a7e2Skre 
8694e9fc30dSkre 			/*
8707a2f8a05Skre 			 * one of the reported job's processes exited,
8717a2f8a05Skre 			 * but there are more still running, back for more
8724e9fc30dSkre 			 */
8734e9fc30dSkre 			if (job->state == JOBRUNNING)
8744e9fc30dSkre 				continue;
8754e9fc30dSkre 		} else
8764e9fc30dSkre 			job = jp;	/* we want this, and it is done */
8774e9fc30dSkre 
878da460725Skre 		if (job->flags & JOBWANTED) {
8794e9fc30dSkre 			int rv;
8804e9fc30dSkre 
8814e9fc30dSkre 			job->flags &= ~JOBWANTED;	/* got it */
8824e9fc30dSkre 			rv = jobstatus(job, 0);
8834e9fc30dSkre 			VTRACE(DBG_WAIT, (
8844e9fc30dSkre 			    "wanted %d (%s) done: st=%d", i,
8854e9fc30dSkre 			    job->ref ? job->ref : "", rv));
8864e9fc30dSkre 			if (any || job == last) {
8874e9fc30dSkre 				retval = rv;
8884e9fc30dSkre 				fpid = job->ref;
8894e9fc30dSkre 
8904e9fc30dSkre 				VTRACE(DBG_WAIT, (" save"));
8914e9fc30dSkre 				if (pid) {
8924e9fc30dSkre 				   /*
8934e9fc30dSkre 				    * don't need fpid unless we are going
8944e9fc30dSkre 				    * to return it.
8954e9fc30dSkre 				    */
8964e9fc30dSkre 				   if (fpid == NULL) {
8974e9fc30dSkre 					/*
8984e9fc30dSkre 					 * this only happens with "wait -n"
8994e9fc30dSkre 					 * (that is, no pid args)
9004e9fc30dSkre 					 */
9014e9fc30dSkre 					snprintf(idstring, sizeof idstring,
9024e9fc30dSkre 					    "%d", job->ps[ job->nprocs ?
9030827e1f9Skre 						    job->nprocs-1 : 0 ].pid);
9044e9fc30dSkre 					fpid = idstring;
9054e9fc30dSkre 				    }
9064e9fc30dSkre 				    VTRACE(DBG_WAIT, (" (for %s)", fpid));
9074e9fc30dSkre 				}
9084e9fc30dSkre 			}
9094e9fc30dSkre 
9104e9fc30dSkre 			if (job->state == JOBDONE) {
9114e9fc30dSkre 				VTRACE(DBG_WAIT, (" free"));
9124e9fc30dSkre 				freejob(job);
9134e9fc30dSkre 			}
9144e9fc30dSkre 
9154e9fc30dSkre 			if (any || (found > 0 && --found == 0)) {
9164e9fc30dSkre 				if (pid && fpid)
9174e9fc30dSkre 					setvar(pid, fpid, 0);
9184e9fc30dSkre 				VTRACE(DBG_WAIT, (" return %d\n", retval));
9194e9fc30dSkre 				return retval;
9204e9fc30dSkre 			}
9214e9fc30dSkre 			VTRACE(DBG_WAIT, ("\n"));
9224e9fc30dSkre 			continue;
9234e9fc30dSkre 		}
9244e9fc30dSkre 
9254e9fc30dSkre 		/* this is to handle "wait" (no args) */
92645f1e871Skre 		if (oldwait && job->state == JOBDONE) {
9274e9fc30dSkre 			VTRACE(DBG_JOBS|DBG_WAIT, ("Cleanup: %d\n", i));
9284e9fc30dSkre 			freejob(job);
9294e9fc30dSkre 		}
9304e9fc30dSkre 	}
9314e9fc30dSkre }
93261f28255Scgd 
93361f28255Scgd 
9345dad1439Scgd int
935c02b3bbdSchristos jobidcmd(int argc, char **argv)
9365dad1439Scgd {
93761f28255Scgd 	struct job *jp;
93861f28255Scgd 	int i;
939f697d47eSkre 	int pg = 0, onep = 0, job = 0;
94061f28255Scgd 
941f697d47eSkre 	while ((i = nextopt("gjp"))) {
942f697d47eSkre 		switch (i) {
943f697d47eSkre 		case 'g':	pg = 1;		break;
944f697d47eSkre 		case 'j':	job = 1;	break;
945f697d47eSkre 		case 'p':	onep = 1;	break;
946f697d47eSkre 		}
947f697d47eSkre 	}
948f697d47eSkre 	CTRACE(DBG_JOBS, ("jobidcmd%s%s%s%s %s\n", pg ? " -g" : "",
949f697d47eSkre 	    onep ? " -p" : "", job ? " -j" : "", jobs_invalid ? " [inv]" : "",
950f697d47eSkre 	    *argptr ? *argptr : "<implicit %%>"));
951f697d47eSkre 	if (pg + onep + job > 1)
952f697d47eSkre 		error("-g -j and -p options cannot be combined");
953f697d47eSkre 
954f697d47eSkre 	if (argptr[0] && argptr[1])
955f697d47eSkre 		error("usage: jobid [-g|-p|-r] jobid");
956f697d47eSkre 
957c02b3bbdSchristos 	jp = getjob(*argptr, 0);
958f697d47eSkre 	if (job) {
95921f00868Skre 		out1fmt("%%%d\n", JNUM(jp));
960f697d47eSkre 		return 0;
961f697d47eSkre 	}
962f697d47eSkre 	if (pg) {
963f697d47eSkre 		if (jp->pgrp != 0) {
964f697d47eSkre 			out1fmt("%ld\n", (long)jp->pgrp);
965f697d47eSkre 			return 0;
966f697d47eSkre 		}
967f697d47eSkre 		return 1;
968f697d47eSkre 	}
969f697d47eSkre 	if (onep) {
970f697d47eSkre 		i = jp->nprocs - 1;
971f697d47eSkre 		if (i < 0)
972f697d47eSkre 			return 1;
973f697d47eSkre 		out1fmt("%ld\n", (long)jp->ps[i].pid);
974f697d47eSkre 		return 0;
975f697d47eSkre 	}
97661f28255Scgd 	for (i = 0 ; i < jp->nprocs ; ) {
97796133865Schristos 		out1fmt("%ld", (long)jp->ps[i].pid);
97861f28255Scgd 		out1c(++i < jp->nprocs ? ' ' : '\n');
97961f28255Scgd 	}
98061f28255Scgd 	return 0;
98161f28255Scgd }
98261f28255Scgd 
983*d1c86d36Skre #if JOBS
984*d1c86d36Skre #ifndef SMALL
985*d1c86d36Skre 
986*d1c86d36Skre static int
987*d1c86d36Skre stop_me(int sig, int force, int pgrp, pid_t pid)
988*d1c86d36Skre {
989*d1c86d36Skre 	if (force || (!loginsh && mflag && rootshell)) {
990*d1c86d36Skre 		struct sigaction sig_dfl, sig_was;
991*d1c86d36Skre 
992*d1c86d36Skre 		sig_dfl.sa_handler = SIG_DFL;
993*d1c86d36Skre 		sig_dfl.sa_flags = 0;
994*d1c86d36Skre 		sigemptyset(&sig_dfl.sa_mask);
995*d1c86d36Skre 
996*d1c86d36Skre 		(void)sigaction(sig, &sig_dfl, &sig_was);
997*d1c86d36Skre 
998*d1c86d36Skre 		if (kill(pgrp ? 0 : pid, sig) == -1) {
999*d1c86d36Skre 			sh_warn("suspend myself");
1000*d1c86d36Skre 			(void)sigaction(sig, &sig_was, NULL);
1001*d1c86d36Skre 			error(NULL);
1002*d1c86d36Skre 		}
1003*d1c86d36Skre 
1004*d1c86d36Skre 		(void)sigaction(sig, &sig_was, NULL);
1005*d1c86d36Skre 
1006*d1c86d36Skre 		return 0;
1007*d1c86d36Skre 	}
1008*d1c86d36Skre 
1009*d1c86d36Skre 	if (!rootshell)
1010*d1c86d36Skre 		sh_warnx("subshell environment");
1011*d1c86d36Skre 	else if (!mflag)
1012*d1c86d36Skre 		sh_warnx("job control disabled");
1013*d1c86d36Skre 	else if (loginsh)
1014*d1c86d36Skre 		sh_warnx("login shell");
1015*d1c86d36Skre 	else
1016*d1c86d36Skre 		sh_warnx("not possible??");
1017*d1c86d36Skre 
1018*d1c86d36Skre 	return 1;
1019*d1c86d36Skre }
1020*d1c86d36Skre 
1021*d1c86d36Skre int
1022*d1c86d36Skre suspendcmd(int argc, char **argv)
1023*d1c86d36Skre {
1024*d1c86d36Skre 	int sig = SIGTSTP;
1025*d1c86d36Skre 	int force = 0;
1026*d1c86d36Skre 	int pgrp = 0;
1027*d1c86d36Skre 	int status = 0;
1028*d1c86d36Skre 	char *target;
1029*d1c86d36Skre 	int c;
1030*d1c86d36Skre 
1031*d1c86d36Skre 	while ((c = nextopt("fgs:")) != 0) {
1032*d1c86d36Skre 		switch (c) {
1033*d1c86d36Skre 		case 'f':
1034*d1c86d36Skre 			force = 1;
1035*d1c86d36Skre 			break;
1036*d1c86d36Skre 		case 'g':
1037*d1c86d36Skre 			pgrp = 1;
1038*d1c86d36Skre 			break;
1039*d1c86d36Skre 		case 's':
1040*d1c86d36Skre 			sig = signame_to_signum(optionarg);
1041*d1c86d36Skre 
1042*d1c86d36Skre 			if (sig != SIGSTOP && sig != SIGTSTP &&
1043*d1c86d36Skre 			    sig != SIGTTIN && sig != SIGTTOU)
1044*d1c86d36Skre 				error("bad signal '%s'", optionarg);
1045*d1c86d36Skre 			break;
1046*d1c86d36Skre 		}
1047*d1c86d36Skre 	}
1048*d1c86d36Skre 
1049*d1c86d36Skre 	if (!*argptr)		/* suspend myself */
1050*d1c86d36Skre 		return stop_me(sig, force, pgrp, getpid());
1051*d1c86d36Skre 
1052*d1c86d36Skre 	while ((target = *argptr++) != NULL)
1053*d1c86d36Skre 	{
1054*d1c86d36Skre 		int pid;
1055*d1c86d36Skre 
1056*d1c86d36Skre 		if (is_number(target)) {
1057*d1c86d36Skre 			if ((pid = number(target)) == 0) {
1058*d1c86d36Skre 				sh_warnx("Cannot (yet) suspend kernel (%s)",
1059*d1c86d36Skre 				    target);
1060*d1c86d36Skre 				status = 1;
1061*d1c86d36Skre 				continue;
1062*d1c86d36Skre 			}
1063*d1c86d36Skre 		} else if ((pid = getjobpgrp(target)) == 0) {
1064*d1c86d36Skre 			sh_warnx("Unknown job: %s", target);
1065*d1c86d36Skre 			status = 1;
1066*d1c86d36Skre 			continue;
1067*d1c86d36Skre 		}
1068*d1c86d36Skre 
1069*d1c86d36Skre 		if (pid == rootpid || pid == getpid()) {
1070*d1c86d36Skre 			status |= stop_me(sig, force, pgrp, pid);
1071*d1c86d36Skre 			continue;
1072*d1c86d36Skre 		}
1073*d1c86d36Skre 
1074*d1c86d36Skre 		if (pid == 1 || pid == -1) {
1075*d1c86d36Skre 			sh_warnx("Don't be funny");
1076*d1c86d36Skre 			status = 1;
1077*d1c86d36Skre 			continue;
1078*d1c86d36Skre 		}
1079*d1c86d36Skre 
1080*d1c86d36Skre 		if (pid > 0 && pgrp)
1081*d1c86d36Skre 			pid = -pid;
1082*d1c86d36Skre 
1083*d1c86d36Skre 		if (kill(pid, sig) == -1) {
1084*d1c86d36Skre 			sh_warn("failed to suspend %s", target);
1085*d1c86d36Skre 			status = 1;
1086*d1c86d36Skre 		}
1087*d1c86d36Skre 	}
1088*d1c86d36Skre 
1089*d1c86d36Skre 	return status;
1090*d1c86d36Skre }
1091*d1c86d36Skre #endif	/* SMALL */
1092*d1c86d36Skre #endif	/* JOBS */
1093*d1c86d36Skre 
1094c02b3bbdSchristos int
1095c02b3bbdSchristos getjobpgrp(const char *name)
1096c02b3bbdSchristos {
1097c02b3bbdSchristos 	struct job *jp;
109861f28255Scgd 
10999572a5c2Skre 	if (jobs_invalid)
1100*d1c86d36Skre 		return 0;
1101c02b3bbdSchristos 	jp = getjob(name, 1);
1102c02b3bbdSchristos 	if (jp == 0)
1103c02b3bbdSchristos 		return 0;
11048ad9ebd9Skre 	return -jp->pgrp;
1105c02b3bbdSchristos }
110661f28255Scgd 
110761f28255Scgd /*
110861f28255Scgd  * Convert a job name to a job structure.
110961f28255Scgd  */
111061f28255Scgd 
111161f28255Scgd STATIC struct job *
1112c02b3bbdSchristos getjob(const char *name, int noerror)
111361f28255Scgd {
1114c02b3bbdSchristos 	int jobno = -1;
111548250187Stls 	struct job *jp;
111661f28255Scgd 	int pid;
111761f28255Scgd 	int i;
1118c02b3bbdSchristos 	const char *err_msg = "No such job: %s";
111961f28255Scgd 
112061f28255Scgd 	if (name == NULL) {
112161f28255Scgd #if JOBS
1122c02b3bbdSchristos 		jobno = curjob;
112361f28255Scgd #endif
1124c02b3bbdSchristos 		err_msg = "No current job";
112561f28255Scgd 	} else if (name[0] == '%') {
1126c02b3bbdSchristos 		if (is_number(name + 1)) {
1127c02b3bbdSchristos 			jobno = number(name + 1) - 1;
11284e9fc30dSkre 		} else if (!name[1] || !name[2]) {
1129c02b3bbdSchristos 			switch (name[1]) {
113061f28255Scgd #if JOBS
1131c02b3bbdSchristos 			case 0:
1132c02b3bbdSchristos 			case '+':
1133c02b3bbdSchristos 			case '%':
1134c02b3bbdSchristos 				jobno = curjob;
1135c02b3bbdSchristos 				err_msg = "No current job";
1136c02b3bbdSchristos 				break;
1137c02b3bbdSchristos 			case '-':
1138c02b3bbdSchristos 				jobno = curjob;
1139c02b3bbdSchristos 				if (jobno != -1)
1140c02b3bbdSchristos 					jobno = jobtab[jobno].prev_job;
1141c02b3bbdSchristos 				err_msg = "No previous job";
1142c02b3bbdSchristos 				break;
114361f28255Scgd #endif
1144c02b3bbdSchristos 			default:
1145c02b3bbdSchristos 				goto check_pattern;
1146c02b3bbdSchristos 			}
114761f28255Scgd 		} else {
1148c02b3bbdSchristos 			struct job *found;
1149c02b3bbdSchristos     check_pattern:
1150c02b3bbdSchristos 			found = NULL;
115161f28255Scgd 			for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
1152c02b3bbdSchristos 				if (!jp->used || jp->nprocs <= 0)
1153c02b3bbdSchristos 					continue;
1154c02b3bbdSchristos 				if ((name[1] == '?'
1155c02b3bbdSchristos 					&& strstr(jp->ps[0].cmd, name + 2))
1156c02b3bbdSchristos 				    || prefix(name + 1, jp->ps[0].cmd)) {
1157c02b3bbdSchristos 					if (found) {
1158c02b3bbdSchristos 						err_msg = "%s: ambiguous";
1159c02b3bbdSchristos 						found = 0;
1160c02b3bbdSchristos 						break;
1161c02b3bbdSchristos 					}
116261f28255Scgd 					found = jp;
116361f28255Scgd 				}
116461f28255Scgd 			}
116561f28255Scgd 			if (found)
116661f28255Scgd 				return found;
116761f28255Scgd 		}
1168c02b3bbdSchristos 
116961f28255Scgd 	} else if (is_number(name)) {
117061f28255Scgd 		pid = number(name);
117161f28255Scgd 		for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
117261f28255Scgd 			if (jp->used && jp->nprocs > 0
117361f28255Scgd 			 && jp->ps[jp->nprocs - 1].pid == pid)
117461f28255Scgd 				return jp;
117561f28255Scgd 		}
117661f28255Scgd 	}
1177c02b3bbdSchristos 
11789572a5c2Skre 	if (jobno >= 0 && jobno < njobs) {
1179c02b3bbdSchristos 		jp = jobtab + jobno;
1180c02b3bbdSchristos 		if (jp->used)
1181c02b3bbdSchristos 			return jp;
1182c02b3bbdSchristos 	}
1183c02b3bbdSchristos 	if (!noerror)
1184c02b3bbdSchristos 		error(err_msg, name);
1185c02b3bbdSchristos 	return 0;
118661f28255Scgd }
118761f28255Scgd 
118861f28255Scgd 
11898821fc2cSkre /*
11908821fc2cSkre  * Find out if there are any running (that is, unwaited upon)
11918821fc2cSkre  * background children of the current shell.
11928821fc2cSkre  *
11938821fc2cSkre  * Return 1/0 (yes, no).
11948821fc2cSkre  *
11958821fc2cSkre  * Needed as we cannot optimise away sub-shell creation if
11968821fc2cSkre  * we have such a child, or a "wait" in that sub-shell would
11978821fc2cSkre  * observe the already existing job.
11988821fc2cSkre  */
11998821fc2cSkre int
12008821fc2cSkre anyjobs(void)
12018821fc2cSkre {
12028821fc2cSkre 	struct job *jp;
12038821fc2cSkre 	int i;
12048821fc2cSkre 
12058821fc2cSkre 	if (jobs_invalid)
12068821fc2cSkre 		return 0;
12078821fc2cSkre 
12088821fc2cSkre 	for (i = njobs, jp = jobtab ; --i >= 0 ; jp++) {
12098821fc2cSkre 		if (jp->used)
12108821fc2cSkre 			return 1;
12118821fc2cSkre 	}
12128821fc2cSkre 
12138821fc2cSkre 	return 0;
12148821fc2cSkre }
121561f28255Scgd 
121661f28255Scgd /*
1217aec4b6e2Skre  * Output the (new) POSIX required "[%d] %d" string whenever an
1218aec4b6e2Skre  * async (ie: background) job is started in an interactive shell.
1219aec4b6e2Skre  * Note that a subshell environment is not regarded as interactive.
1220aec4b6e2Skre  */
1221aec4b6e2Skre void
1222aec4b6e2Skre jobstarted(struct job *jp)
1223aec4b6e2Skre {
1224aec4b6e2Skre 	if (!iflag || !rootshell)
1225aec4b6e2Skre 		return;
1226aec4b6e2Skre 
1227aec4b6e2Skre 	outfmt(out2, "[%d] %ld\n", JNUM(jp),
1228aec4b6e2Skre 	    jp->pgrp != 0 ? (long)jp->pgrp : (long)jp->ps->pid);
1229aec4b6e2Skre }
1230aec4b6e2Skre 
1231aec4b6e2Skre /*
123261f28255Scgd  * Return a new job structure,
123361f28255Scgd  */
123461f28255Scgd 
123561f28255Scgd struct job *
1236c02b3bbdSchristos makejob(union node *node, int nprocs)
123761f28255Scgd {
123861f28255Scgd 	int i;
123961f28255Scgd 	struct job *jp;
124061f28255Scgd 
1241c02b3bbdSchristos 	if (jobs_invalid) {
12428821fc2cSkre 		VTRACE(DBG_JOBS, ("makejob(%p, %d) clearing jobtab (%d)\n",
12438821fc2cSkre 			(void *)node, nprocs, njobs));
1244c02b3bbdSchristos 		for (i = njobs, jp = jobtab ; --i >= 0 ; jp++) {
1245c02b3bbdSchristos 			if (jp->used)
1246c02b3bbdSchristos 				freejob(jp);
1247c02b3bbdSchristos 		}
1248c02b3bbdSchristos 		jobs_invalid = 0;
1249c02b3bbdSchristos 	}
1250c02b3bbdSchristos 
125161f28255Scgd 	for (i = njobs, jp = jobtab ; ; jp++) {
125261f28255Scgd 		if (--i < 0) {
125361f28255Scgd 			INTOFF;
125461f28255Scgd 			if (njobs == 0) {
125561f28255Scgd 				jobtab = ckmalloc(4 * sizeof jobtab[0]);
125661f28255Scgd 			} else {
125761f28255Scgd 				jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
125806f53b68Smycroft 				memcpy(jp, jobtab, njobs * sizeof jp[0]);
1259edae5eadSpk 				/* Relocate `ps' pointers */
1260edae5eadSpk 				for (i = 0; i < njobs; i++)
1261edae5eadSpk 					if (jp[i].ps == &jobtab[i].ps0)
1262edae5eadSpk 						jp[i].ps = &jp[i].ps0;
126361f28255Scgd 				ckfree(jobtab);
126461f28255Scgd 				jobtab = jp;
126561f28255Scgd 			}
126661f28255Scgd 			jp = jobtab + njobs;
1267d27db487Schristos 			for (i = 4 ; --i >= 0 ; njobs++) {
1268d27db487Schristos 				jobtab[njobs].used = 0;
1269d27db487Schristos 				jobtab[njobs].prev_job = -1;
1270d27db487Schristos 			}
127161f28255Scgd 			INTON;
127261f28255Scgd 			break;
127361f28255Scgd 		}
127461f28255Scgd 		if (jp->used == 0)
127561f28255Scgd 			break;
127661f28255Scgd 	}
127761f28255Scgd 	INTOFF;
1278c02b3bbdSchristos 	jp->state = JOBRUNNING;
127961f28255Scgd 	jp->used = 1;
1280f53fd6e9Skre 	jp->flags = pipefail ? JPIPEFAIL : 0;
128161f28255Scgd 	jp->nprocs = 0;
1282f697d47eSkre 	jp->pgrp = 0;
128361f28255Scgd #if JOBS
128461f28255Scgd 	jp->jobctl = jobctl;
1285c02b3bbdSchristos 	set_curjob(jp, 1);
128661f28255Scgd #endif
128761f28255Scgd 	if (nprocs > 1) {
128861f28255Scgd 		jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
128961f28255Scgd 	} else {
129061f28255Scgd 		jp->ps = &jp->ps0;
129161f28255Scgd 	}
129261f28255Scgd 	INTON;
1293f53fd6e9Skre 	VTRACE(DBG_JOBS, ("makejob(%p, %d)%s returns %%%d\n", (void *)node,
129421f00868Skre 	    nprocs, (jp->flags & JPIPEFAIL) ? " PF" : "", JNUM(jp)));
129561f28255Scgd 	return jp;
129661f28255Scgd }
129761f28255Scgd 
129861f28255Scgd 
129961f28255Scgd /*
1300c0b18597Slukem  * Fork off a subshell.  If we are doing job control, give the subshell its
130161f28255Scgd  * own process group.  Jp is a job structure that the job is to be added to.
130261f28255Scgd  * N is the command that will be evaluated by the child.  Both jp and n may
130361f28255Scgd  * be NULL.  The mode parameter can be one of the following:
130461f28255Scgd  *	FORK_FG - Fork off a foreground process.
130561f28255Scgd  *	FORK_BG - Fork off a background process.
130661f28255Scgd  *	FORK_NOJOB - Like FORK_FG, but don't give the process its own
130761f28255Scgd  *		     process group even if job control is on.
130861f28255Scgd  *
130961f28255Scgd  * When job control is turned off, background processes have their standard
131061f28255Scgd  * input redirected to /dev/null (except for the second and later processes
131161f28255Scgd  * in a pipeline).
131261f28255Scgd  */
131361f28255Scgd 
131461f28255Scgd int
1315c02b3bbdSchristos forkshell(struct job *jp, union node *n, int mode)
131661f28255Scgd {
13171468e9a3Schristos 	pid_t pid;
13181468e9a3Schristos 	int serrno;
131961f28255Scgd 
132016bbc7c6Skre 	CTRACE(DBG_JOBS, ("forkshell(%%%d, %p, %d) called\n",
132121f00868Skre 	    JNUM(jp), n, mode));
132216bbc7c6Skre 
1323edcb4544Schristos 	switch ((pid = fork())) {
1324edcb4544Schristos 	case -1:
13251468e9a3Schristos 		serrno = errno;
132616bbc7c6Skre 		VTRACE(DBG_JOBS, ("Fork failed, errno=%d\n", serrno));
13271468e9a3Schristos 		error("Cannot fork (%s)", strerror(serrno));
1328edcb4544Schristos 		break;
1329edcb4544Schristos 	case 0:
1330352391ffSkre 		SHELL_FORKED();
13314783843fSchristos 		forkchild(jp, n, mode, 0);
1332edcb4544Schristos 		return 0;
1333edcb4544Schristos 	default:
13344783843fSchristos 		return forkparent(jp, n, mode, pid);
1335e6bccfe4Selric 	}
1336edcb4544Schristos }
1337e6bccfe4Selric 
1338edcb4544Schristos int
1339c02b3bbdSchristos forkparent(struct job *jp, union node *n, int mode, pid_t pid)
1340edcb4544Schristos {
13411c627bdfSkre 	int pgrp = 0;
1342edcb4544Schristos 
13436092f7d5Smycroft 	if (rootshell && mode != FORK_NOJOB && mflag) {
13441c627bdfSkre 		/*
13451c627bdfSkre 		 * The process group ID must always be that of the
13461c627bdfSkre 		 * first process created for the job.   If this proc
13471c627bdfSkre 		 * is the first, that's us, otherwise the pgrp has
13481c627bdfSkre 		 * already been determined.
13491c627bdfSkre 		 */
13506092f7d5Smycroft 		if (jp == NULL || jp->nprocs == 0)
13516092f7d5Smycroft 			pgrp = pid;
13526092f7d5Smycroft 		else
13531c627bdfSkre 			pgrp = jp->pgrp;
13546092f7d5Smycroft 		/* This can fail because we are doing it in the child also */
13556092f7d5Smycroft 		(void)setpgid(pid, pgrp);
13566092f7d5Smycroft 	}
135761f28255Scgd 	if (mode == FORK_BG)
135861f28255Scgd 		backgndpid = pid;		/* set $! */
135961f28255Scgd 	if (jp) {
136061f28255Scgd 		struct procstat *ps = &jp->ps[jp->nprocs++];
136161f28255Scgd 		ps->pid = pid;
136261f28255Scgd 		ps->status = -1;
1363c02b3bbdSchristos 		ps->cmd[0] = 0;
13641c627bdfSkre 		jp->pgrp = pgrp;	/* 0 if !mflag */
1365c02b3bbdSchristos 		if (/* iflag && rootshell && */ n)
1366c02b3bbdSchristos 			commandtext(ps, n);
136761f28255Scgd 	}
1368f07d3f9bSkre 	CTRACE(DBG_JOBS, ("In parent shell: child = %d (mode %d)\n",pid,mode));
136961f28255Scgd 	return pid;
137061f28255Scgd }
137161f28255Scgd 
1372edcb4544Schristos void
1373c02b3bbdSchristos forkchild(struct job *jp, union node *n, int mode, int vforked)
1374edcb4544Schristos {
1375edcb4544Schristos 	int wasroot;
1376edcb4544Schristos 	int pgrp;
1377edcb4544Schristos 	const char *devnull = _PATH_DEVNULL;
1378edcb4544Schristos 	const char *nullerr = "Can't open %s";
1379edcb4544Schristos 
13804783843fSchristos 	wasroot = rootshell;
13819572a5c2Skre 	CTRACE(DBG_JOBS, ("Child shell %d %sforked from %d (mode %d)\n",
13829572a5c2Skre 	    getpid(), vforked?"v":"", getppid(), mode));
13837f63ac72Skre 
13847f63ac72Skre 	if (!vforked) {
13854783843fSchristos 		rootshell = 0;
13867f63ac72Skre 		handler = &main_handler;
13877f63ac72Skre 	}
1388c02b3bbdSchristos 
1389edcb4544Schristos 	closescript(vforked);
1390edcb4544Schristos 	clear_traps(vforked);
1391edcb4544Schristos #if JOBS
1392edcb4544Schristos 	if (!vforked)
1393edcb4544Schristos 		jobctl = 0;		/* do job control only in root shell */
1394edcb4544Schristos 	if (wasroot && mode != FORK_NOJOB && mflag) {
1395edcb4544Schristos 		if (jp == NULL || jp->nprocs == 0)
1396edcb4544Schristos 			pgrp = getpid();
1397edcb4544Schristos 		else
1398edcb4544Schristos 			pgrp = jp->ps[0].pid;
13996092f7d5Smycroft 		/* This can fail because we are doing it in the parent also */
14006092f7d5Smycroft 		(void)setpgid(0, pgrp);
1401edcb4544Schristos 		if (mode == FORK_FG) {
1402edcb4544Schristos 			if (tcsetpgrp(ttyfd, pgrp) == -1)
1403edcb4544Schristos 				error("Cannot set tty process group (%s) at %d",
1404edcb4544Schristos 				    strerror(errno), __LINE__);
1405edcb4544Schristos 		}
1406edcb4544Schristos 		setsignal(SIGTSTP, vforked);
1407edcb4544Schristos 		setsignal(SIGTTOU, vforked);
1408edcb4544Schristos 	} else if (mode == FORK_BG) {
1409edcb4544Schristos 		ignoresig(SIGINT, vforked);
1410edcb4544Schristos 		ignoresig(SIGQUIT, vforked);
1411edcb4544Schristos 		if ((jp == NULL || jp->nprocs == 0) &&
1412edcb4544Schristos 		    ! fd0_redirected_p ()) {
1413edcb4544Schristos 			close(0);
1414edcb4544Schristos 			if (open(devnull, O_RDONLY) != 0)
1415edcb4544Schristos 				error(nullerr, devnull);
1416edcb4544Schristos 		}
1417edcb4544Schristos 	}
1418edcb4544Schristos #else
1419edcb4544Schristos 	if (mode == FORK_BG) {
1420edcb4544Schristos 		ignoresig(SIGINT, vforked);
1421edcb4544Schristos 		ignoresig(SIGQUIT, vforked);
1422edcb4544Schristos 		if ((jp == NULL || jp->nprocs == 0) &&
1423edcb4544Schristos 		    ! fd0_redirected_p ()) {
1424edcb4544Schristos 			close(0);
1425edcb4544Schristos 			if (open(devnull, O_RDONLY) != 0)
1426edcb4544Schristos 				error(nullerr, devnull);
1427edcb4544Schristos 		}
1428edcb4544Schristos 	}
1429edcb4544Schristos #endif
1430edcb4544Schristos 	if (wasroot && iflag) {
1431edcb4544Schristos 		setsignal(SIGINT, vforked);
1432edcb4544Schristos 		setsignal(SIGQUIT, vforked);
1433edcb4544Schristos 		setsignal(SIGTERM, vforked);
1434edcb4544Schristos 	}
1435c02b3bbdSchristos 
1436c02b3bbdSchristos 	if (!vforked)
1437c02b3bbdSchristos 		jobs_invalid = 1;
1438edcb4544Schristos }
1439edcb4544Schristos 
144061f28255Scgd /*
144161f28255Scgd  * Wait for job to finish.
144261f28255Scgd  *
144361f28255Scgd  * Under job control we have the problem that while a child process is
144461f28255Scgd  * running interrupts generated by the user are sent to the child but not
144561f28255Scgd  * to the shell.  This means that an infinite loop started by an inter-
144661f28255Scgd  * active user may be hard to kill.  With job control turned off, an
144761f28255Scgd  * interactive user may place an interactive program inside a loop.  If
144861f28255Scgd  * the interactive program catches interrupts, the user doesn't want
144961f28255Scgd  * these interrupts to also abort the loop.  The approach we take here
145061f28255Scgd  * is to have the shell ignore interrupt signals while waiting for a
145192e3dd64Sandvar  * foreground process to terminate, and then send itself an interrupt
145261f28255Scgd  * signal if the child process was terminated by an interrupt signal.
145361f28255Scgd  * Unfortunately, some programs want to do a bit of cleanup and then
145461f28255Scgd  * exit on interrupt; unless these processes terminate themselves by
145561f28255Scgd  * sending a signal to themselves (instead of calling exit) they will
145661f28255Scgd  * confuse this approach.
145761f28255Scgd  */
145861f28255Scgd 
145961f28255Scgd int
1460c02b3bbdSchristos waitforjob(struct job *jp)
146161f28255Scgd {
146261f28255Scgd #if JOBS
1463df81ab82Sjtc 	int mypgrp = getpgrp();
146461f28255Scgd #endif
146561f28255Scgd 	int status;
146661f28255Scgd 	int st;
146761f28255Scgd 
146861f28255Scgd 	INTOFF;
146921f00868Skre 	VTRACE(DBG_JOBS, ("waitforjob(%%%d) called\n", JNUM(jp)));
1470c02b3bbdSchristos 	while (jp->state == JOBRUNNING) {
14714e9fc30dSkre 		dowait(WBLOCK, jp, NULL);
147261f28255Scgd 	}
147361f28255Scgd #if JOBS
147461f28255Scgd 	if (jp->jobctl) {
1475edcb4544Schristos 		if (tcsetpgrp(ttyfd, mypgrp) == -1)
1476edcb4544Schristos 			error("Cannot set tty process group (%s) at %d",
1477edcb4544Schristos 			    strerror(errno), __LINE__);
147861f28255Scgd 	}
1479c02b3bbdSchristos 	if (jp->state == JOBSTOPPED && curjob != jp - jobtab)
1480c02b3bbdSchristos 		set_curjob(jp, 2);
148161f28255Scgd #endif
1482bffe5190Skre 	status = jobstatus(jp, 1);
1483bffe5190Skre 
148461f28255Scgd 	/* convert to 8 bits */
14857f2a2717Schristos 	if (WIFEXITED(status))
14867f2a2717Schristos 		st = WEXITSTATUS(status);
148761f28255Scgd #if JOBS
14887f2a2717Schristos 	else if (WIFSTOPPED(status))
14897f2a2717Schristos 		st = WSTOPSIG(status) + 128;
149061f28255Scgd #endif
149161f28255Scgd 	else
14927f2a2717Schristos 		st = WTERMSIG(status) + 128;
149316bbc7c6Skre 
149416bbc7c6Skre 	VTRACE(DBG_JOBS, ("waitforjob: job %d, nproc %d, status %d, st %x\n",
149521f00868Skre 		JNUM(jp), jp->nprocs, status, st));
14962806df57Smycroft #if JOBS
14972806df57Smycroft 	if (jp->jobctl) {
1498c06643b8Smycroft 		/*
14992806df57Smycroft 		 * This is truly gross.
15002806df57Smycroft 		 * If we're doing job control, then we did a TIOCSPGRP which
15012806df57Smycroft 		 * caused us (the shell) to no longer be in the controlling
15022806df57Smycroft 		 * session -- so we wouldn't have seen any ^C/SIGINT.  So, we
15032806df57Smycroft 		 * intuit from the subprocess exit status whether a SIGINT
1504456dff6cSwiz 		 * occurred, and if so interrupt ourselves.  Yuck.  - mycroft
1505c06643b8Smycroft 		 */
15067f2a2717Schristos 		if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
1507c06643b8Smycroft 			raise(SIGINT);
15082806df57Smycroft 	}
1509c06643b8Smycroft #endif
15102806df57Smycroft 	if (! JOBS || jp->state == JOBDONE)
15112806df57Smycroft 		freejob(jp);
151261f28255Scgd 	INTON;
151361f28255Scgd 	return st;
151461f28255Scgd }
151561f28255Scgd 
151661f28255Scgd 
151761f28255Scgd 
151861f28255Scgd /*
15197a2f8a05Skre  * Wait for a process (any process) to terminate.
15207a2f8a05Skre  *
15217a2f8a05Skre  * If "job" is given (not NULL), then its jobcontrol status (and mflag)
15227a2f8a05Skre  * are used to determine if we wait for stopping/continuing processes or
15237a2f8a05Skre  * only terminating ones, and the decision whether to report to stdout
15247a2f8a05Skre  * or not varies depending what happened, and whether the affected job
15257a2f8a05Skre  * is the one that was requested or not.
15267a2f8a05Skre  *
15277a2f8a05Skre  * If "changed" is not NULL, then the job which changed because a
15287a2f8a05Skre  * process terminated/stopped will be reported by setting *changed,
15297a2f8a05Skre  * if there is any such job, otherwise we set *changed = NULL.
153061f28255Scgd  */
153161f28255Scgd 
153261f28255Scgd STATIC int
15334e9fc30dSkre dowait(int flags, struct job *job, struct job **changed)
153461f28255Scgd {
153561f28255Scgd 	int pid;
153661f28255Scgd 	int status;
153761f28255Scgd 	struct procstat *sp;
153861f28255Scgd 	struct job *jp;
153961f28255Scgd 	struct job *thisjob;
154061f28255Scgd 	int done;
154161f28255Scgd 	int stopped;
15427a2f8a05Skre 	int err;
154361f28255Scgd 
15447a2f8a05Skre 	VTRACE(DBG_JOBS|DBG_PROCS, ("dowait(%x) called for job %d%s\n",
154521f00868Skre 	    flags, JNUM(job), changed ? " [report change]" : ""));
15464e9fc30dSkre 
15474e9fc30dSkre 	if (changed != NULL)
15484e9fc30dSkre 		*changed = NULL;
15494e9fc30dSkre 
15507a2f8a05Skre 	/*
15517a2f8a05Skre 	 * First deal with the kernel, collect info on any (one) of our
15527a2f8a05Skre 	 * children that has changed state since we last asked.
15537a2f8a05Skre 	 * (loop if we're interrupted by a signal that we aren't processing)
15547a2f8a05Skre 	 */
155561f28255Scgd 	do {
15567a2f8a05Skre 		err = 0;
155797f12feaSchristos 		pid = waitproc(flags & WBLOCK, job, &status);
15587a2f8a05Skre 		if (pid == -1)
15597a2f8a05Skre 			err = errno;
15607a2f8a05Skre 		VTRACE(DBG_JOBS|DBG_PROCS,
15617a2f8a05Skre 		    ("wait returns pid %d (e:%d), status %#x (ps=%d)\n",
15627a2f8a05Skre 		    pid, err, status, pendingsigs));
15637a2f8a05Skre 	} while (pid == -1 && err == EINTR && pendingsigs == 0);
15647a2f8a05Skre 
15657a2f8a05Skre 	/*
15667a2f8a05Skre 	 * if nothing exited/stopped/..., we have nothing else to do
15677a2f8a05Skre 	 */
156861f28255Scgd 	if (pid <= 0)
156961f28255Scgd 		return pid;
15707a2f8a05Skre 
15717a2f8a05Skre 	/*
15727a2f8a05Skre 	 * Otherwise, try to find the process, somewhere in our job table
15737a2f8a05Skre 	 */
157461f28255Scgd 	INTOFF;
157561f28255Scgd 	thisjob = NULL;
157661f28255Scgd 	for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
157761f28255Scgd 		if (jp->used) {
15787a2f8a05Skre 			/*
15797a2f8a05Skre 			 * For each job that is in use (this is one)
15807a2f8a05Skre 			 */
15817a2f8a05Skre 			done = 1;	/* assume it is finished */
15827a2f8a05Skre 			stopped = 1;	/* and has stopped */
15837a2f8a05Skre 
15847a2f8a05Skre 			/*
15857a2f8a05Skre 			 * Now scan all our child processes of the job
15867a2f8a05Skre 			 */
158761f28255Scgd 			for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
158861f28255Scgd 				if (sp->pid == -1)
158961f28255Scgd 					continue;
15907a2f8a05Skre 				/*
15917a2f8a05Skre 				 * If the process that changed is the one
15927a2f8a05Skre 				 * we're looking at, and it was previously
15937a2f8a05Skre 				 * running (-1) or was stopped (anything else
15947a2f8a05Skre 				 * and it must have already finished earlier,
15957a2f8a05Skre 				 * so cannot be the process that just changed)
15967a2f8a05Skre 				 * then we update its status
15977a2f8a05Skre 				 */
15989572a5c2Skre 				if (sp->pid == pid &&
15999572a5c2Skre 				  (sp->status==-1 || WIFSTOPPED(sp->status))) {
160016bbc7c6Skre 					VTRACE(DBG_JOBS | DBG_PROCS,
160121f00868Skre 			("Job %d: changing status of proc %d from %#x to ",
160221f00868Skre 					    JNUM(jp), pid, sp->status));
16037a2f8a05Skre 
16047a2f8a05Skre 					/*
16057a2f8a05Skre 					 * If the process continued,
16067a2f8a05Skre 					 * then update its status to running
16077a2f8a05Skre 					 * and mark the job running as well.
16087a2f8a05Skre 					 *
16097a2f8a05Skre 					 * If it was anything but running
16107a2f8a05Skre 					 * before, flag it as a change for
16117a2f8a05Skre 					 * reporting purposes later
16127a2f8a05Skre 					 */
16139572a5c2Skre 					if (WIFCONTINUED(status)) {
16149572a5c2Skre 						if (sp->status != -1)
16154e9fc30dSkre 							jp->flags |= JOBCHANGED;
16169572a5c2Skre 						sp->status = -1;
161721f00868Skre 						jp->state = JOBRUNNING;
161821f00868Skre 						VTRACE(DBG_JOBS|DBG_PROCS,
161921f00868Skre 						    ("running\n"));
16207a2f8a05Skre 					} else {
16217a2f8a05Skre 						/* otherwise update status */
162261f28255Scgd 						sp->status = status;
162321f00868Skre 						VTRACE(DBG_JOBS|DBG_PROCS,
162421f00868Skre 						    ("%#x\n", status));
16257a2f8a05Skre 					}
16267a2f8a05Skre 
16277a2f8a05Skre 					/*
16287a2f8a05Skre 					 * We now know the affected job
16297a2f8a05Skre 					 */
163061f28255Scgd 					thisjob = jp;
16314e9fc30dSkre 					if (changed != NULL)
16324e9fc30dSkre 						*changed = jp;
163361f28255Scgd 				}
16347a2f8a05Skre 				/*
16357a2f8a05Skre 				 * After any update that might have just
16367a2f8a05Skre 				 * happened, if this process is running,
16377a2f8a05Skre 				 * the job is not stopped, or if the process
16387a2f8a05Skre 				 * simply stopped (not terminated) then the
16397a2f8a05Skre 				 * job is certainly not completed (done).
16407a2f8a05Skre 				 */
164161f28255Scgd 				if (sp->status == -1)
164261f28255Scgd 					stopped = 0;
16437f2a2717Schristos 				else if (WIFSTOPPED(sp->status))
164461f28255Scgd 					done = 0;
164561f28255Scgd 			}
16467a2f8a05Skre 
16477a2f8a05Skre 			/*
16487a2f8a05Skre 			 * Once we have examined all processes for the
16497a2f8a05Skre 			 * job, if we still show it as stopped, then...
16507a2f8a05Skre 			 */
165161f28255Scgd 			if (stopped) {		/* stopped or done */
16527a2f8a05Skre 				/*
16537a2f8a05Skre 				 * it might be stopped, or finished, decide:
16547a2f8a05Skre 				 */
165561f28255Scgd 				int state = done ? JOBDONE : JOBSTOPPED;
16569572a5c2Skre 
16577a2f8a05Skre 				/*
16587a2f8a05Skre 				 * If that wasn't the same as it was before
16597a2f8a05Skre 				 * then update its state, and if it just
16607a2f8a05Skre 				 * completed, make it be the current job (%%)
16617a2f8a05Skre 				 */
166261f28255Scgd 				if (jp->state != state) {
166316bbc7c6Skre 					VTRACE(DBG_JOBS,
166416bbc7c6Skre 				("Job %d: changing state from %d to %d\n",
166521f00868Skre 					    JNUM(jp), jp->state, state));
166661f28255Scgd 					jp->state = state;
166761f28255Scgd #if JOBS
1668c02b3bbdSchristos 					if (done)
1669c02b3bbdSchristos 						set_curjob(jp, 0);
167061f28255Scgd #endif
167161f28255Scgd 				}
167261f28255Scgd 			}
167361f28255Scgd 		}
167461f28255Scgd 	}
16757f2a2717Schristos 
16767a2f8a05Skre 	/*
16777a2f8a05Skre 	 * Now we have scanned all jobs.   If we found the job that
16787a2f8a05Skre 	 * the process that changed state belonged to (we occasionally
16797a2f8a05Skre 	 * fork processes without associating them with a job, when one
16807a2f8a05Skre 	 * of those finishes, we simply ignore it, the zombie has been
16817a2f8a05Skre 	 * cleaned up, which is all that matters) then we need to
16827a2f8a05Skre 	 * determine if we should say something about it to stdout
16837a2f8a05Skre 	 */
16847a2f8a05Skre 
16854e9fc30dSkre 	if (thisjob &&
16864e9fc30dSkre 	    (thisjob->state != JOBRUNNING || thisjob->flags & JOBCHANGED)) {
1687c02b3bbdSchristos 		int mode = 0;
16889572a5c2Skre 
1689c02b3bbdSchristos 		if (!rootshell || !iflag)
1690c02b3bbdSchristos 			mode = SHOW_SIGNALLED;
169197f12feaSchristos 		if ((job == thisjob && (flags & WNOFREE) == 0) ||
16929572a5c2Skre 		    job != thisjob)
1693c02b3bbdSchristos 			mode = SHOW_SIGNALLED | SHOW_NO_FREE;
16949572a5c2Skre 		if (mode && (flags & WSILENT) == 0)
1695c02b3bbdSchristos 			showjob(out2, thisjob, mode);
1696c02b3bbdSchristos 		else {
169716bbc7c6Skre 			VTRACE(DBG_JOBS,
16987a2f8a05Skre 			    ("Not printing status for %p [%d], "
16997a2f8a05Skre 			     "mode=%#x rootshell=%d, job=%p [%d]\n",
170021f00868Skre 			    thisjob, JNUM(thisjob), mode, rootshell,
170121f00868Skre 			    job, JNUM(job)));
17024e9fc30dSkre 			thisjob->flags |= JOBCHANGED;
170361f28255Scgd 		}
1704c02b3bbdSchristos 	}
1705c02b3bbdSchristos 
1706edcb4544Schristos 	INTON;
17077a2f8a05Skre 	/*
17087a2f8a05Skre 	 * Finally tell our caller that something happened (in general all
17097a2f8a05Skre 	 * anyone tests for is <= 0 (or >0) so the actual pid value here
171021f00868Skre 	 * doesn't matter much, but we know pid is >0 so we may as well
17117a2f8a05Skre 	 * give back something meaningful
17127a2f8a05Skre 	 */
171361f28255Scgd 	return pid;
171461f28255Scgd }
171561f28255Scgd 
171661f28255Scgd 
171761f28255Scgd 
171861f28255Scgd /*
171961f28255Scgd  * Do a wait system call.  If job control is compiled in, we accept
172061f28255Scgd  * stopped processes.  If block is zero, we return a value of zero
172161f28255Scgd  * rather than blocking.
172261f28255Scgd  *
172361f28255Scgd  * System V doesn't have a non-blocking wait system call.  It does
1724a640fe8cSsnj  * have a SIGCLD signal that is sent to a process when one of its
172561f28255Scgd  * children dies.  The obvious way to use SIGCLD would be to install
172661f28255Scgd  * a handler for SIGCLD which simply bumped a counter when a SIGCLD
172761f28255Scgd  * was received, and have waitproc bump another counter when it got
172861f28255Scgd  * the status of a process.  Waitproc would then know that a wait
172961f28255Scgd  * system call would not block if the two counters were different.
173061f28255Scgd  * This approach doesn't work because if a process has children that
173161f28255Scgd  * have not been waited for, System V will send it a SIGCLD when it
173261f28255Scgd  * installs a signal handler for SIGCLD.  What this means is that when
173361f28255Scgd  * a child exits, the shell will be sent SIGCLD signals continuously
173461f28255Scgd  * until is runs out of stack space, unless it does a wait call before
173561f28255Scgd  * restoring the signal handler.  The code below takes advantage of
173661f28255Scgd  * this (mis)feature by installing a signal handler for SIGCLD and
173761f28255Scgd  * then checking to see whether it was called.  If there are any
173861f28255Scgd  * children to be waited for, it will be.
173961f28255Scgd  *
174061f28255Scgd  * If neither SYSV nor BSD is defined, we don't implement nonblocking
174161f28255Scgd  * waits at all.  In this case, the user will not be informed when
174221f00868Skre  * a background process ends until the next time she runs a real program
174361f28255Scgd  * (as opposed to running a builtin command or just typing return),
174461f28255Scgd  * and the jobs command may give out of date information.
174561f28255Scgd  */
174661f28255Scgd 
174761f28255Scgd #ifdef SYSV
174861f28255Scgd STATIC int gotsigchild;
174961f28255Scgd 
175061f28255Scgd STATIC int onsigchild() {
175161f28255Scgd 	gotsigchild = 1;
175261f28255Scgd }
175361f28255Scgd #endif
175461f28255Scgd 
175561f28255Scgd 
175661f28255Scgd STATIC int
1757c02b3bbdSchristos waitproc(int block, struct job *jp, int *status)
175861f28255Scgd {
175961f28255Scgd #ifdef BSD
1760a7560f06Schristos 	int flags = 0;
176161f28255Scgd 
176261f28255Scgd #if JOBS
17639572a5c2Skre 	if (mflag || (jp != NULL && jp->jobctl))
17649572a5c2Skre 		flags |= WUNTRACED | WCONTINUED;
176561f28255Scgd #endif
176661f28255Scgd 	if (block == 0)
176761f28255Scgd 		flags |= WNOHANG;
17689572a5c2Skre 	VTRACE(DBG_WAIT, ("waitproc: doing waitpid(flags=%#x)\n", flags));
1769964f98e7Stv 	return waitpid(-1, status, flags);
177061f28255Scgd #else
177161f28255Scgd #ifdef SYSV
177261f28255Scgd 	int (*save)();
177361f28255Scgd 
177461f28255Scgd 	if (block == 0) {
177561f28255Scgd 		gotsigchild = 0;
177661f28255Scgd 		save = signal(SIGCLD, onsigchild);
177761f28255Scgd 		signal(SIGCLD, save);
177861f28255Scgd 		if (gotsigchild == 0)
177961f28255Scgd 			return 0;
178061f28255Scgd 	}
178161f28255Scgd 	return wait(status);
178261f28255Scgd #else
178361f28255Scgd 	if (block == 0)
178461f28255Scgd 		return 0;
178561f28255Scgd 	return wait(status);
178661f28255Scgd #endif
178761f28255Scgd #endif
178861f28255Scgd }
178961f28255Scgd 
179037ed7877Sjtc /*
179137ed7877Sjtc  * return 1 if there are stopped jobs, otherwise 0
179237ed7877Sjtc  */
179337ed7877Sjtc int job_warning = 0;
179437ed7877Sjtc int
1795c02b3bbdSchristos stoppedjobs(void)
179637ed7877Sjtc {
179748250187Stls 	int jobno;
179848250187Stls 	struct job *jp;
179961f28255Scgd 
1800c02b3bbdSchristos 	if (job_warning || jobs_invalid)
180137ed7877Sjtc 		return (0);
180237ed7877Sjtc 	for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) {
180337ed7877Sjtc 		if (jp->used == 0)
180437ed7877Sjtc 			continue;
180537ed7877Sjtc 		if (jp->state == JOBSTOPPED) {
180637ed7877Sjtc 			out2str("You have stopped jobs.\n");
180737ed7877Sjtc 			job_warning = 2;
180837ed7877Sjtc 			return (1);
180937ed7877Sjtc 		}
181037ed7877Sjtc 	}
181137ed7877Sjtc 
181237ed7877Sjtc 	return (0);
181337ed7877Sjtc }
181461f28255Scgd 
181561f28255Scgd /*
181661f28255Scgd  * Return a string identifying a command (to be printed by the
1817c02b3bbdSchristos  * jobs command).
181861f28255Scgd  */
181961f28255Scgd 
182061f28255Scgd STATIC char *cmdnextc;
182161f28255Scgd STATIC int cmdnleft;
182261f28255Scgd 
1823c02b3bbdSchristos void
1824c02b3bbdSchristos commandtext(struct procstat *ps, union node *n)
182561f28255Scgd {
1826c02b3bbdSchristos 	int len;
182761f28255Scgd 
1828c02b3bbdSchristos 	cmdnextc = ps->cmd;
1829ee301070Skre 	if (iflag || mflag || sizeof(ps->cmd) <= 60)
1830c02b3bbdSchristos 		len = sizeof(ps->cmd);
1831ee301070Skre 	else if (sizeof ps->cmd <= 400)
1832ee301070Skre 		len = 50;
1833ee301070Skre 	else if (sizeof ps->cmd <= 800)
1834ee301070Skre 		len = 80;
1835c02b3bbdSchristos 	else
1836c02b3bbdSchristos 		len = sizeof(ps->cmd) / 10;
1837c02b3bbdSchristos 	cmdnleft = len;
1838601249a2Skre 	(void)cmdtxt(n, 1);
1839c02b3bbdSchristos 	if (cmdnleft <= 0) {
1840c02b3bbdSchristos 		char *p = ps->cmd + len - 4;
1841c02b3bbdSchristos 		p[0] = '.';
1842c02b3bbdSchristos 		p[1] = '.';
1843c02b3bbdSchristos 		p[2] = '.';
1844c02b3bbdSchristos 		p[3] = 0;
1845c02b3bbdSchristos 	} else
184661f28255Scgd 		*cmdnextc = '\0';
184716bbc7c6Skre 
184816bbc7c6Skre 	VTRACE(DBG_JOBS,
1849ee301070Skre 	    ("commandtext: ps->cmd %p, end %p, left %d\n\t\"%s\"\n",
1850c02b3bbdSchristos 	    ps->cmd, cmdnextc, cmdnleft, ps->cmd));
185161f28255Scgd }
185261f28255Scgd 
185361f28255Scgd 
1854601249a2Skre /*
1855601249a2Skre  * Generate a string describing tree node n & its descendants (recursive calls)
1856601249a2Skre  *
1857601249a2Skre  * Return true (non-zero) if the output is complete (ends with an operator)
1858601249a2Skre  * so no ';' need be added before the following command.  Return false (zero)
1859601249a2Skre  * if a ';' is needed to terminate the output if it is followed by something
1860601249a2Skre  * which is not an operator.
1861601249a2Skre  */
1862601249a2Skre STATIC int
1863601249a2Skre cmdtxt(union node *n, int top)
186461f28255Scgd {
186561f28255Scgd 	union node *np;
186661f28255Scgd 	struct nodelist *lp;
18673d424690Schristos 	const char *p;
186861f28255Scgd 	int i;
186961f28255Scgd 
1870c02b3bbdSchristos 	if (n == NULL || cmdnleft <= 0)
1871601249a2Skre 		return 1;
187261f28255Scgd 	switch (n->type) {
187361f28255Scgd 	case NSEMI:
1874601249a2Skre 		if (!cmdtxt(n->nbinary.ch1, 0))
187561f28255Scgd 			cmdputs(";");
1876601249a2Skre 		cmdputs(" ");
1877601249a2Skre 		return cmdtxt(n->nbinary.ch2, 0);
187861f28255Scgd 	case NAND:
1879601249a2Skre 		(void)cmdtxt(n->nbinary.ch1, 0);
188061f28255Scgd 		cmdputs(" && ");
1881601249a2Skre 		return cmdtxt(n->nbinary.ch2, 0);
188261f28255Scgd 	case NOR:
1883601249a2Skre 		(void) cmdtxt(n->nbinary.ch1, 0);
188461f28255Scgd 		cmdputs(" || ");
1885601249a2Skre 		return cmdtxt(n->nbinary.ch2, 0);
1886aa563ca4Skre 	case NDNOT:
1887aa563ca4Skre 		cmdputs("! ");
1888aa563ca4Skre 		/* FALLTHROUGH */
18898eee1dabSkre 	case NNOT:
18908eee1dabSkre 		cmdputs("! ");
1891601249a2Skre 		return cmdtxt(n->nnot.com, 0);
18928eee1dabSkre 		break;
189361f28255Scgd 	case NPIPE:
189461f28255Scgd 		for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
1895601249a2Skre 			(void) cmdtxt(lp->n, 0);
189661f28255Scgd 			if (lp->next)
189761f28255Scgd 				cmdputs(" | ");
189861f28255Scgd 		}
1899601249a2Skre 		if (!top && n->npipe.backgnd) {
190009ecfab9Skre 			cmdputs(" &");
1901601249a2Skre 			return 1;
1902601249a2Skre 		}
1903601249a2Skre 		return 0;
190461f28255Scgd 	case NSUBSHELL:
190561f28255Scgd 		cmdputs("(");
1906601249a2Skre 		(void) cmdtxt(n->nredir.n, 0);
190761f28255Scgd 		cmdputs(")");
1908601249a2Skre 		return 0;
190961f28255Scgd 	case NREDIR:
191061f28255Scgd 	case NBACKGND:
1911601249a2Skre 		return cmdtxt(n->nredir.n, top);
191261f28255Scgd 	case NIF:
191361f28255Scgd 		cmdputs("if ");
1914601249a2Skre 		if (!cmdtxt(n->nif.test, 0))
1915601249a2Skre 			cmdputs(";");
1916601249a2Skre 		cmdputs(" then ");
1917601249a2Skre 		i = cmdtxt(n->nif.ifpart, 0);
1918c02b3bbdSchristos 		if (n->nif.elsepart) {
1919601249a2Skre 			if (i == 0)
1920601249a2Skre 				cmdputs(";");
1921601249a2Skre 			cmdputs(" else ");
1922601249a2Skre 			i = cmdtxt(n->nif.elsepart, 0);
1923c02b3bbdSchristos 		}
1924601249a2Skre 		if (i == 0)
1925601249a2Skre 			cmdputs(";");
1926601249a2Skre 		cmdputs(" fi");
1927601249a2Skre 		return 0;
192861f28255Scgd 	case NWHILE:
192961f28255Scgd 		cmdputs("while ");
193061f28255Scgd 		goto until;
193161f28255Scgd 	case NUNTIL:
193261f28255Scgd 		cmdputs("until ");
193361f28255Scgd  until:
1934601249a2Skre 		if (!cmdtxt(n->nbinary.ch1, 0))
1935601249a2Skre 			cmdputs(";");
1936601249a2Skre 		cmdputs(" do ");
1937601249a2Skre 		if (!cmdtxt(n->nbinary.ch2, 0))
1938601249a2Skre 			cmdputs(";");
1939601249a2Skre 		cmdputs(" done");
1940601249a2Skre 		return 0;
194161f28255Scgd 	case NFOR:
194261f28255Scgd 		cmdputs("for ");
194361f28255Scgd 		cmdputs(n->nfor.var);
1944c02b3bbdSchristos 		cmdputs(" in ");
1945c02b3bbdSchristos 		cmdlist(n->nfor.args, 1);
1946c02b3bbdSchristos 		cmdputs("; do ");
1947601249a2Skre 		if (!cmdtxt(n->nfor.body, 0))
1948601249a2Skre 			cmdputs(";");
1949601249a2Skre 		cmdputs(" done");
1950601249a2Skre 		return 0;
195161f28255Scgd 	case NCASE:
195261f28255Scgd 		cmdputs("case ");
195361f28255Scgd 		cmdputs(n->ncase.expr->narg.text);
1954c02b3bbdSchristos 		cmdputs(" in ");
1955c02b3bbdSchristos 		for (np = n->ncase.cases; np; np = np->nclist.next) {
1956601249a2Skre 			(void) cmdtxt(np->nclist.pattern, 0);
1957c02b3bbdSchristos 			cmdputs(") ");
1958601249a2Skre 			(void) cmdtxt(np->nclist.body, 0);
19597d41ae4eSkre 			switch (n->type) {	/* switch (not if) for later */
19607d41ae4eSkre 			case NCLISTCONT:
19617d41ae4eSkre 				cmdputs(" ;& ");
19627d41ae4eSkre 				break;
19637d41ae4eSkre 			default:
1964c02b3bbdSchristos 				cmdputs(" ;; ");
19657d41ae4eSkre 				break;
19667d41ae4eSkre 			}
1967c02b3bbdSchristos 		}
1968c02b3bbdSchristos 		cmdputs("esac");
1969601249a2Skre 		return 0;
197061f28255Scgd 	case NDEFUN:
197161f28255Scgd 		cmdputs(n->narg.text);
1972c02b3bbdSchristos 		cmdputs("() { ... }");
1973601249a2Skre 		return 0;
197461f28255Scgd 	case NCMD:
1975c02b3bbdSchristos 		cmdlist(n->ncmd.args, 1);
1976c02b3bbdSchristos 		cmdlist(n->ncmd.redirect, 0);
1977601249a2Skre 		if (!top && n->ncmd.backgnd) {
197809ecfab9Skre 			cmdputs(" &");
1979601249a2Skre 			return 1;
1980601249a2Skre 		}
1981601249a2Skre 		return 0;
198261f28255Scgd 	case NARG:
198361f28255Scgd 		cmdputs(n->narg.text);
1984601249a2Skre 		return 0;
198561f28255Scgd 	case NTO:
198661f28255Scgd 		p = ">";  i = 1;  goto redir;
1987f629aa28Schristos 	case NCLOBBER:
1988f629aa28Schristos 		p = ">|";  i = 1;  goto redir;
198961f28255Scgd 	case NAPPEND:
199061f28255Scgd 		p = ">>";  i = 1;  goto redir;
199161f28255Scgd 	case NTOFD:
199261f28255Scgd 		p = ">&";  i = 1;  goto redir;
199361f28255Scgd 	case NFROM:
199461f28255Scgd 		p = "<";  i = 0;  goto redir;
199561f28255Scgd 	case NFROMFD:
199661f28255Scgd 		p = "<&";  i = 0;  goto redir;
19976e50d7a8Schristos 	case NFROMTO:
19986e50d7a8Schristos 		p = "<>";  i = 0;  goto redir;
199961f28255Scgd  redir:
20003c6d76cdSkre 		if (n->nfile.fd != i)
20013c6d76cdSkre 			cmdputi(n->nfile.fd);
200261f28255Scgd 		cmdputs(p);
200361f28255Scgd 		if (n->type == NTOFD || n->type == NFROMFD) {
20043c6d76cdSkre 			if (n->ndup.dupfd < 0)
20053c6d76cdSkre 				cmdputs("-");
20063c6d76cdSkre 			else
20073c6d76cdSkre 				cmdputi(n->ndup.dupfd);
200861f28255Scgd 		} else {
2009601249a2Skre 			(void) cmdtxt(n->nfile.fname, 0);
201061f28255Scgd 		}
2011601249a2Skre 		return 0;
201261f28255Scgd 	case NHERE:
201361f28255Scgd 	case NXHERE:
201461f28255Scgd 		cmdputs("<<...");
2015601249a2Skre 		return 0;
201661f28255Scgd 	default:
201761f28255Scgd 		cmdputs("???");
2018601249a2Skre 		return 0;
201961f28255Scgd 	}
2020601249a2Skre 	return 0;
202161f28255Scgd }
202261f28255Scgd 
2023c02b3bbdSchristos STATIC void
2024c02b3bbdSchristos cmdlist(union node *np, int sep)
2025c02b3bbdSchristos {
2026c02b3bbdSchristos 	for (; np; np = np->narg.next) {
2027c02b3bbdSchristos 		if (!sep)
2028c02b3bbdSchristos 			cmdputs(" ");
2029601249a2Skre 		(void) cmdtxt(np, 0);
2030c02b3bbdSchristos 		if (sep && np->narg.next)
2031c02b3bbdSchristos 			cmdputs(" ");
2032c02b3bbdSchristos 	}
2033c02b3bbdSchristos }
203461f28255Scgd 
203561f28255Scgd 
203661f28255Scgd STATIC void
2037c02b3bbdSchristos cmdputs(const char *s)
203861f28255Scgd {
2039c02b3bbdSchristos 	const char *p, *str = 0;
2040c02b3bbdSchristos 	char c, cc[2] = " ";
2041c02b3bbdSchristos 	char *nextc;
2042c02b3bbdSchristos 	int nleft;
204361f28255Scgd 	int subtype = 0;
2044c02b3bbdSchristos 	int quoted = 0;
2045c02b3bbdSchristos 	static char vstype[16][4] = { "", "}", "-", "+", "?", "=",
2046d7c8afdfSkre 					"#", "##", "%", "%%", "}" };
204761f28255Scgd 
204861f28255Scgd 	p = s;
2049c02b3bbdSchristos 	nextc = cmdnextc;
2050c02b3bbdSchristos 	nleft = cmdnleft;
2051c02b3bbdSchristos 	while (nleft > 0 && (c = *p++) != 0) {
2052c02b3bbdSchristos 		switch (c) {
2053727a69dcSkre 		case CTLNONL:
2054727a69dcSkre 			c = '\0';
2055727a69dcSkre 			break;
2056c02b3bbdSchristos 		case CTLESC:
2057c02b3bbdSchristos 			c = *p++;
2058c02b3bbdSchristos 			break;
2059c02b3bbdSchristos 		case CTLVAR:
206061f28255Scgd 			subtype = *p++;
2061727a69dcSkre 			if (subtype & VSLINENO) {	/* undo LINENO hack */
2062c02b3bbdSchristos 				if ((subtype & VSTYPE) == VSLENGTH)
2063727a69dcSkre 					str = "${#LINENO";	/*}*/
2064d7c8afdfSkre 				else
2065727a69dcSkre 					str = "${LINENO";	/*}*/
2066d7c8afdfSkre 				while (is_digit(*p))
2067d7c8afdfSkre 					p++;
2068d7c8afdfSkre 			} else if ((subtype & VSTYPE) == VSLENGTH)
2069727a69dcSkre 				str = "${#"; /*}*/
207061f28255Scgd 			else
2071727a69dcSkre 				str = "${"; /*}*/
2072c02b3bbdSchristos 			if (!(subtype & VSQUOTE) != !(quoted & 1)) {
2073c02b3bbdSchristos 				quoted ^= 1;
2074c02b3bbdSchristos 				c = '"';
2075d7c8afdfSkre 			} else {
2076c02b3bbdSchristos 				c = *str++;
2077d7c8afdfSkre 			}
2078c02b3bbdSchristos 			break;
2079727a69dcSkre 		case CTLENDVAR:		/*{*/
2080c02b3bbdSchristos 			c = '}';
2081d7c8afdfSkre 			if (quoted & 1)
2082d7c8afdfSkre 				str = "\"";
2083c02b3bbdSchristos 			quoted >>= 1;
2084c02b3bbdSchristos 			subtype = 0;
2085c02b3bbdSchristos 			break;
2086c02b3bbdSchristos 		case CTLBACKQ:
2087c02b3bbdSchristos 			c = '$';
2088c02b3bbdSchristos 			str = "(...)";
2089c02b3bbdSchristos 			break;
2090c02b3bbdSchristos 		case CTLBACKQ+CTLQUOTE:
2091c02b3bbdSchristos 			c = '"';
2092c02b3bbdSchristos 			str = "$(...)\"";
2093c02b3bbdSchristos 			break;
2094c02b3bbdSchristos 		case CTLARI:
2095c02b3bbdSchristos 			c = '$';
2096727a69dcSkre 			if (*p == ' ')
2097727a69dcSkre 				p++;
2098727a69dcSkre 			str = "((";	/*))*/
2099c02b3bbdSchristos 			break;
2100727a69dcSkre 		case CTLENDARI:		/*((*/
2101c02b3bbdSchristos 			c = ')';
2102c02b3bbdSchristos 			str = ")";
2103c02b3bbdSchristos 			break;
2104c02b3bbdSchristos 		case CTLQUOTEMARK:
2105c02b3bbdSchristos 			quoted ^= 1;
2106c02b3bbdSchristos 			c = '"';
2107c02b3bbdSchristos 			break;
2108d7c8afdfSkre 		case CTLQUOTEEND:
2109d7c8afdfSkre 			quoted >>= 1;
2110d7c8afdfSkre 			c = '"';
2111d7c8afdfSkre 			break;
2112c02b3bbdSchristos 		case '=':
2113c02b3bbdSchristos 			if (subtype == 0)
2114c02b3bbdSchristos 				break;
2115c02b3bbdSchristos 			str = vstype[subtype & VSTYPE];
2116c02b3bbdSchristos 			if (subtype & VSNUL)
2117c02b3bbdSchristos 				c = ':';
2118c02b3bbdSchristos 			else
2119727a69dcSkre 				c = *str++;		/*{*/
2120c02b3bbdSchristos 			if (c != '}')
2121c02b3bbdSchristos 				quoted <<= 1;
2122d7c8afdfSkre 			else if (*p == CTLENDVAR)
2123d7c8afdfSkre 				c = *str++;
2124d7c8afdfSkre 			subtype = 0;
2125c02b3bbdSchristos 			break;
2126c02b3bbdSchristos 		case '\'':
2127c02b3bbdSchristos 		case '\\':
2128c02b3bbdSchristos 		case '"':
2129c02b3bbdSchristos 		case '$':
2130c02b3bbdSchristos 			/* These can only happen inside quotes */
2131c02b3bbdSchristos 			cc[0] = c;
2132c02b3bbdSchristos 			str = cc;
2133c02b3bbdSchristos 			c = '\\';
2134c02b3bbdSchristos 			break;
2135c02b3bbdSchristos 		default:
213661f28255Scgd 			break;
213761f28255Scgd 		}
2138d7c8afdfSkre 		if (c != '\0') do {	/* c == 0 implies nothing in str */
2139c02b3bbdSchristos 			*nextc++ = c;
2140c02b3bbdSchristos 		} while (--nleft > 0 && str && (c = *str++));
2141c02b3bbdSchristos 		str = 0;
214261f28255Scgd 	}
2143c02b3bbdSchristos 	if ((quoted & 1) && nleft) {
2144c02b3bbdSchristos 		*nextc++ = '"';
2145c02b3bbdSchristos 		nleft--;
2146c02b3bbdSchristos 	}
2147c02b3bbdSchristos 	cmdnleft = nleft;
2148c02b3bbdSchristos 	cmdnextc = nextc;
214961f28255Scgd }
2150