1c84f3f3cSopenharmony_ci/*	$OpenBSD: jobs.c,v 1.43 2015/09/10 22:48:58 nicm Exp $	*/
2c84f3f3cSopenharmony_ci
3c84f3f3cSopenharmony_ci/*-
4c84f3f3cSopenharmony_ci * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
5c84f3f3cSopenharmony_ci *		 2012, 2013, 2014, 2015, 2016, 2018, 2019
6c84f3f3cSopenharmony_ci *	mirabilos <m@mirbsd.org>
7c84f3f3cSopenharmony_ci *
8c84f3f3cSopenharmony_ci * Provided that these terms and disclaimer and all copyright notices
9c84f3f3cSopenharmony_ci * are retained or reproduced in an accompanying document, permission
10c84f3f3cSopenharmony_ci * is granted to deal in this work without restriction, including un-
11c84f3f3cSopenharmony_ci * limited rights to use, publicly perform, distribute, sell, modify,
12c84f3f3cSopenharmony_ci * merge, give away, or sublicence.
13c84f3f3cSopenharmony_ci *
14c84f3f3cSopenharmony_ci * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
15c84f3f3cSopenharmony_ci * the utmost extent permitted by applicable law, neither express nor
16c84f3f3cSopenharmony_ci * implied; without malicious intent or gross negligence. In no event
17c84f3f3cSopenharmony_ci * may a licensor, author or contributor be held liable for indirect,
18c84f3f3cSopenharmony_ci * direct, other damage, loss, or other issues arising in any way out
19c84f3f3cSopenharmony_ci * of dealing in the work, even if advised of the possibility of such
20c84f3f3cSopenharmony_ci * damage or existence of a defect, except proven that it results out
21c84f3f3cSopenharmony_ci * of said person's immediate fault when using the work as intended.
22c84f3f3cSopenharmony_ci */
23c84f3f3cSopenharmony_ci
24c84f3f3cSopenharmony_ci#include "sh.h"
25c84f3f3cSopenharmony_ci
26c84f3f3cSopenharmony_ci__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.128 2019/12/11 19:46:20 tg Exp $");
27c84f3f3cSopenharmony_ci
28c84f3f3cSopenharmony_ci#if HAVE_KILLPG
29c84f3f3cSopenharmony_ci#define mksh_killpg		killpg
30c84f3f3cSopenharmony_ci#else
31c84f3f3cSopenharmony_ci/* cross fingers and hope kill is killpg-endowed */
32c84f3f3cSopenharmony_ci#define mksh_killpg(p,s)	kill(-(p), (s))
33c84f3f3cSopenharmony_ci#endif
34c84f3f3cSopenharmony_ci
35c84f3f3cSopenharmony_ci/* Order important! */
36c84f3f3cSopenharmony_ci#define PRUNNING	0
37c84f3f3cSopenharmony_ci#define PEXITED		1
38c84f3f3cSopenharmony_ci#define PSIGNALLED	2
39c84f3f3cSopenharmony_ci#define PSTOPPED	3
40c84f3f3cSopenharmony_ci
41c84f3f3cSopenharmony_citypedef struct proc Proc;
42c84f3f3cSopenharmony_ci/* to take alignment into consideration */
43c84f3f3cSopenharmony_cistruct proc_dummy {
44c84f3f3cSopenharmony_ci	Proc *next;
45c84f3f3cSopenharmony_ci	pid_t pid;
46c84f3f3cSopenharmony_ci	int state;
47c84f3f3cSopenharmony_ci	int status;
48c84f3f3cSopenharmony_ci	char command[128];
49c84f3f3cSopenharmony_ci};
50c84f3f3cSopenharmony_ci/* real structure */
51c84f3f3cSopenharmony_cistruct proc {
52c84f3f3cSopenharmony_ci	/* next process in pipeline (if any) */
53c84f3f3cSopenharmony_ci	Proc *next;
54c84f3f3cSopenharmony_ci	/* process id of this Unix process in the job */
55c84f3f3cSopenharmony_ci	pid_t pid;
56c84f3f3cSopenharmony_ci	/* one of the four P… above */
57c84f3f3cSopenharmony_ci	int state;
58c84f3f3cSopenharmony_ci	/* wait status */
59c84f3f3cSopenharmony_ci	int status;
60c84f3f3cSopenharmony_ci	/* process command string from vistree */
61c84f3f3cSopenharmony_ci	char command[256 - (ALLOC_OVERHEAD +
62c84f3f3cSopenharmony_ci	    offsetof(struct proc_dummy, command[0]))];
63c84f3f3cSopenharmony_ci};
64c84f3f3cSopenharmony_ci
65c84f3f3cSopenharmony_ci/* Notify/print flag - j_print() argument */
66c84f3f3cSopenharmony_ci#define JP_SHORT	1	/* print signals processes were killed by */
67c84f3f3cSopenharmony_ci#define JP_MEDIUM	2	/* print [job-num] -/+ command */
68c84f3f3cSopenharmony_ci#define JP_LONG		3	/* print [job-num] -/+ pid command */
69c84f3f3cSopenharmony_ci#define JP_PGRP		4	/* print pgrp */
70c84f3f3cSopenharmony_ci
71c84f3f3cSopenharmony_ci/* put_job() flags */
72c84f3f3cSopenharmony_ci#define PJ_ON_FRONT	0	/* at very front */
73c84f3f3cSopenharmony_ci#define PJ_PAST_STOPPED	1	/* just past any stopped jobs */
74c84f3f3cSopenharmony_ci
75c84f3f3cSopenharmony_ci/* Job.flags values */
76c84f3f3cSopenharmony_ci#define JF_STARTED	0x001	/* set when all processes in job are started */
77c84f3f3cSopenharmony_ci#define JF_WAITING	0x002	/* set if j_waitj() is waiting on job */
78c84f3f3cSopenharmony_ci#define JF_W_ASYNCNOTIFY 0x004	/* set if waiting and async notification ok */
79c84f3f3cSopenharmony_ci#define JF_XXCOM	0x008	/* set for $(command) jobs */
80c84f3f3cSopenharmony_ci#define JF_FG		0x010	/* running in foreground (also has tty pgrp) */
81c84f3f3cSopenharmony_ci#define JF_SAVEDTTY	0x020	/* j->ttystat is valid */
82c84f3f3cSopenharmony_ci#define JF_CHANGED	0x040	/* process has changed state */
83c84f3f3cSopenharmony_ci#define JF_KNOWN	0x080	/* $! referenced */
84c84f3f3cSopenharmony_ci#define JF_ZOMBIE	0x100	/* known, unwaited process */
85c84f3f3cSopenharmony_ci#define JF_REMOVE	0x200	/* flagged for removal (j_jobs()/j_noityf()) */
86c84f3f3cSopenharmony_ci#define JF_USETTYMODE	0x400	/* tty mode saved if process exits normally */
87c84f3f3cSopenharmony_ci#define JF_SAVEDTTYPGRP	0x800	/* j->saved_ttypgrp is valid */
88c84f3f3cSopenharmony_ci
89c84f3f3cSopenharmony_citypedef struct job Job;
90c84f3f3cSopenharmony_cistruct job {
91c84f3f3cSopenharmony_ci	ALLOC_ITEM alloc_INT;	/* internal, do not touch */
92c84f3f3cSopenharmony_ci	Job *next;		/* next job in list */
93c84f3f3cSopenharmony_ci	Proc *proc_list;	/* process list */
94c84f3f3cSopenharmony_ci	Proc *last_proc;	/* last process in list */
95c84f3f3cSopenharmony_ci	struct timeval systime;	/* system time used by job */
96c84f3f3cSopenharmony_ci	struct timeval usrtime;	/* user time used by job */
97c84f3f3cSopenharmony_ci	pid_t pgrp;		/* process group of job */
98c84f3f3cSopenharmony_ci	pid_t ppid;		/* pid of process that forked job */
99c84f3f3cSopenharmony_ci	int job;		/* job number: %n */
100c84f3f3cSopenharmony_ci	int flags;		/* see JF_* */
101c84f3f3cSopenharmony_ci	volatile int state;	/* job state */
102c84f3f3cSopenharmony_ci	int status;		/* exit status of last process */
103c84f3f3cSopenharmony_ci	int age;		/* number of jobs started */
104c84f3f3cSopenharmony_ci	Coproc_id coproc_id;	/* 0 or id of coprocess output pipe */
105c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
106c84f3f3cSopenharmony_ci	mksh_ttyst ttystat;	/* saved tty state for stopped jobs */
107c84f3f3cSopenharmony_ci	pid_t saved_ttypgrp;	/* saved tty process group for stopped jobs */
108c84f3f3cSopenharmony_ci#endif
109c84f3f3cSopenharmony_ci};
110c84f3f3cSopenharmony_ci
111c84f3f3cSopenharmony_ci/* Flags for j_waitj() */
112c84f3f3cSopenharmony_ci#define JW_NONE		0x00
113c84f3f3cSopenharmony_ci#define JW_INTERRUPT	0x01	/* ^C will stop the wait */
114c84f3f3cSopenharmony_ci#define JW_ASYNCNOTIFY	0x02	/* asynchronous notification during wait ok */
115c84f3f3cSopenharmony_ci#define JW_STOPPEDWAIT	0x04	/* wait even if job stopped */
116c84f3f3cSopenharmony_ci#define JW_PIPEST	0x08	/* want PIPESTATUS */
117c84f3f3cSopenharmony_ci
118c84f3f3cSopenharmony_ci/* Error codes for j_lookup() */
119c84f3f3cSopenharmony_ci#define JL_NOSUCH	0	/* no such job */
120c84f3f3cSopenharmony_ci#define JL_AMBIG	1	/* %foo or %?foo is ambiguous */
121c84f3f3cSopenharmony_ci#define JL_INVALID	2	/* non-pid, non-% job id */
122c84f3f3cSopenharmony_ci
123c84f3f3cSopenharmony_cistatic const char * const lookup_msgs[] = {
124c84f3f3cSopenharmony_ci	"no such job",
125c84f3f3cSopenharmony_ci	"ambiguous",
126c84f3f3cSopenharmony_ci	"argument must be %job or process id"
127c84f3f3cSopenharmony_ci};
128c84f3f3cSopenharmony_ci
129c84f3f3cSopenharmony_cistatic Job *job_list;		/* job list */
130c84f3f3cSopenharmony_cistatic Job *last_job;
131c84f3f3cSopenharmony_cistatic Job *async_job;
132c84f3f3cSopenharmony_cistatic pid_t async_pid;
133c84f3f3cSopenharmony_ci
134c84f3f3cSopenharmony_cistatic int nzombie;		/* # of zombies owned by this process */
135c84f3f3cSopenharmony_cistatic int njobs;		/* # of jobs started */
136c84f3f3cSopenharmony_ci
137c84f3f3cSopenharmony_ci#ifndef CHILD_MAX
138c84f3f3cSopenharmony_ci#define CHILD_MAX	25
139c84f3f3cSopenharmony_ci#endif
140c84f3f3cSopenharmony_ci
141c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
142c84f3f3cSopenharmony_ci/* held_sigchld is set if sigchld occurs before a job is completely started */
143c84f3f3cSopenharmony_cistatic volatile sig_atomic_t held_sigchld;
144c84f3f3cSopenharmony_ci#endif
145c84f3f3cSopenharmony_ci
146c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
147c84f3f3cSopenharmony_cistatic struct shf	*shl_j;
148c84f3f3cSopenharmony_cistatic bool		ttypgrp_ok;	/* set if can use tty pgrps */
149c84f3f3cSopenharmony_cistatic pid_t		restore_ttypgrp = -1;
150c84f3f3cSopenharmony_cistatic int const	tt_sigs[] = { SIGTSTP, SIGTTIN, SIGTTOU };
151c84f3f3cSopenharmony_ci#endif
152c84f3f3cSopenharmony_ci
153c84f3f3cSopenharmony_cistatic void		j_set_async(Job *);
154c84f3f3cSopenharmony_cistatic void		j_startjob(Job *);
155c84f3f3cSopenharmony_cistatic int		j_waitj(Job *, int, const char *);
156c84f3f3cSopenharmony_cistatic void		j_sigchld(int);
157c84f3f3cSopenharmony_cistatic void		j_print(Job *, int, struct shf *);
158c84f3f3cSopenharmony_cistatic Job		*j_lookup(const char *, int *);
159c84f3f3cSopenharmony_cistatic Job		*new_job(void);
160c84f3f3cSopenharmony_cistatic Proc		*new_proc(void);
161c84f3f3cSopenharmony_cistatic void		check_job(Job *);
162c84f3f3cSopenharmony_cistatic void		put_job(Job *, int);
163c84f3f3cSopenharmony_cistatic void		remove_job(Job *, const char *);
164c84f3f3cSopenharmony_cistatic int		kill_job(Job *, int);
165c84f3f3cSopenharmony_ci
166c84f3f3cSopenharmony_cistatic void tty_init_talking(void);
167c84f3f3cSopenharmony_cistatic void tty_init_state(void);
168c84f3f3cSopenharmony_ci
169c84f3f3cSopenharmony_ci/* initialise job control */
170c84f3f3cSopenharmony_civoid
171c84f3f3cSopenharmony_cij_init(void)
172c84f3f3cSopenharmony_ci{
173c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
174c84f3f3cSopenharmony_ci	(void)sigemptyset(&sm_default);
175c84f3f3cSopenharmony_ci	sigprocmask(SIG_SETMASK, &sm_default, NULL);
176c84f3f3cSopenharmony_ci
177c84f3f3cSopenharmony_ci	(void)sigemptyset(&sm_sigchld);
178c84f3f3cSopenharmony_ci	(void)sigaddset(&sm_sigchld, SIGCHLD);
179c84f3f3cSopenharmony_ci
180c84f3f3cSopenharmony_ci	setsig(&sigtraps[SIGCHLD], j_sigchld,
181c84f3f3cSopenharmony_ci	    SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
182c84f3f3cSopenharmony_ci#else
183c84f3f3cSopenharmony_ci	/* Make sure SIGCHLD isn't ignored - can do odd things under SYSV */
184c84f3f3cSopenharmony_ci	setsig(&sigtraps[SIGCHLD], SIG_DFL, SS_RESTORE_ORIG|SS_FORCE);
185c84f3f3cSopenharmony_ci#endif
186c84f3f3cSopenharmony_ci
187c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
188c84f3f3cSopenharmony_ci	if (Flag(FMONITOR) == 127)
189c84f3f3cSopenharmony_ci		Flag(FMONITOR) = Flag(FTALKING);
190c84f3f3cSopenharmony_ci
191c84f3f3cSopenharmony_ci	/*
192c84f3f3cSopenharmony_ci	 * shl_j is used to do asynchronous notification (used in
193c84f3f3cSopenharmony_ci	 * an interrupt handler, so need a distinct shf)
194c84f3f3cSopenharmony_ci	 */
195c84f3f3cSopenharmony_ci	shl_j = shf_fdopen(2, SHF_WR, NULL);
196c84f3f3cSopenharmony_ci
197c84f3f3cSopenharmony_ci	if (Flag(FMONITOR) || Flag(FTALKING)) {
198c84f3f3cSopenharmony_ci		int i;
199c84f3f3cSopenharmony_ci
200c84f3f3cSopenharmony_ci		/*
201c84f3f3cSopenharmony_ci		 * the TF_SHELL_USES test is a kludge that lets us know if
202c84f3f3cSopenharmony_ci		 * if the signals have been changed by the shell.
203c84f3f3cSopenharmony_ci		 */
204c84f3f3cSopenharmony_ci		for (i = NELEM(tt_sigs); --i >= 0; ) {
205c84f3f3cSopenharmony_ci			sigtraps[tt_sigs[i]].flags |= TF_SHELL_USES;
206c84f3f3cSopenharmony_ci			/* j_change() sets this to SS_RESTORE_DFL if FMONITOR */
207c84f3f3cSopenharmony_ci			setsig(&sigtraps[tt_sigs[i]], SIG_IGN,
208c84f3f3cSopenharmony_ci			    SS_RESTORE_IGN|SS_FORCE);
209c84f3f3cSopenharmony_ci		}
210c84f3f3cSopenharmony_ci	}
211c84f3f3cSopenharmony_ci
212c84f3f3cSopenharmony_ci	/* j_change() calls tty_init_talking() and tty_init_state() */
213c84f3f3cSopenharmony_ci	if (Flag(FMONITOR))
214c84f3f3cSopenharmony_ci		j_change();
215c84f3f3cSopenharmony_ci	else
216c84f3f3cSopenharmony_ci#endif
217c84f3f3cSopenharmony_ci	  if (Flag(FTALKING)) {
218c84f3f3cSopenharmony_ci		tty_init_talking();
219c84f3f3cSopenharmony_ci		tty_init_state();
220c84f3f3cSopenharmony_ci	}
221c84f3f3cSopenharmony_ci}
222c84f3f3cSopenharmony_ci
223c84f3f3cSopenharmony_cistatic int
224c84f3f3cSopenharmony_ciproc_errorlevel(Proc *p)
225c84f3f3cSopenharmony_ci{
226c84f3f3cSopenharmony_ci	switch (p->state) {
227c84f3f3cSopenharmony_ci	case PEXITED:
228c84f3f3cSopenharmony_ci		return ((WEXITSTATUS(p->status)) & 255);
229c84f3f3cSopenharmony_ci	case PSIGNALLED:
230c84f3f3cSopenharmony_ci		return (ksh_sigmask(WTERMSIG(p->status)));
231c84f3f3cSopenharmony_ci	default:
232c84f3f3cSopenharmony_ci		return (0);
233c84f3f3cSopenharmony_ci	}
234c84f3f3cSopenharmony_ci}
235c84f3f3cSopenharmony_ci
236c84f3f3cSopenharmony_ci#if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
237c84f3f3cSopenharmony_ci/* suspend the shell */
238c84f3f3cSopenharmony_civoid
239c84f3f3cSopenharmony_cij_suspend(void)
240c84f3f3cSopenharmony_ci{
241c84f3f3cSopenharmony_ci	struct sigaction sa, osa;
242c84f3f3cSopenharmony_ci
243c84f3f3cSopenharmony_ci	/* Restore tty and pgrp. */
244c84f3f3cSopenharmony_ci	if (ttypgrp_ok) {
245c84f3f3cSopenharmony_ci		if (tty_hasstate)
246c84f3f3cSopenharmony_ci			mksh_tcset(tty_fd, &tty_state);
247c84f3f3cSopenharmony_ci		if (restore_ttypgrp >= 0) {
248c84f3f3cSopenharmony_ci			if (tcsetpgrp(tty_fd, restore_ttypgrp) < 0) {
249c84f3f3cSopenharmony_ci				warningf(false, Tf_ssfaileds,
250c84f3f3cSopenharmony_ci				    Tj_suspend, "tcsetpgrp", cstrerror(errno));
251c84f3f3cSopenharmony_ci			} else if (setpgid(0, restore_ttypgrp) < 0) {
252c84f3f3cSopenharmony_ci				warningf(false, Tf_ssfaileds,
253c84f3f3cSopenharmony_ci				    Tj_suspend, "setpgid", cstrerror(errno));
254c84f3f3cSopenharmony_ci			}
255c84f3f3cSopenharmony_ci		}
256c84f3f3cSopenharmony_ci	}
257c84f3f3cSopenharmony_ci
258c84f3f3cSopenharmony_ci	/* Suspend the shell. */
259c84f3f3cSopenharmony_ci	memset(&sa, 0, sizeof(sa));
260c84f3f3cSopenharmony_ci	sigemptyset(&sa.sa_mask);
261c84f3f3cSopenharmony_ci	sa.sa_handler = SIG_DFL;
262c84f3f3cSopenharmony_ci	sigaction(SIGTSTP, &sa, &osa);
263c84f3f3cSopenharmony_ci	kill(0, SIGTSTP);
264c84f3f3cSopenharmony_ci
265c84f3f3cSopenharmony_ci	/* Back from suspend, reset signals, pgrp and tty. */
266c84f3f3cSopenharmony_ci	sigaction(SIGTSTP, &osa, NULL);
267c84f3f3cSopenharmony_ci	if (ttypgrp_ok) {
268c84f3f3cSopenharmony_ci		if (restore_ttypgrp >= 0) {
269c84f3f3cSopenharmony_ci			if (setpgid(0, kshpid) < 0) {
270c84f3f3cSopenharmony_ci				warningf(false, Tf_ssfaileds,
271c84f3f3cSopenharmony_ci				    Tj_suspend, "setpgid", cstrerror(errno));
272c84f3f3cSopenharmony_ci				ttypgrp_ok = false;
273c84f3f3cSopenharmony_ci			} else if (tcsetpgrp(tty_fd, kshpid) < 0) {
274c84f3f3cSopenharmony_ci				warningf(false, Tf_ssfaileds,
275c84f3f3cSopenharmony_ci				    Tj_suspend, "tcsetpgrp", cstrerror(errno));
276c84f3f3cSopenharmony_ci				ttypgrp_ok = false;
277c84f3f3cSopenharmony_ci			}
278c84f3f3cSopenharmony_ci		}
279c84f3f3cSopenharmony_ci		tty_init_state();
280c84f3f3cSopenharmony_ci	}
281c84f3f3cSopenharmony_ci}
282c84f3f3cSopenharmony_ci#endif
283c84f3f3cSopenharmony_ci
284c84f3f3cSopenharmony_ci/* job cleanup before shell exit */
285c84f3f3cSopenharmony_civoid
286c84f3f3cSopenharmony_cij_exit(void)
287c84f3f3cSopenharmony_ci{
288c84f3f3cSopenharmony_ci	/* kill stopped, and possibly running, jobs */
289c84f3f3cSopenharmony_ci	Job *j;
290c84f3f3cSopenharmony_ci	bool killed = false;
291c84f3f3cSopenharmony_ci
292c84f3f3cSopenharmony_ci	for (j = job_list; j != NULL; j = j->next) {
293c84f3f3cSopenharmony_ci		if (j->ppid == procpid &&
294c84f3f3cSopenharmony_ci		    (j->state == PSTOPPED ||
295c84f3f3cSopenharmony_ci		    (j->state == PRUNNING &&
296c84f3f3cSopenharmony_ci		    ((j->flags & JF_FG) ||
297c84f3f3cSopenharmony_ci		    (Flag(FLOGIN) && !Flag(FNOHUP) && procpid == kshpid))))) {
298c84f3f3cSopenharmony_ci			killed = true;
299c84f3f3cSopenharmony_ci			if (j->pgrp == 0)
300c84f3f3cSopenharmony_ci				kill_job(j, SIGHUP);
301c84f3f3cSopenharmony_ci			else
302c84f3f3cSopenharmony_ci				mksh_killpg(j->pgrp, SIGHUP);
303c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
304c84f3f3cSopenharmony_ci			if (j->state == PSTOPPED) {
305c84f3f3cSopenharmony_ci				if (j->pgrp == 0)
306c84f3f3cSopenharmony_ci					kill_job(j, SIGCONT);
307c84f3f3cSopenharmony_ci				else
308c84f3f3cSopenharmony_ci					mksh_killpg(j->pgrp, SIGCONT);
309c84f3f3cSopenharmony_ci			}
310c84f3f3cSopenharmony_ci#endif
311c84f3f3cSopenharmony_ci		}
312c84f3f3cSopenharmony_ci	}
313c84f3f3cSopenharmony_ci	if (killed)
314c84f3f3cSopenharmony_ci		sleep(1);
315c84f3f3cSopenharmony_ci	j_notify();
316c84f3f3cSopenharmony_ci
317c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
318c84f3f3cSopenharmony_ci	if (kshpid == procpid && restore_ttypgrp >= 0) {
319c84f3f3cSopenharmony_ci		/*
320c84f3f3cSopenharmony_ci		 * Need to restore the tty pgrp to what it was when the
321c84f3f3cSopenharmony_ci		 * shell started up, so that the process that started us
322c84f3f3cSopenharmony_ci		 * will be able to access the tty when we are done.
323c84f3f3cSopenharmony_ci		 * Also need to restore our process group in case we are
324c84f3f3cSopenharmony_ci		 * about to do an exec so that both our parent and the
325c84f3f3cSopenharmony_ci		 * process we are to become will be able to access the tty.
326c84f3f3cSopenharmony_ci		 */
327c84f3f3cSopenharmony_ci		tcsetpgrp(tty_fd, restore_ttypgrp);
328c84f3f3cSopenharmony_ci		setpgid(0, restore_ttypgrp);
329c84f3f3cSopenharmony_ci	}
330c84f3f3cSopenharmony_ci	if (Flag(FMONITOR)) {
331c84f3f3cSopenharmony_ci		Flag(FMONITOR) = 0;
332c84f3f3cSopenharmony_ci		j_change();
333c84f3f3cSopenharmony_ci	}
334c84f3f3cSopenharmony_ci#endif
335c84f3f3cSopenharmony_ci}
336c84f3f3cSopenharmony_ci
337c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
338c84f3f3cSopenharmony_ci/* turn job control on or off according to Flag(FMONITOR) */
339c84f3f3cSopenharmony_civoid
340c84f3f3cSopenharmony_cij_change(void)
341c84f3f3cSopenharmony_ci{
342c84f3f3cSopenharmony_ci	int i;
343c84f3f3cSopenharmony_ci
344c84f3f3cSopenharmony_ci	if (Flag(FMONITOR)) {
345c84f3f3cSopenharmony_ci		bool use_tty = Flag(FTALKING);
346c84f3f3cSopenharmony_ci
347c84f3f3cSopenharmony_ci		/* don't call mksh_tcget until we own the tty process group */
348c84f3f3cSopenharmony_ci		if (use_tty)
349c84f3f3cSopenharmony_ci			tty_init_talking();
350c84f3f3cSopenharmony_ci
351c84f3f3cSopenharmony_ci		/* no controlling tty, no SIGT* */
352c84f3f3cSopenharmony_ci#ifndef ADAPT_FOR_LITEOS_A
353c84f3f3cSopenharmony_ci		if ((ttypgrp_ok = (use_tty && tty_fd >= 0 && tty_devtty))) {
354c84f3f3cSopenharmony_ci#else // ADAPT_FOR_LITEOS_A
355c84f3f3cSopenharmony_ci		if ((ttypgrp_ok = (use_tty && tty_fd >= 0))) {
356c84f3f3cSopenharmony_ci#endif // ADAPT_FOR_LITEOS_A
357c84f3f3cSopenharmony_ci			setsig(&sigtraps[SIGTTIN], SIG_DFL,
358c84f3f3cSopenharmony_ci			    SS_RESTORE_ORIG|SS_FORCE);
359c84f3f3cSopenharmony_ci			/* wait to be given tty (POSIX.1, B.2, job control) */
360c84f3f3cSopenharmony_ci			while (/* CONSTCOND */ 1) {
361c84f3f3cSopenharmony_ci				pid_t ttypgrp;
362c84f3f3cSopenharmony_ci
363c84f3f3cSopenharmony_ci				if ((ttypgrp = tcgetpgrp(tty_fd)) < 0) {
364c84f3f3cSopenharmony_ci					warningf(false, Tf_ssfaileds,
365c84f3f3cSopenharmony_ci					    "j_init", "tcgetpgrp",
366c84f3f3cSopenharmony_ci					    cstrerror(errno));
367c84f3f3cSopenharmony_ci					ttypgrp_ok = false;
368c84f3f3cSopenharmony_ci					break;
369c84f3f3cSopenharmony_ci				}
370c84f3f3cSopenharmony_ci				if (ttypgrp == kshpgrp)
371c84f3f3cSopenharmony_ci					break;
372c84f3f3cSopenharmony_ci				kill(0, SIGTTIN);
373c84f3f3cSopenharmony_ci			}
374c84f3f3cSopenharmony_ci		}
375c84f3f3cSopenharmony_ci		for (i = NELEM(tt_sigs); --i >= 0; )
376c84f3f3cSopenharmony_ci			setsig(&sigtraps[tt_sigs[i]], SIG_IGN,
377c84f3f3cSopenharmony_ci			    SS_RESTORE_DFL|SS_FORCE);
378c84f3f3cSopenharmony_ci		if (ttypgrp_ok && kshpgrp != kshpid) {
379c84f3f3cSopenharmony_ci			if (setpgid(0, kshpid) < 0) {
380c84f3f3cSopenharmony_ci				warningf(false, Tf_ssfaileds,
381c84f3f3cSopenharmony_ci				    "j_init", "setpgid", cstrerror(errno));
382c84f3f3cSopenharmony_ci				ttypgrp_ok = false;
383c84f3f3cSopenharmony_ci			} else {
384c84f3f3cSopenharmony_ci				if (tcsetpgrp(tty_fd, kshpid) < 0) {
385c84f3f3cSopenharmony_ci					warningf(false, Tf_ssfaileds,
386c84f3f3cSopenharmony_ci					    "j_init", "tcsetpgrp",
387c84f3f3cSopenharmony_ci					    cstrerror(errno));
388c84f3f3cSopenharmony_ci					ttypgrp_ok = false;
389c84f3f3cSopenharmony_ci				} else
390c84f3f3cSopenharmony_ci					restore_ttypgrp = kshpgrp;
391c84f3f3cSopenharmony_ci				kshpgrp = kshpid;
392c84f3f3cSopenharmony_ci			}
393c84f3f3cSopenharmony_ci		}
394c84f3f3cSopenharmony_ci#ifndef MKSH_DISABLE_TTY_WARNING
395c84f3f3cSopenharmony_ci		if (use_tty && !ttypgrp_ok)
396c84f3f3cSopenharmony_ci			warningf(false, Tf_sD_s, "warning",
397c84f3f3cSopenharmony_ci			    "won't have full job control");
398c84f3f3cSopenharmony_ci#endif
399c84f3f3cSopenharmony_ci	} else {
400c84f3f3cSopenharmony_ci		ttypgrp_ok = false;
401c84f3f3cSopenharmony_ci		if (Flag(FTALKING))
402c84f3f3cSopenharmony_ci			for (i = NELEM(tt_sigs); --i >= 0; )
403c84f3f3cSopenharmony_ci				setsig(&sigtraps[tt_sigs[i]], SIG_IGN,
404c84f3f3cSopenharmony_ci				    SS_RESTORE_IGN|SS_FORCE);
405c84f3f3cSopenharmony_ci		else
406c84f3f3cSopenharmony_ci			for (i = NELEM(tt_sigs); --i >= 0; ) {
407c84f3f3cSopenharmony_ci				if (sigtraps[tt_sigs[i]].flags &
408c84f3f3cSopenharmony_ci				    (TF_ORIG_IGN | TF_ORIG_DFL))
409c84f3f3cSopenharmony_ci					setsig(&sigtraps[tt_sigs[i]],
410c84f3f3cSopenharmony_ci					    (sigtraps[tt_sigs[i]].flags & TF_ORIG_IGN) ?
411c84f3f3cSopenharmony_ci					    SIG_IGN : SIG_DFL,
412c84f3f3cSopenharmony_ci					    SS_RESTORE_ORIG|SS_FORCE);
413c84f3f3cSopenharmony_ci			}
414c84f3f3cSopenharmony_ci	}
415c84f3f3cSopenharmony_ci	tty_init_state();
416c84f3f3cSopenharmony_ci}
417c84f3f3cSopenharmony_ci#endif
418c84f3f3cSopenharmony_ci
419c84f3f3cSopenharmony_ci#if HAVE_NICE
420c84f3f3cSopenharmony_ci/* run nice(3) and ignore the result */
421c84f3f3cSopenharmony_cistatic void
422c84f3f3cSopenharmony_ciksh_nice(int ness)
423c84f3f3cSopenharmony_ci{
424c84f3f3cSopenharmony_ci#if defined(__USE_FORTIFY_LEVEL) && (__USE_FORTIFY_LEVEL > 0)
425c84f3f3cSopenharmony_ci	int eno;
426c84f3f3cSopenharmony_ci
427c84f3f3cSopenharmony_ci	errno = 0;
428c84f3f3cSopenharmony_ci	/* this is gonna annoy users; complain to your distro, people! */
429c84f3f3cSopenharmony_ci	if (nice(ness) == -1 && (eno = errno) != 0)
430c84f3f3cSopenharmony_ci		warningf(false, Tf_sD_s, "bgnice", cstrerror(eno));
431c84f3f3cSopenharmony_ci#else
432c84f3f3cSopenharmony_ci	(void)nice(ness);
433c84f3f3cSopenharmony_ci#endif
434c84f3f3cSopenharmony_ci}
435c84f3f3cSopenharmony_ci#endif
436c84f3f3cSopenharmony_ci
437c84f3f3cSopenharmony_ci/* execute tree in child subprocess */
438c84f3f3cSopenharmony_ciint
439c84f3f3cSopenharmony_ciexchild(struct op *t, int flags,
440c84f3f3cSopenharmony_ci    volatile int *xerrok,
441c84f3f3cSopenharmony_ci    /* used if XPCLOSE or XCCLOSE */
442c84f3f3cSopenharmony_ci    int close_fd)
443c84f3f3cSopenharmony_ci{
444c84f3f3cSopenharmony_ci	/* for pipelines */
445c84f3f3cSopenharmony_ci	static Proc *last_proc;
446c84f3f3cSopenharmony_ci
447c84f3f3cSopenharmony_ci	int rv = 0, forksleep, jwflags = JW_NONE;
448c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
449c84f3f3cSopenharmony_ci	sigset_t omask;
450c84f3f3cSopenharmony_ci#endif
451c84f3f3cSopenharmony_ci	Proc *p;
452c84f3f3cSopenharmony_ci	Job *j;
453c84f3f3cSopenharmony_ci	pid_t cldpid;
454c84f3f3cSopenharmony_ci
455c84f3f3cSopenharmony_ci	if (flags & XPIPEST) {
456c84f3f3cSopenharmony_ci		flags &= ~XPIPEST;
457c84f3f3cSopenharmony_ci		jwflags |= JW_PIPEST;
458c84f3f3cSopenharmony_ci	}
459c84f3f3cSopenharmony_ci
460c84f3f3cSopenharmony_ci	if (flags & XEXEC)
461c84f3f3cSopenharmony_ci		/*
462c84f3f3cSopenharmony_ci		 * Clear XFORK|XPCLOSE|XCCLOSE|XCOPROC|XPIPEO|XPIPEI|XXCOM|XBGND
463c84f3f3cSopenharmony_ci		 * (also done in another execute() below)
464c84f3f3cSopenharmony_ci		 */
465c84f3f3cSopenharmony_ci		return (execute(t, flags & (XEXEC | XERROK), xerrok));
466c84f3f3cSopenharmony_ci
467c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
468c84f3f3cSopenharmony_ci	/* no SIGCHLDs while messing with job and process lists */
469c84f3f3cSopenharmony_ci	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
470c84f3f3cSopenharmony_ci#endif
471c84f3f3cSopenharmony_ci
472c84f3f3cSopenharmony_ci	p = new_proc();
473c84f3f3cSopenharmony_ci	p->next = NULL;
474c84f3f3cSopenharmony_ci	p->state = PRUNNING;
475c84f3f3cSopenharmony_ci	p->status = 0;
476c84f3f3cSopenharmony_ci	p->pid = 0;
477c84f3f3cSopenharmony_ci
478c84f3f3cSopenharmony_ci	/* link process into jobs list */
479c84f3f3cSopenharmony_ci	if (flags & XPIPEI) {
480c84f3f3cSopenharmony_ci		/* continuing with a pipe */
481c84f3f3cSopenharmony_ci		if (!last_job)
482c84f3f3cSopenharmony_ci			internal_errorf("exchild: XPIPEI and no last_job - pid %d",
483c84f3f3cSopenharmony_ci			    (int)procpid);
484c84f3f3cSopenharmony_ci		j = last_job;
485c84f3f3cSopenharmony_ci		if (last_proc)
486c84f3f3cSopenharmony_ci			last_proc->next = p;
487c84f3f3cSopenharmony_ci		last_proc = p;
488c84f3f3cSopenharmony_ci	} else {
489c84f3f3cSopenharmony_ci		/* fills in j->job */
490c84f3f3cSopenharmony_ci		j = new_job();
491c84f3f3cSopenharmony_ci		/*
492c84f3f3cSopenharmony_ci		 * we don't consider XXCOMs foreground since they don't get
493c84f3f3cSopenharmony_ci		 * tty process group and we don't save or restore tty modes.
494c84f3f3cSopenharmony_ci		 */
495c84f3f3cSopenharmony_ci		j->flags = (flags & XXCOM) ? JF_XXCOM :
496c84f3f3cSopenharmony_ci		    ((flags & XBGND) ? 0 : (JF_FG|JF_USETTYMODE));
497c84f3f3cSopenharmony_ci		timerclear(&j->usrtime);
498c84f3f3cSopenharmony_ci		timerclear(&j->systime);
499c84f3f3cSopenharmony_ci		j->state = PRUNNING;
500c84f3f3cSopenharmony_ci		j->pgrp = 0;
501c84f3f3cSopenharmony_ci		j->ppid = procpid;
502c84f3f3cSopenharmony_ci		j->age = ++njobs;
503c84f3f3cSopenharmony_ci		j->proc_list = p;
504c84f3f3cSopenharmony_ci		j->coproc_id = 0;
505c84f3f3cSopenharmony_ci		last_job = j;
506c84f3f3cSopenharmony_ci		last_proc = p;
507c84f3f3cSopenharmony_ci		put_job(j, PJ_PAST_STOPPED);
508c84f3f3cSopenharmony_ci	}
509c84f3f3cSopenharmony_ci
510c84f3f3cSopenharmony_ci	vistree(p->command, sizeof(p->command), t);
511c84f3f3cSopenharmony_ci
512c84f3f3cSopenharmony_ci	/* create child process */
513c84f3f3cSopenharmony_ci	forksleep = 1;
514c84f3f3cSopenharmony_ci	while ((cldpid = fork()) < 0 && errno == EAGAIN && forksleep < 32) {
515c84f3f3cSopenharmony_ci		if (intrsig)
516c84f3f3cSopenharmony_ci			/* allow user to ^C out... */
517c84f3f3cSopenharmony_ci			break;
518c84f3f3cSopenharmony_ci		sleep(forksleep);
519c84f3f3cSopenharmony_ci		forksleep <<= 1;
520c84f3f3cSopenharmony_ci	}
521c84f3f3cSopenharmony_ci	/* ensure $RANDOM changes between parent and child */
522c84f3f3cSopenharmony_ci	rndset((unsigned long)cldpid);
523c84f3f3cSopenharmony_ci	/* fork failed? */
524c84f3f3cSopenharmony_ci	if (cldpid < 0) {
525c84f3f3cSopenharmony_ci		kill_job(j, SIGKILL);
526c84f3f3cSopenharmony_ci		remove_job(j, "fork failed");
527c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
528c84f3f3cSopenharmony_ci		sigprocmask(SIG_SETMASK, &omask, NULL);
529c84f3f3cSopenharmony_ci#endif
530c84f3f3cSopenharmony_ci		errorf("can't fork - try again");
531c84f3f3cSopenharmony_ci	}
532c84f3f3cSopenharmony_ci	p->pid = cldpid ? cldpid : (procpid = getpid());
533c84f3f3cSopenharmony_ci
534c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
535c84f3f3cSopenharmony_ci	/* job control set up */
536c84f3f3cSopenharmony_ci	if (Flag(FMONITOR) && !(flags&XXCOM)) {
537c84f3f3cSopenharmony_ci		bool dotty = false;
538c84f3f3cSopenharmony_ci
539c84f3f3cSopenharmony_ci		if (j->pgrp == 0) {
540c84f3f3cSopenharmony_ci			/* First process */
541c84f3f3cSopenharmony_ci			j->pgrp = p->pid;
542c84f3f3cSopenharmony_ci			dotty = true;
543c84f3f3cSopenharmony_ci		}
544c84f3f3cSopenharmony_ci
545c84f3f3cSopenharmony_ci		/*
546c84f3f3cSopenharmony_ci		 * set pgrp in both parent and child to deal with race
547c84f3f3cSopenharmony_ci		 * condition
548c84f3f3cSopenharmony_ci		 */
549c84f3f3cSopenharmony_ci		setpgid(p->pid, j->pgrp);
550c84f3f3cSopenharmony_ci		if (ttypgrp_ok && dotty && !(flags & XBGND))
551c84f3f3cSopenharmony_ci			tcsetpgrp(tty_fd, j->pgrp);
552c84f3f3cSopenharmony_ci	}
553c84f3f3cSopenharmony_ci#endif
554c84f3f3cSopenharmony_ci
555c84f3f3cSopenharmony_ci	/* used to close pipe input fd */
556c84f3f3cSopenharmony_ci	if (close_fd >= 0 && (((flags & XPCLOSE) && cldpid) ||
557c84f3f3cSopenharmony_ci	    ((flags & XCCLOSE) && !cldpid)))
558c84f3f3cSopenharmony_ci		close(close_fd);
559c84f3f3cSopenharmony_ci	if (!cldpid) {
560c84f3f3cSopenharmony_ci		/* child */
561c84f3f3cSopenharmony_ci
562c84f3f3cSopenharmony_ci		/* Do this before restoring signal */
563c84f3f3cSopenharmony_ci		if (flags & XCOPROC)
564c84f3f3cSopenharmony_ci			coproc_cleanup(false);
565c84f3f3cSopenharmony_ci		cleanup_parents_env();
566c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
567c84f3f3cSopenharmony_ci		/*
568c84f3f3cSopenharmony_ci		 * If FMONITOR or FTALKING is set, these signals are ignored,
569c84f3f3cSopenharmony_ci		 * if neither FMONITOR nor FTALKING are set, the signals have
570c84f3f3cSopenharmony_ci		 * their inherited values.
571c84f3f3cSopenharmony_ci		 */
572c84f3f3cSopenharmony_ci		if (Flag(FMONITOR) && !(flags & XXCOM)) {
573c84f3f3cSopenharmony_ci			for (forksleep = NELEM(tt_sigs); --forksleep >= 0; )
574c84f3f3cSopenharmony_ci				setsig(&sigtraps[tt_sigs[forksleep]], SIG_DFL,
575c84f3f3cSopenharmony_ci				    SS_RESTORE_DFL|SS_FORCE);
576c84f3f3cSopenharmony_ci		}
577c84f3f3cSopenharmony_ci#endif
578c84f3f3cSopenharmony_ci#if HAVE_NICE
579c84f3f3cSopenharmony_ci		if (Flag(FBGNICE) && (flags & XBGND))
580c84f3f3cSopenharmony_ci			ksh_nice(4);
581c84f3f3cSopenharmony_ci#endif
582c84f3f3cSopenharmony_ci		if ((flags & XBGND)
583c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
584c84f3f3cSopenharmony_ci		    && !Flag(FMONITOR)
585c84f3f3cSopenharmony_ci#endif
586c84f3f3cSopenharmony_ci		    ) {
587c84f3f3cSopenharmony_ci			setsig(&sigtraps[SIGINT], SIG_IGN,
588c84f3f3cSopenharmony_ci			    SS_RESTORE_IGN|SS_FORCE);
589c84f3f3cSopenharmony_ci			setsig(&sigtraps[SIGQUIT], SIG_IGN,
590c84f3f3cSopenharmony_ci			    SS_RESTORE_IGN|SS_FORCE);
591c84f3f3cSopenharmony_ci			if ((!(flags & (XPIPEI | XCOPROC))) &&
592c84f3f3cSopenharmony_ci			    ((forksleep = open("/dev/null", 0)) > 0)) {
593c84f3f3cSopenharmony_ci				(void)ksh_dup2(forksleep, 0, true);
594c84f3f3cSopenharmony_ci				close(forksleep);
595c84f3f3cSopenharmony_ci			}
596c84f3f3cSopenharmony_ci		}
597c84f3f3cSopenharmony_ci		/* in case of $(jobs) command */
598c84f3f3cSopenharmony_ci		remove_job(j, "child");
599c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
600c84f3f3cSopenharmony_ci		/* remove_job needs SIGCHLD blocked still */
601c84f3f3cSopenharmony_ci		sigprocmask(SIG_SETMASK, &omask, NULL);
602c84f3f3cSopenharmony_ci#endif
603c84f3f3cSopenharmony_ci		nzombie = 0;
604c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
605c84f3f3cSopenharmony_ci		ttypgrp_ok = false;
606c84f3f3cSopenharmony_ci		Flag(FMONITOR) = 0;
607c84f3f3cSopenharmony_ci#endif
608c84f3f3cSopenharmony_ci		Flag(FTALKING) = 0;
609c84f3f3cSopenharmony_ci		cleartraps();
610c84f3f3cSopenharmony_ci		/* no return */
611c84f3f3cSopenharmony_ci		execute(t, (flags & XERROK) | XEXEC, NULL);
612c84f3f3cSopenharmony_ci#ifndef MKSH_SMALL
613c84f3f3cSopenharmony_ci		if (t->type == TPIPE)
614c84f3f3cSopenharmony_ci			unwind(LLEAVE);
615c84f3f3cSopenharmony_ci		internal_warningf("%s: execute() returned", "exchild");
616c84f3f3cSopenharmony_ci		fptreef(shl_out, 8, "%s: tried to execute {\n\t%T\n}\n",
617c84f3f3cSopenharmony_ci		    "exchild", t);
618c84f3f3cSopenharmony_ci		shf_flush(shl_out);
619c84f3f3cSopenharmony_ci#endif
620c84f3f3cSopenharmony_ci		unwind(LLEAVE);
621c84f3f3cSopenharmony_ci		/* NOTREACHED */
622c84f3f3cSopenharmony_ci	}
623c84f3f3cSopenharmony_ci
624c84f3f3cSopenharmony_ci	/* shell (parent) stuff */
625c84f3f3cSopenharmony_ci	if (!(flags & XPIPEO)) {
626c84f3f3cSopenharmony_ci		/* last process in a job */
627c84f3f3cSopenharmony_ci		j_startjob(j);
628c84f3f3cSopenharmony_ci		if (flags & XCOPROC) {
629c84f3f3cSopenharmony_ci			j->coproc_id = coproc.id;
630c84f3f3cSopenharmony_ci			/* n jobs using co-process output */
631c84f3f3cSopenharmony_ci			coproc.njobs++;
632c84f3f3cSopenharmony_ci			/* j using co-process input */
633c84f3f3cSopenharmony_ci			coproc.job = (void *)j;
634c84f3f3cSopenharmony_ci		}
635c84f3f3cSopenharmony_ci		if (flags & XBGND) {
636c84f3f3cSopenharmony_ci			j_set_async(j);
637c84f3f3cSopenharmony_ci			if (Flag(FTALKING)) {
638c84f3f3cSopenharmony_ci				shf_fprintf(shl_out, "[%d]", j->job);
639c84f3f3cSopenharmony_ci				for (p = j->proc_list; p; p = p->next)
640c84f3f3cSopenharmony_ci					shf_fprintf(shl_out, Tf__d,
641c84f3f3cSopenharmony_ci					    (int)p->pid);
642c84f3f3cSopenharmony_ci				shf_putchar('\n', shl_out);
643c84f3f3cSopenharmony_ci				shf_flush(shl_out);
644c84f3f3cSopenharmony_ci			}
645c84f3f3cSopenharmony_ci		} else
646c84f3f3cSopenharmony_ci			rv = j_waitj(j, jwflags, "jw:last proc");
647c84f3f3cSopenharmony_ci	}
648c84f3f3cSopenharmony_ci
649c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
650c84f3f3cSopenharmony_ci	sigprocmask(SIG_SETMASK, &omask, NULL);
651c84f3f3cSopenharmony_ci#endif
652c84f3f3cSopenharmony_ci
653c84f3f3cSopenharmony_ci	return (rv);
654c84f3f3cSopenharmony_ci}
655c84f3f3cSopenharmony_ci
656c84f3f3cSopenharmony_ci/* start the last job: only used for $(command) jobs */
657c84f3f3cSopenharmony_civoid
658c84f3f3cSopenharmony_cistartlast(void)
659c84f3f3cSopenharmony_ci{
660c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
661c84f3f3cSopenharmony_ci	sigset_t omask;
662c84f3f3cSopenharmony_ci
663c84f3f3cSopenharmony_ci	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
664c84f3f3cSopenharmony_ci#endif
665c84f3f3cSopenharmony_ci
666c84f3f3cSopenharmony_ci	/* no need to report error - waitlast() will do it */
667c84f3f3cSopenharmony_ci	if (last_job) {
668c84f3f3cSopenharmony_ci		/* ensure it isn't removed by check_job() */
669c84f3f3cSopenharmony_ci		last_job->flags |= JF_WAITING;
670c84f3f3cSopenharmony_ci		j_startjob(last_job);
671c84f3f3cSopenharmony_ci	}
672c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
673c84f3f3cSopenharmony_ci	sigprocmask(SIG_SETMASK, &omask, NULL);
674c84f3f3cSopenharmony_ci#endif
675c84f3f3cSopenharmony_ci}
676c84f3f3cSopenharmony_ci
677c84f3f3cSopenharmony_ci/* wait for last job: only used for $(command) jobs */
678c84f3f3cSopenharmony_ciint
679c84f3f3cSopenharmony_ciwaitlast(void)
680c84f3f3cSopenharmony_ci{
681c84f3f3cSopenharmony_ci	int rv;
682c84f3f3cSopenharmony_ci	Job *j;
683c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
684c84f3f3cSopenharmony_ci	sigset_t omask;
685c84f3f3cSopenharmony_ci
686c84f3f3cSopenharmony_ci	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
687c84f3f3cSopenharmony_ci#endif
688c84f3f3cSopenharmony_ci
689c84f3f3cSopenharmony_ci	j = last_job;
690c84f3f3cSopenharmony_ci	if (!j || !(j->flags & JF_STARTED)) {
691c84f3f3cSopenharmony_ci		if (!j)
692c84f3f3cSopenharmony_ci			warningf(true, Tf_sD_s, "waitlast", "no last job");
693c84f3f3cSopenharmony_ci		else
694c84f3f3cSopenharmony_ci			internal_warningf(Tf_sD_s, "waitlast", Tnot_started);
695c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
696c84f3f3cSopenharmony_ci		sigprocmask(SIG_SETMASK, &omask, NULL);
697c84f3f3cSopenharmony_ci#endif
698c84f3f3cSopenharmony_ci		/* not so arbitrary, non-zero value */
699c84f3f3cSopenharmony_ci		return (125);
700c84f3f3cSopenharmony_ci	}
701c84f3f3cSopenharmony_ci
702c84f3f3cSopenharmony_ci	rv = j_waitj(j, JW_NONE, "waitlast");
703c84f3f3cSopenharmony_ci
704c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
705c84f3f3cSopenharmony_ci	sigprocmask(SIG_SETMASK, &omask, NULL);
706c84f3f3cSopenharmony_ci#endif
707c84f3f3cSopenharmony_ci
708c84f3f3cSopenharmony_ci	return (rv);
709c84f3f3cSopenharmony_ci}
710c84f3f3cSopenharmony_ci
711c84f3f3cSopenharmony_ci/* wait for child, interruptable. */
712c84f3f3cSopenharmony_ciint
713c84f3f3cSopenharmony_ciwaitfor(const char *cp, int *sigp)
714c84f3f3cSopenharmony_ci{
715c84f3f3cSopenharmony_ci	int rv, ecode, flags = JW_INTERRUPT|JW_ASYNCNOTIFY;
716c84f3f3cSopenharmony_ci	Job *j;
717c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
718c84f3f3cSopenharmony_ci	sigset_t omask;
719c84f3f3cSopenharmony_ci
720c84f3f3cSopenharmony_ci	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
721c84f3f3cSopenharmony_ci#endif
722c84f3f3cSopenharmony_ci
723c84f3f3cSopenharmony_ci	*sigp = 0;
724c84f3f3cSopenharmony_ci
725c84f3f3cSopenharmony_ci	if (cp == NULL) {
726c84f3f3cSopenharmony_ci		/*
727c84f3f3cSopenharmony_ci		 * wait for an unspecified job - always returns 0, so
728c84f3f3cSopenharmony_ci		 * don't have to worry about exited/signaled jobs
729c84f3f3cSopenharmony_ci		 */
730c84f3f3cSopenharmony_ci		for (j = job_list; j; j = j->next)
731c84f3f3cSopenharmony_ci			/* AT&T ksh will wait for stopped jobs - we don't */
732c84f3f3cSopenharmony_ci			if (j->ppid == procpid && j->state == PRUNNING)
733c84f3f3cSopenharmony_ci				break;
734c84f3f3cSopenharmony_ci		if (!j) {
735c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
736c84f3f3cSopenharmony_ci			sigprocmask(SIG_SETMASK, &omask, NULL);
737c84f3f3cSopenharmony_ci#endif
738c84f3f3cSopenharmony_ci			return (-1);
739c84f3f3cSopenharmony_ci		}
740c84f3f3cSopenharmony_ci	} else if ((j = j_lookup(cp, &ecode))) {
741c84f3f3cSopenharmony_ci		/* don't report normal job completion */
742c84f3f3cSopenharmony_ci		flags &= ~JW_ASYNCNOTIFY;
743c84f3f3cSopenharmony_ci		if (j->ppid != procpid) {
744c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
745c84f3f3cSopenharmony_ci			sigprocmask(SIG_SETMASK, &omask, NULL);
746c84f3f3cSopenharmony_ci#endif
747c84f3f3cSopenharmony_ci			return (-1);
748c84f3f3cSopenharmony_ci		}
749c84f3f3cSopenharmony_ci	} else {
750c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
751c84f3f3cSopenharmony_ci		sigprocmask(SIG_SETMASK, &omask, NULL);
752c84f3f3cSopenharmony_ci#endif
753c84f3f3cSopenharmony_ci		if (ecode != JL_NOSUCH)
754c84f3f3cSopenharmony_ci			bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
755c84f3f3cSopenharmony_ci		return (-1);
756c84f3f3cSopenharmony_ci	}
757c84f3f3cSopenharmony_ci
758c84f3f3cSopenharmony_ci	/* AT&T ksh will wait for stopped jobs - we don't */
759c84f3f3cSopenharmony_ci	rv = j_waitj(j, flags, "jw:waitfor");
760c84f3f3cSopenharmony_ci
761c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
762c84f3f3cSopenharmony_ci	sigprocmask(SIG_SETMASK, &omask, NULL);
763c84f3f3cSopenharmony_ci#endif
764c84f3f3cSopenharmony_ci
765c84f3f3cSopenharmony_ci	if (rv < 0)
766c84f3f3cSopenharmony_ci		/* we were interrupted */
767c84f3f3cSopenharmony_ci		*sigp = ksh_sigmask(-rv);
768c84f3f3cSopenharmony_ci
769c84f3f3cSopenharmony_ci	return (rv);
770c84f3f3cSopenharmony_ci}
771c84f3f3cSopenharmony_ci
772c84f3f3cSopenharmony_ci/* kill (built-in) a job */
773c84f3f3cSopenharmony_ciint
774c84f3f3cSopenharmony_cij_kill(const char *cp, int sig)
775c84f3f3cSopenharmony_ci{
776c84f3f3cSopenharmony_ci	Job *j;
777c84f3f3cSopenharmony_ci	int rv = 0, ecode;
778c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
779c84f3f3cSopenharmony_ci	sigset_t omask;
780c84f3f3cSopenharmony_ci
781c84f3f3cSopenharmony_ci	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
782c84f3f3cSopenharmony_ci#endif
783c84f3f3cSopenharmony_ci
784c84f3f3cSopenharmony_ci	if ((j = j_lookup(cp, &ecode)) == NULL) {
785c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
786c84f3f3cSopenharmony_ci		sigprocmask(SIG_SETMASK, &omask, NULL);
787c84f3f3cSopenharmony_ci#endif
788c84f3f3cSopenharmony_ci		bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
789c84f3f3cSopenharmony_ci		return (1);
790c84f3f3cSopenharmony_ci	}
791c84f3f3cSopenharmony_ci
792c84f3f3cSopenharmony_ci	if (j->pgrp == 0) {
793c84f3f3cSopenharmony_ci		/* started when !Flag(FMONITOR) */
794c84f3f3cSopenharmony_ci		if (kill_job(j, sig) < 0) {
795c84f3f3cSopenharmony_ci			bi_errorf(Tf_sD_s, cp, cstrerror(errno));
796c84f3f3cSopenharmony_ci			rv = 1;
797c84f3f3cSopenharmony_ci		}
798c84f3f3cSopenharmony_ci	} else {
799c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
800c84f3f3cSopenharmony_ci		if (j->state == PSTOPPED && (sig == SIGTERM || sig == SIGHUP))
801c84f3f3cSopenharmony_ci			mksh_killpg(j->pgrp, SIGCONT);
802c84f3f3cSopenharmony_ci#endif
803c84f3f3cSopenharmony_ci		if (mksh_killpg(j->pgrp, sig) < 0) {
804c84f3f3cSopenharmony_ci			bi_errorf(Tf_sD_s, cp, cstrerror(errno));
805c84f3f3cSopenharmony_ci			rv = 1;
806c84f3f3cSopenharmony_ci		}
807c84f3f3cSopenharmony_ci	}
808c84f3f3cSopenharmony_ci
809c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
810c84f3f3cSopenharmony_ci	sigprocmask(SIG_SETMASK, &omask, NULL);
811c84f3f3cSopenharmony_ci#endif
812c84f3f3cSopenharmony_ci
813c84f3f3cSopenharmony_ci	return (rv);
814c84f3f3cSopenharmony_ci}
815c84f3f3cSopenharmony_ci
816c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
817c84f3f3cSopenharmony_ci/* fg and bg built-ins: called only if Flag(FMONITOR) set */
818c84f3f3cSopenharmony_ciint
819c84f3f3cSopenharmony_cij_resume(const char *cp, int bg)
820c84f3f3cSopenharmony_ci{
821c84f3f3cSopenharmony_ci	Job *j;
822c84f3f3cSopenharmony_ci	Proc *p;
823c84f3f3cSopenharmony_ci	int ecode, rv = 0;
824c84f3f3cSopenharmony_ci	bool running;
825c84f3f3cSopenharmony_ci	sigset_t omask;
826c84f3f3cSopenharmony_ci
827c84f3f3cSopenharmony_ci	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
828c84f3f3cSopenharmony_ci
829c84f3f3cSopenharmony_ci	if ((j = j_lookup(cp, &ecode)) == NULL) {
830c84f3f3cSopenharmony_ci		sigprocmask(SIG_SETMASK, &omask, NULL);
831c84f3f3cSopenharmony_ci		bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
832c84f3f3cSopenharmony_ci		return (1);
833c84f3f3cSopenharmony_ci	}
834c84f3f3cSopenharmony_ci
835c84f3f3cSopenharmony_ci	if (j->pgrp == 0) {
836c84f3f3cSopenharmony_ci		sigprocmask(SIG_SETMASK, &omask, NULL);
837c84f3f3cSopenharmony_ci		bi_errorf("job not job-controlled");
838c84f3f3cSopenharmony_ci		return (1);
839c84f3f3cSopenharmony_ci	}
840c84f3f3cSopenharmony_ci
841c84f3f3cSopenharmony_ci	if (bg)
842c84f3f3cSopenharmony_ci		shprintf("[%d] ", j->job);
843c84f3f3cSopenharmony_ci
844c84f3f3cSopenharmony_ci	running = false;
845c84f3f3cSopenharmony_ci	for (p = j->proc_list; p != NULL; p = p->next) {
846c84f3f3cSopenharmony_ci		if (p->state == PSTOPPED) {
847c84f3f3cSopenharmony_ci			p->state = PRUNNING;
848c84f3f3cSopenharmony_ci			p->status = 0;
849c84f3f3cSopenharmony_ci			running = true;
850c84f3f3cSopenharmony_ci		}
851c84f3f3cSopenharmony_ci		shf_puts(p->command, shl_stdout);
852c84f3f3cSopenharmony_ci		if (p->next)
853c84f3f3cSopenharmony_ci			shf_puts("| ", shl_stdout);
854c84f3f3cSopenharmony_ci	}
855c84f3f3cSopenharmony_ci	shf_putc('\n', shl_stdout);
856c84f3f3cSopenharmony_ci	shf_flush(shl_stdout);
857c84f3f3cSopenharmony_ci	if (running)
858c84f3f3cSopenharmony_ci		j->state = PRUNNING;
859c84f3f3cSopenharmony_ci
860c84f3f3cSopenharmony_ci	put_job(j, PJ_PAST_STOPPED);
861c84f3f3cSopenharmony_ci	if (bg)
862c84f3f3cSopenharmony_ci		j_set_async(j);
863c84f3f3cSopenharmony_ci	else {
864c84f3f3cSopenharmony_ci		/* attach tty to job */
865c84f3f3cSopenharmony_ci		if (j->state == PRUNNING) {
866c84f3f3cSopenharmony_ci			if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))
867c84f3f3cSopenharmony_ci				mksh_tcset(tty_fd, &j->ttystat);
868c84f3f3cSopenharmony_ci			/* See comment in j_waitj regarding saved_ttypgrp. */
869c84f3f3cSopenharmony_ci			if (ttypgrp_ok &&
870c84f3f3cSopenharmony_ci			    tcsetpgrp(tty_fd, (j->flags & JF_SAVEDTTYPGRP) ?
871c84f3f3cSopenharmony_ci			    j->saved_ttypgrp : j->pgrp) < 0) {
872c84f3f3cSopenharmony_ci				rv = errno;
873c84f3f3cSopenharmony_ci				if (j->flags & JF_SAVEDTTY)
874c84f3f3cSopenharmony_ci					mksh_tcset(tty_fd, &tty_state);
875c84f3f3cSopenharmony_ci				sigprocmask(SIG_SETMASK, &omask, NULL);
876c84f3f3cSopenharmony_ci				bi_errorf(Tf_ldfailed,
877c84f3f3cSopenharmony_ci				    "fg: 1st", "tcsetpgrp", tty_fd,
878c84f3f3cSopenharmony_ci				    (long)((j->flags & JF_SAVEDTTYPGRP) ?
879c84f3f3cSopenharmony_ci				    j->saved_ttypgrp : j->pgrp),
880c84f3f3cSopenharmony_ci				    cstrerror(rv));
881c84f3f3cSopenharmony_ci				return (1);
882c84f3f3cSopenharmony_ci			}
883c84f3f3cSopenharmony_ci		}
884c84f3f3cSopenharmony_ci		j->flags |= JF_FG;
885c84f3f3cSopenharmony_ci		j->flags &= ~JF_KNOWN;
886c84f3f3cSopenharmony_ci		if (j == async_job)
887c84f3f3cSopenharmony_ci			async_job = NULL;
888c84f3f3cSopenharmony_ci	}
889c84f3f3cSopenharmony_ci
890c84f3f3cSopenharmony_ci	if (j->state == PRUNNING && mksh_killpg(j->pgrp, SIGCONT) < 0) {
891c84f3f3cSopenharmony_ci		int eno = errno;
892c84f3f3cSopenharmony_ci
893c84f3f3cSopenharmony_ci		if (!bg) {
894c84f3f3cSopenharmony_ci			j->flags &= ~JF_FG;
895c84f3f3cSopenharmony_ci			if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))
896c84f3f3cSopenharmony_ci				mksh_tcset(tty_fd, &tty_state);
897c84f3f3cSopenharmony_ci			if (ttypgrp_ok && tcsetpgrp(tty_fd, kshpgrp) < 0)
898c84f3f3cSopenharmony_ci				warningf(true, Tf_ldfailed,
899c84f3f3cSopenharmony_ci				    "fg: 2nd", "tcsetpgrp", tty_fd,
900c84f3f3cSopenharmony_ci				    (long)kshpgrp, cstrerror(errno));
901c84f3f3cSopenharmony_ci		}
902c84f3f3cSopenharmony_ci		sigprocmask(SIG_SETMASK, &omask, NULL);
903c84f3f3cSopenharmony_ci		bi_errorf(Tf_s_sD_s, "can't continue job",
904c84f3f3cSopenharmony_ci		    cp, cstrerror(eno));
905c84f3f3cSopenharmony_ci		return (1);
906c84f3f3cSopenharmony_ci	}
907c84f3f3cSopenharmony_ci	if (!bg) {
908c84f3f3cSopenharmony_ci		if (ttypgrp_ok) {
909c84f3f3cSopenharmony_ci			j->flags &= ~(JF_SAVEDTTY | JF_SAVEDTTYPGRP);
910c84f3f3cSopenharmony_ci		}
911c84f3f3cSopenharmony_ci		rv = j_waitj(j, JW_NONE, "jw:resume");
912c84f3f3cSopenharmony_ci	}
913c84f3f3cSopenharmony_ci	sigprocmask(SIG_SETMASK, &omask, NULL);
914c84f3f3cSopenharmony_ci	return (rv);
915c84f3f3cSopenharmony_ci}
916c84f3f3cSopenharmony_ci#endif
917c84f3f3cSopenharmony_ci
918c84f3f3cSopenharmony_ci/* are there any running or stopped jobs ? */
919c84f3f3cSopenharmony_ciint
920c84f3f3cSopenharmony_cij_stopped_running(void)
921c84f3f3cSopenharmony_ci{
922c84f3f3cSopenharmony_ci	Job *j;
923c84f3f3cSopenharmony_ci	int which = 0;
924c84f3f3cSopenharmony_ci
925c84f3f3cSopenharmony_ci	for (j = job_list; j != NULL; j = j->next) {
926c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
927c84f3f3cSopenharmony_ci		if (j->ppid == procpid && j->state == PSTOPPED)
928c84f3f3cSopenharmony_ci			which |= 1;
929c84f3f3cSopenharmony_ci#endif
930c84f3f3cSopenharmony_ci		if (Flag(FLOGIN) && !Flag(FNOHUP) && procpid == kshpid &&
931c84f3f3cSopenharmony_ci		    j->ppid == procpid && j->state == PRUNNING)
932c84f3f3cSopenharmony_ci			which |= 2;
933c84f3f3cSopenharmony_ci	}
934c84f3f3cSopenharmony_ci	if (which) {
935c84f3f3cSopenharmony_ci		shellf("You have %s%s%s jobs\n",
936c84f3f3cSopenharmony_ci		    which & 1 ? "stopped" : "",
937c84f3f3cSopenharmony_ci		    which == 3 ? " and " : "",
938c84f3f3cSopenharmony_ci		    which & 2 ? "running" : "");
939c84f3f3cSopenharmony_ci		return (1);
940c84f3f3cSopenharmony_ci	}
941c84f3f3cSopenharmony_ci
942c84f3f3cSopenharmony_ci	return (0);
943c84f3f3cSopenharmony_ci}
944c84f3f3cSopenharmony_ci
945c84f3f3cSopenharmony_ci
946c84f3f3cSopenharmony_ci/* list jobs for jobs built-in */
947c84f3f3cSopenharmony_ciint
948c84f3f3cSopenharmony_cij_jobs(const char *cp, int slp,
949c84f3f3cSopenharmony_ci    /* 0: short, 1: long, 2: pgrp */
950c84f3f3cSopenharmony_ci    int nflag)
951c84f3f3cSopenharmony_ci{
952c84f3f3cSopenharmony_ci	Job *j, *tmp;
953c84f3f3cSopenharmony_ci	int how, zflag = 0;
954c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
955c84f3f3cSopenharmony_ci	sigset_t omask;
956c84f3f3cSopenharmony_ci
957c84f3f3cSopenharmony_ci	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
958c84f3f3cSopenharmony_ci#endif
959c84f3f3cSopenharmony_ci
960c84f3f3cSopenharmony_ci	if (nflag < 0) {
961c84f3f3cSopenharmony_ci		/* kludge: print zombies */
962c84f3f3cSopenharmony_ci		nflag = 0;
963c84f3f3cSopenharmony_ci		zflag = 1;
964c84f3f3cSopenharmony_ci	}
965c84f3f3cSopenharmony_ci	if (cp) {
966c84f3f3cSopenharmony_ci		int ecode;
967c84f3f3cSopenharmony_ci
968c84f3f3cSopenharmony_ci		if ((j = j_lookup(cp, &ecode)) == NULL) {
969c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
970c84f3f3cSopenharmony_ci			sigprocmask(SIG_SETMASK, &omask, NULL);
971c84f3f3cSopenharmony_ci#endif
972c84f3f3cSopenharmony_ci			bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
973c84f3f3cSopenharmony_ci			return (1);
974c84f3f3cSopenharmony_ci		}
975c84f3f3cSopenharmony_ci	} else
976c84f3f3cSopenharmony_ci		j = job_list;
977c84f3f3cSopenharmony_ci	how = slp == 0 ? JP_MEDIUM : (slp == 1 ? JP_LONG : JP_PGRP);
978c84f3f3cSopenharmony_ci	for (; j; j = j->next) {
979c84f3f3cSopenharmony_ci		if ((!(j->flags & JF_ZOMBIE) || zflag) &&
980c84f3f3cSopenharmony_ci		    (!nflag || (j->flags & JF_CHANGED))) {
981c84f3f3cSopenharmony_ci			j_print(j, how, shl_stdout);
982c84f3f3cSopenharmony_ci			if (j->state == PEXITED || j->state == PSIGNALLED)
983c84f3f3cSopenharmony_ci				j->flags |= JF_REMOVE;
984c84f3f3cSopenharmony_ci		}
985c84f3f3cSopenharmony_ci		if (cp)
986c84f3f3cSopenharmony_ci			break;
987c84f3f3cSopenharmony_ci	}
988c84f3f3cSopenharmony_ci	/* Remove jobs after printing so there won't be multiple + or - jobs */
989c84f3f3cSopenharmony_ci	for (j = job_list; j; j = tmp) {
990c84f3f3cSopenharmony_ci		tmp = j->next;
991c84f3f3cSopenharmony_ci		if (j->flags & JF_REMOVE)
992c84f3f3cSopenharmony_ci			remove_job(j, Tjobs);
993c84f3f3cSopenharmony_ci	}
994c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
995c84f3f3cSopenharmony_ci	sigprocmask(SIG_SETMASK, &omask, NULL);
996c84f3f3cSopenharmony_ci#endif
997c84f3f3cSopenharmony_ci	return (0);
998c84f3f3cSopenharmony_ci}
999c84f3f3cSopenharmony_ci
1000c84f3f3cSopenharmony_ci/* list jobs for top-level notification */
1001c84f3f3cSopenharmony_civoid
1002c84f3f3cSopenharmony_cij_notify(void)
1003c84f3f3cSopenharmony_ci{
1004c84f3f3cSopenharmony_ci	Job *j, *tmp;
1005c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
1006c84f3f3cSopenharmony_ci	sigset_t omask;
1007c84f3f3cSopenharmony_ci
1008c84f3f3cSopenharmony_ci	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
1009c84f3f3cSopenharmony_ci#endif
1010c84f3f3cSopenharmony_ci	for (j = job_list; j; j = j->next) {
1011c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
1012c84f3f3cSopenharmony_ci		if (Flag(FMONITOR) && (j->flags & JF_CHANGED))
1013c84f3f3cSopenharmony_ci			j_print(j, JP_MEDIUM, shl_out);
1014c84f3f3cSopenharmony_ci#endif
1015c84f3f3cSopenharmony_ci		/*
1016c84f3f3cSopenharmony_ci		 * Remove job after doing reports so there aren't
1017c84f3f3cSopenharmony_ci		 * multiple +/- jobs.
1018c84f3f3cSopenharmony_ci		 */
1019c84f3f3cSopenharmony_ci		if (j->state == PEXITED || j->state == PSIGNALLED)
1020c84f3f3cSopenharmony_ci			j->flags |= JF_REMOVE;
1021c84f3f3cSopenharmony_ci	}
1022c84f3f3cSopenharmony_ci	for (j = job_list; j; j = tmp) {
1023c84f3f3cSopenharmony_ci		tmp = j->next;
1024c84f3f3cSopenharmony_ci		if (j->flags & JF_REMOVE) {
1025c84f3f3cSopenharmony_ci			if (j == async_job || (j->flags & JF_KNOWN)) {
1026c84f3f3cSopenharmony_ci				j->flags = (j->flags & ~JF_REMOVE) | JF_ZOMBIE;
1027c84f3f3cSopenharmony_ci				j->job = -1;
1028c84f3f3cSopenharmony_ci				nzombie++;
1029c84f3f3cSopenharmony_ci			} else
1030c84f3f3cSopenharmony_ci				remove_job(j, "notify");
1031c84f3f3cSopenharmony_ci		}
1032c84f3f3cSopenharmony_ci	}
1033c84f3f3cSopenharmony_ci	shf_flush(shl_out);
1034c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
1035c84f3f3cSopenharmony_ci	sigprocmask(SIG_SETMASK, &omask, NULL);
1036c84f3f3cSopenharmony_ci#endif
1037c84f3f3cSopenharmony_ci}
1038c84f3f3cSopenharmony_ci
1039c84f3f3cSopenharmony_ci/* Return pid of last process in last asynchronous job */
1040c84f3f3cSopenharmony_cipid_t
1041c84f3f3cSopenharmony_cij_async(void)
1042c84f3f3cSopenharmony_ci{
1043c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
1044c84f3f3cSopenharmony_ci	sigset_t omask;
1045c84f3f3cSopenharmony_ci
1046c84f3f3cSopenharmony_ci	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
1047c84f3f3cSopenharmony_ci#endif
1048c84f3f3cSopenharmony_ci
1049c84f3f3cSopenharmony_ci	if (async_job)
1050c84f3f3cSopenharmony_ci		async_job->flags |= JF_KNOWN;
1051c84f3f3cSopenharmony_ci
1052c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
1053c84f3f3cSopenharmony_ci	sigprocmask(SIG_SETMASK, &omask, NULL);
1054c84f3f3cSopenharmony_ci#endif
1055c84f3f3cSopenharmony_ci
1056c84f3f3cSopenharmony_ci	return (async_pid);
1057c84f3f3cSopenharmony_ci}
1058c84f3f3cSopenharmony_ci
1059c84f3f3cSopenharmony_ci/*
1060c84f3f3cSopenharmony_ci * Make j the last async process
1061c84f3f3cSopenharmony_ci *
1062c84f3f3cSopenharmony_ci * If jobs are compiled in then this routine expects sigchld to be blocked.
1063c84f3f3cSopenharmony_ci */
1064c84f3f3cSopenharmony_cistatic void
1065c84f3f3cSopenharmony_cij_set_async(Job *j)
1066c84f3f3cSopenharmony_ci{
1067c84f3f3cSopenharmony_ci	Job *jl, *oldest;
1068c84f3f3cSopenharmony_ci
1069c84f3f3cSopenharmony_ci	if (async_job && (async_job->flags & (JF_KNOWN|JF_ZOMBIE)) == JF_ZOMBIE)
1070c84f3f3cSopenharmony_ci		remove_job(async_job, "async");
1071c84f3f3cSopenharmony_ci	if (!(j->flags & JF_STARTED)) {
1072c84f3f3cSopenharmony_ci		internal_warningf(Tf_sD_s, "j_async", Tjob_not_started);
1073c84f3f3cSopenharmony_ci		return;
1074c84f3f3cSopenharmony_ci	}
1075c84f3f3cSopenharmony_ci	async_job = j;
1076c84f3f3cSopenharmony_ci	async_pid = j->last_proc->pid;
1077c84f3f3cSopenharmony_ci	while (nzombie > CHILD_MAX) {
1078c84f3f3cSopenharmony_ci		oldest = NULL;
1079c84f3f3cSopenharmony_ci		for (jl = job_list; jl; jl = jl->next)
1080c84f3f3cSopenharmony_ci			if (jl != async_job && (jl->flags & JF_ZOMBIE) &&
1081c84f3f3cSopenharmony_ci			    (!oldest || jl->age < oldest->age))
1082c84f3f3cSopenharmony_ci				oldest = jl;
1083c84f3f3cSopenharmony_ci		if (!oldest) {
1084c84f3f3cSopenharmony_ci			/* XXX debugging */
1085c84f3f3cSopenharmony_ci			if (!(async_job->flags & JF_ZOMBIE) || nzombie != 1) {
1086c84f3f3cSopenharmony_ci				internal_warningf("%s: bad nzombie (%d)",
1087c84f3f3cSopenharmony_ci				    "j_async", nzombie);
1088c84f3f3cSopenharmony_ci				nzombie = 0;
1089c84f3f3cSopenharmony_ci			}
1090c84f3f3cSopenharmony_ci			break;
1091c84f3f3cSopenharmony_ci		}
1092c84f3f3cSopenharmony_ci		remove_job(oldest, "zombie");
1093c84f3f3cSopenharmony_ci	}
1094c84f3f3cSopenharmony_ci}
1095c84f3f3cSopenharmony_ci
1096c84f3f3cSopenharmony_ci/*
1097c84f3f3cSopenharmony_ci * Start a job: set STARTED, check for held signals and set j->last_proc
1098c84f3f3cSopenharmony_ci *
1099c84f3f3cSopenharmony_ci * If jobs are compiled in then this routine expects sigchld to be blocked.
1100c84f3f3cSopenharmony_ci */
1101c84f3f3cSopenharmony_cistatic void
1102c84f3f3cSopenharmony_cij_startjob(Job *j)
1103c84f3f3cSopenharmony_ci{
1104c84f3f3cSopenharmony_ci	Proc *p;
1105c84f3f3cSopenharmony_ci
1106c84f3f3cSopenharmony_ci	j->flags |= JF_STARTED;
1107c84f3f3cSopenharmony_ci	for (p = j->proc_list; p->next; p = p->next)
1108c84f3f3cSopenharmony_ci		;
1109c84f3f3cSopenharmony_ci	j->last_proc = p;
1110c84f3f3cSopenharmony_ci
1111c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
1112c84f3f3cSopenharmony_ci	if (held_sigchld) {
1113c84f3f3cSopenharmony_ci		held_sigchld = 0;
1114c84f3f3cSopenharmony_ci		/* Don't call j_sigchld() as it may remove job... */
1115c84f3f3cSopenharmony_ci		kill(procpid, SIGCHLD);
1116c84f3f3cSopenharmony_ci	}
1117c84f3f3cSopenharmony_ci#endif
1118c84f3f3cSopenharmony_ci}
1119c84f3f3cSopenharmony_ci
1120c84f3f3cSopenharmony_ci/*
1121c84f3f3cSopenharmony_ci * wait for job to complete or change state
1122c84f3f3cSopenharmony_ci *
1123c84f3f3cSopenharmony_ci * If jobs are compiled in then this routine expects sigchld to be blocked.
1124c84f3f3cSopenharmony_ci */
1125c84f3f3cSopenharmony_cistatic int
1126c84f3f3cSopenharmony_cij_waitj(Job *j,
1127c84f3f3cSopenharmony_ci    /* see JW_* */
1128c84f3f3cSopenharmony_ci    int flags,
1129c84f3f3cSopenharmony_ci    const char *where)
1130c84f3f3cSopenharmony_ci{
1131c84f3f3cSopenharmony_ci	Proc *p;
1132c84f3f3cSopenharmony_ci	int rv;
1133c84f3f3cSopenharmony_ci#ifdef MKSH_NO_SIGSUSPEND
1134c84f3f3cSopenharmony_ci	sigset_t omask;
1135c84f3f3cSopenharmony_ci#endif
1136c84f3f3cSopenharmony_ci
1137c84f3f3cSopenharmony_ci	/*
1138c84f3f3cSopenharmony_ci	 * No auto-notify on the job we are waiting on.
1139c84f3f3cSopenharmony_ci	 */
1140c84f3f3cSopenharmony_ci	j->flags |= JF_WAITING;
1141c84f3f3cSopenharmony_ci	if (flags & JW_ASYNCNOTIFY)
1142c84f3f3cSopenharmony_ci		j->flags |= JF_W_ASYNCNOTIFY;
1143c84f3f3cSopenharmony_ci
1144c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
1145c84f3f3cSopenharmony_ci	if (!Flag(FMONITOR))
1146c84f3f3cSopenharmony_ci#endif
1147c84f3f3cSopenharmony_ci		flags |= JW_STOPPEDWAIT;
1148c84f3f3cSopenharmony_ci
1149c84f3f3cSopenharmony_ci	while (j->state == PRUNNING ||
1150c84f3f3cSopenharmony_ci	    ((flags & JW_STOPPEDWAIT) && j->state == PSTOPPED)) {
1151c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
1152c84f3f3cSopenharmony_ci#ifdef MKSH_NO_SIGSUSPEND
1153c84f3f3cSopenharmony_ci		sigprocmask(SIG_SETMASK, &sm_default, &omask);
1154c84f3f3cSopenharmony_ci		pause();
1155c84f3f3cSopenharmony_ci		/* note that handlers may run here so they need to know */
1156c84f3f3cSopenharmony_ci		sigprocmask(SIG_SETMASK, &omask, NULL);
1157c84f3f3cSopenharmony_ci#else
1158c84f3f3cSopenharmony_ci		sigsuspend(&sm_default);
1159c84f3f3cSopenharmony_ci#endif
1160c84f3f3cSopenharmony_ci#else
1161c84f3f3cSopenharmony_ci		j_sigchld(SIGCHLD);
1162c84f3f3cSopenharmony_ci#endif
1163c84f3f3cSopenharmony_ci		if (fatal_trap) {
1164c84f3f3cSopenharmony_ci			int oldf = j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY);
1165c84f3f3cSopenharmony_ci			j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
1166c84f3f3cSopenharmony_ci			runtraps(TF_FATAL);
1167c84f3f3cSopenharmony_ci			/* not reached... */
1168c84f3f3cSopenharmony_ci			j->flags |= oldf;
1169c84f3f3cSopenharmony_ci		}
1170c84f3f3cSopenharmony_ci		if ((flags & JW_INTERRUPT) && (rv = trap_pending())) {
1171c84f3f3cSopenharmony_ci			j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
1172c84f3f3cSopenharmony_ci			return (-rv);
1173c84f3f3cSopenharmony_ci		}
1174c84f3f3cSopenharmony_ci	}
1175c84f3f3cSopenharmony_ci	j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
1176c84f3f3cSopenharmony_ci
1177c84f3f3cSopenharmony_ci	if (j->flags & JF_FG) {
1178c84f3f3cSopenharmony_ci		j->flags &= ~JF_FG;
1179c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
1180c84f3f3cSopenharmony_ci		if (Flag(FMONITOR) && ttypgrp_ok && j->pgrp) {
1181c84f3f3cSopenharmony_ci			/*
1182c84f3f3cSopenharmony_ci			 * Save the tty's current pgrp so it can be restored
1183c84f3f3cSopenharmony_ci			 * when the job is foregrounded. This is to
1184c84f3f3cSopenharmony_ci			 * deal with things like the GNU su which does
1185c84f3f3cSopenharmony_ci			 * a fork/exec instead of an exec (the fork means
1186c84f3f3cSopenharmony_ci			 * the execed shell gets a different pid from its
1187c84f3f3cSopenharmony_ci			 * pgrp, so naturally it sets its pgrp and gets hosed
1188c84f3f3cSopenharmony_ci			 * when it gets foregrounded by the parent shell which
1189c84f3f3cSopenharmony_ci			 * has restored the tty's pgrp to that of the su
1190c84f3f3cSopenharmony_ci			 * process).
1191c84f3f3cSopenharmony_ci			 */
1192c84f3f3cSopenharmony_ci			if (j->state == PSTOPPED &&
1193c84f3f3cSopenharmony_ci			    (j->saved_ttypgrp = tcgetpgrp(tty_fd)) >= 0)
1194c84f3f3cSopenharmony_ci				j->flags |= JF_SAVEDTTYPGRP;
1195c84f3f3cSopenharmony_ci			if (tcsetpgrp(tty_fd, kshpgrp) < 0)
1196c84f3f3cSopenharmony_ci				warningf(true, Tf_ldfailed,
1197c84f3f3cSopenharmony_ci				    "j_waitj:", "tcsetpgrp", tty_fd,
1198c84f3f3cSopenharmony_ci				    (long)kshpgrp, cstrerror(errno));
1199c84f3f3cSopenharmony_ci			if (j->state == PSTOPPED) {
1200c84f3f3cSopenharmony_ci				j->flags |= JF_SAVEDTTY;
1201c84f3f3cSopenharmony_ci				mksh_tcget(tty_fd, &j->ttystat);
1202c84f3f3cSopenharmony_ci			}
1203c84f3f3cSopenharmony_ci		}
1204c84f3f3cSopenharmony_ci#endif
1205c84f3f3cSopenharmony_ci		if (tty_hasstate) {
1206c84f3f3cSopenharmony_ci			/*
1207c84f3f3cSopenharmony_ci			 * Only restore tty settings if job was originally
1208c84f3f3cSopenharmony_ci			 * started in the foreground. Problems can be
1209c84f3f3cSopenharmony_ci			 * caused by things like 'more foobar &' which will
1210c84f3f3cSopenharmony_ci			 * typically get and save the shell's vi/emacs tty
1211c84f3f3cSopenharmony_ci			 * settings before setting up the tty for itself;
1212c84f3f3cSopenharmony_ci			 * when more exits, it restores the 'original'
1213c84f3f3cSopenharmony_ci			 * settings, and things go down hill from there...
1214c84f3f3cSopenharmony_ci			 */
1215c84f3f3cSopenharmony_ci			if (j->state == PEXITED && j->status == 0 &&
1216c84f3f3cSopenharmony_ci			    (j->flags & JF_USETTYMODE)) {
1217c84f3f3cSopenharmony_ci				mksh_tcget(tty_fd, &tty_state);
1218c84f3f3cSopenharmony_ci			} else {
1219c84f3f3cSopenharmony_ci				mksh_tcset(tty_fd, &tty_state);
1220c84f3f3cSopenharmony_ci				/*-
1221c84f3f3cSopenharmony_ci				 * Don't use tty mode if job is stopped and
1222c84f3f3cSopenharmony_ci				 * later restarted and exits. Consider
1223c84f3f3cSopenharmony_ci				 * the sequence:
1224c84f3f3cSopenharmony_ci				 *	vi foo (stopped)
1225c84f3f3cSopenharmony_ci				 *	...
1226c84f3f3cSopenharmony_ci				 *	stty something
1227c84f3f3cSopenharmony_ci				 *	...
1228c84f3f3cSopenharmony_ci				 *	fg (vi; ZZ)
1229c84f3f3cSopenharmony_ci				 * mode should be that of the stty, not what
1230c84f3f3cSopenharmony_ci				 * was before the vi started.
1231c84f3f3cSopenharmony_ci				 */
1232c84f3f3cSopenharmony_ci				if (j->state == PSTOPPED)
1233c84f3f3cSopenharmony_ci					j->flags &= ~JF_USETTYMODE;
1234c84f3f3cSopenharmony_ci			}
1235c84f3f3cSopenharmony_ci		}
1236c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
1237c84f3f3cSopenharmony_ci		/*
1238c84f3f3cSopenharmony_ci		 * If it looks like user hit ^C to kill a job, pretend we got
1239c84f3f3cSopenharmony_ci		 * one too to break out of for loops, etc. (AT&T ksh does this
1240c84f3f3cSopenharmony_ci		 * even when not monitoring, but this doesn't make sense since
1241c84f3f3cSopenharmony_ci		 * a tty generated ^C goes to the whole process group)
1242c84f3f3cSopenharmony_ci		 */
1243c84f3f3cSopenharmony_ci		if (Flag(FMONITOR) && j->state == PSIGNALLED &&
1244c84f3f3cSopenharmony_ci		    WIFSIGNALED(j->last_proc->status)) {
1245c84f3f3cSopenharmony_ci			int termsig;
1246c84f3f3cSopenharmony_ci
1247c84f3f3cSopenharmony_ci			if ((termsig = WTERMSIG(j->last_proc->status)) > 0 &&
1248c84f3f3cSopenharmony_ci			    termsig < ksh_NSIG &&
1249c84f3f3cSopenharmony_ci			    (sigtraps[termsig].flags & TF_TTY_INTR))
1250c84f3f3cSopenharmony_ci				trapsig(termsig);
1251c84f3f3cSopenharmony_ci		}
1252c84f3f3cSopenharmony_ci#endif
1253c84f3f3cSopenharmony_ci	}
1254c84f3f3cSopenharmony_ci
1255c84f3f3cSopenharmony_ci	j_usrtime = j->usrtime;
1256c84f3f3cSopenharmony_ci	j_systime = j->systime;
1257c84f3f3cSopenharmony_ci	rv = j->status;
1258c84f3f3cSopenharmony_ci
1259c84f3f3cSopenharmony_ci	if (!(p = j->proc_list)) {
1260c84f3f3cSopenharmony_ci		;	/* nothing */
1261c84f3f3cSopenharmony_ci	} else if (flags & JW_PIPEST) {
1262c84f3f3cSopenharmony_ci		uint32_t num = 0;
1263c84f3f3cSopenharmony_ci		struct tbl *vp;
1264c84f3f3cSopenharmony_ci
1265c84f3f3cSopenharmony_ci		unset(vp_pipest, 1);
1266c84f3f3cSopenharmony_ci		vp = vp_pipest;
1267c84f3f3cSopenharmony_ci		vp->flag = DEFINED | ISSET | INTEGER | RDONLY | ARRAY | INT_U;
1268c84f3f3cSopenharmony_ci		goto got_array;
1269c84f3f3cSopenharmony_ci
1270c84f3f3cSopenharmony_ci		while (p != NULL) {
1271c84f3f3cSopenharmony_ci			{
1272c84f3f3cSopenharmony_ci				struct tbl *vq;
1273c84f3f3cSopenharmony_ci
1274c84f3f3cSopenharmony_ci				/* strlen(vp_pipest->name) == 10 */
1275c84f3f3cSopenharmony_ci				vq = alloc(offsetof(struct tbl, name[0]) + 11,
1276c84f3f3cSopenharmony_ci				    vp_pipest->areap);
1277c84f3f3cSopenharmony_ci				memset(vq, 0, offsetof(struct tbl, name[0]));
1278c84f3f3cSopenharmony_ci				memcpy(vq->name, vp_pipest->name, 11);
1279c84f3f3cSopenharmony_ci				vp->u.array = vq;
1280c84f3f3cSopenharmony_ci				vp = vq;
1281c84f3f3cSopenharmony_ci			}
1282c84f3f3cSopenharmony_ci			vp->areap = vp_pipest->areap;
1283c84f3f3cSopenharmony_ci			vp->ua.index = ++num;
1284c84f3f3cSopenharmony_ci			vp->flag = DEFINED | ISSET | INTEGER | RDONLY |
1285c84f3f3cSopenharmony_ci			    ARRAY | INT_U | AINDEX;
1286c84f3f3cSopenharmony_ci got_array:
1287c84f3f3cSopenharmony_ci			vp->val.i = proc_errorlevel(p);
1288c84f3f3cSopenharmony_ci			if (Flag(FPIPEFAIL) && vp->val.i)
1289c84f3f3cSopenharmony_ci				rv = vp->val.i;
1290c84f3f3cSopenharmony_ci			p = p->next;
1291c84f3f3cSopenharmony_ci		}
1292c84f3f3cSopenharmony_ci	} else if (Flag(FPIPEFAIL)) {
1293c84f3f3cSopenharmony_ci		do {
1294c84f3f3cSopenharmony_ci			const int i = proc_errorlevel(p);
1295c84f3f3cSopenharmony_ci
1296c84f3f3cSopenharmony_ci			if (i)
1297c84f3f3cSopenharmony_ci				rv = i;
1298c84f3f3cSopenharmony_ci		} while ((p = p->next));
1299c84f3f3cSopenharmony_ci	}
1300c84f3f3cSopenharmony_ci
1301c84f3f3cSopenharmony_ci	if (!(flags & JW_ASYNCNOTIFY)
1302c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
1303c84f3f3cSopenharmony_ci	    && (!Flag(FMONITOR) || j->state != PSTOPPED)
1304c84f3f3cSopenharmony_ci#endif
1305c84f3f3cSopenharmony_ci	    ) {
1306c84f3f3cSopenharmony_ci		j_print(j, JP_SHORT, shl_out);
1307c84f3f3cSopenharmony_ci		shf_flush(shl_out);
1308c84f3f3cSopenharmony_ci	}
1309c84f3f3cSopenharmony_ci	if (j->state != PSTOPPED
1310c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
1311c84f3f3cSopenharmony_ci	    && (!Flag(FMONITOR) || !(flags & JW_ASYNCNOTIFY))
1312c84f3f3cSopenharmony_ci#endif
1313c84f3f3cSopenharmony_ci	    )
1314c84f3f3cSopenharmony_ci		remove_job(j, where);
1315c84f3f3cSopenharmony_ci
1316c84f3f3cSopenharmony_ci	return (rv);
1317c84f3f3cSopenharmony_ci}
1318c84f3f3cSopenharmony_ci
1319c84f3f3cSopenharmony_ci/*
1320c84f3f3cSopenharmony_ci * SIGCHLD handler to reap children and update job states
1321c84f3f3cSopenharmony_ci *
1322c84f3f3cSopenharmony_ci * If jobs are compiled in then this routine expects sigchld to be blocked.
1323c84f3f3cSopenharmony_ci */
1324c84f3f3cSopenharmony_ci/* ARGSUSED */
1325c84f3f3cSopenharmony_cistatic void
1326c84f3f3cSopenharmony_cij_sigchld(int sig MKSH_A_UNUSED)
1327c84f3f3cSopenharmony_ci{
1328c84f3f3cSopenharmony_ci	int saved_errno = errno;
1329c84f3f3cSopenharmony_ci	Job *j;
1330c84f3f3cSopenharmony_ci	Proc *p = NULL;
1331c84f3f3cSopenharmony_ci	pid_t pid;
1332c84f3f3cSopenharmony_ci	int status;
1333c84f3f3cSopenharmony_ci	struct rusage ru0, ru1;
1334c84f3f3cSopenharmony_ci#ifdef MKSH_NO_SIGSUSPEND
1335c84f3f3cSopenharmony_ci	sigset_t omask;
1336c84f3f3cSopenharmony_ci
1337c84f3f3cSopenharmony_ci	/* this handler can run while SIGCHLD is not blocked, so block it now */
1338c84f3f3cSopenharmony_ci	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
1339c84f3f3cSopenharmony_ci#endif
1340c84f3f3cSopenharmony_ci
1341c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
1342c84f3f3cSopenharmony_ci	/*
1343c84f3f3cSopenharmony_ci	 * Don't wait for any processes if a job is partially started.
1344c84f3f3cSopenharmony_ci	 * This is so we don't do away with the process group leader
1345c84f3f3cSopenharmony_ci	 * before all the processes in a pipe line are started (so the
1346c84f3f3cSopenharmony_ci	 * setpgid() won't fail)
1347c84f3f3cSopenharmony_ci	 */
1348c84f3f3cSopenharmony_ci	for (j = job_list; j; j = j->next)
1349c84f3f3cSopenharmony_ci		if (j->ppid == procpid && !(j->flags & JF_STARTED)) {
1350c84f3f3cSopenharmony_ci			held_sigchld = 1;
1351c84f3f3cSopenharmony_ci			goto j_sigchld_out;
1352c84f3f3cSopenharmony_ci		}
1353c84f3f3cSopenharmony_ci#endif
1354c84f3f3cSopenharmony_ci
1355c84f3f3cSopenharmony_ci	getrusage(RUSAGE_CHILDREN, &ru0);
1356c84f3f3cSopenharmony_ci	do {
1357c84f3f3cSopenharmony_ci#ifdef ADAPT_FOR_LITEOS_A
1358c84f3f3cSopenharmony_ci		pid = waitpid(-1, &status, WNOHANG);
1359c84f3f3cSopenharmony_ci#else // ADAPT_FOR_LITEOS_A
1360c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
1361c84f3f3cSopenharmony_ci		pid = waitpid(-1, &status, (WNOHANG |
1362c84f3f3cSopenharmony_ci#if defined(WCONTINUED) && defined(WIFCONTINUED)
1363c84f3f3cSopenharmony_ci		    WCONTINUED |
1364c84f3f3cSopenharmony_ci#endif
1365c84f3f3cSopenharmony_ci		    WUNTRACED));
1366c84f3f3cSopenharmony_ci#else
1367c84f3f3cSopenharmony_ci		pid = wait(&status);
1368c84f3f3cSopenharmony_ci#endif
1369c84f3f3cSopenharmony_ci#endif // ADAPT_FOR_LITEOS_A
1370c84f3f3cSopenharmony_ci
1371c84f3f3cSopenharmony_ci		/*
1372c84f3f3cSopenharmony_ci		 * return if this would block (0) or no children
1373c84f3f3cSopenharmony_ci		 * or interrupted (-1)
1374c84f3f3cSopenharmony_ci		 */
1375c84f3f3cSopenharmony_ci		if (pid <= 0)
1376c84f3f3cSopenharmony_ci			goto j_sigchld_out;
1377c84f3f3cSopenharmony_ci
1378c84f3f3cSopenharmony_ci		getrusage(RUSAGE_CHILDREN, &ru1);
1379c84f3f3cSopenharmony_ci
1380c84f3f3cSopenharmony_ci		/* find job and process structures for this pid */
1381c84f3f3cSopenharmony_ci		for (j = job_list; j != NULL; j = j->next)
1382c84f3f3cSopenharmony_ci			for (p = j->proc_list; p != NULL; p = p->next)
1383c84f3f3cSopenharmony_ci				if (p->pid == pid)
1384c84f3f3cSopenharmony_ci					goto found;
1385c84f3f3cSopenharmony_ci found:
1386c84f3f3cSopenharmony_ci		if (j == NULL) {
1387c84f3f3cSopenharmony_ci			/* Can occur if process has kids, then execs shell
1388c84f3f3cSopenharmony_ci			warningf(true, "bad process waited for (pid = %d)",
1389c84f3f3cSopenharmony_ci				pid);
1390c84f3f3cSopenharmony_ci			 */
1391c84f3f3cSopenharmony_ci			ru0 = ru1;
1392c84f3f3cSopenharmony_ci			continue;
1393c84f3f3cSopenharmony_ci		}
1394c84f3f3cSopenharmony_ci
1395c84f3f3cSopenharmony_ci		timeradd(&j->usrtime, &ru1.ru_utime, &j->usrtime);
1396c84f3f3cSopenharmony_ci		timersub(&j->usrtime, &ru0.ru_utime, &j->usrtime);
1397c84f3f3cSopenharmony_ci		timeradd(&j->systime, &ru1.ru_stime, &j->systime);
1398c84f3f3cSopenharmony_ci		timersub(&j->systime, &ru0.ru_stime, &j->systime);
1399c84f3f3cSopenharmony_ci		ru0 = ru1;
1400c84f3f3cSopenharmony_ci		p->status = status;
1401c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
1402c84f3f3cSopenharmony_ci		if (WIFSTOPPED(status))
1403c84f3f3cSopenharmony_ci			p->state = PSTOPPED;
1404c84f3f3cSopenharmony_ci		else
1405c84f3f3cSopenharmony_ci#if defined(WCONTINUED) && defined(WIFCONTINUED)
1406c84f3f3cSopenharmony_ci		  if (WIFCONTINUED(status)) {
1407c84f3f3cSopenharmony_ci			p->state = j->state = PRUNNING;
1408c84f3f3cSopenharmony_ci			/* skip check_job(), no-op in this case */
1409c84f3f3cSopenharmony_ci			continue;
1410c84f3f3cSopenharmony_ci		} else
1411c84f3f3cSopenharmony_ci#endif
1412c84f3f3cSopenharmony_ci#endif
1413c84f3f3cSopenharmony_ci		  if (WIFSIGNALED(status))
1414c84f3f3cSopenharmony_ci			p->state = PSIGNALLED;
1415c84f3f3cSopenharmony_ci		else
1416c84f3f3cSopenharmony_ci			p->state = PEXITED;
1417c84f3f3cSopenharmony_ci
1418c84f3f3cSopenharmony_ci		/* check to see if entire job is done */
1419c84f3f3cSopenharmony_ci		check_job(j);
1420c84f3f3cSopenharmony_ci	}
1421c84f3f3cSopenharmony_ci#ifndef MKSH_NOPROSPECTOFWORK
1422c84f3f3cSopenharmony_ci	    while (/* CONSTCOND */ 1);
1423c84f3f3cSopenharmony_ci#else
1424c84f3f3cSopenharmony_ci	    while (/* CONSTCOND */ 0);
1425c84f3f3cSopenharmony_ci#endif
1426c84f3f3cSopenharmony_ci
1427c84f3f3cSopenharmony_ci j_sigchld_out:
1428c84f3f3cSopenharmony_ci#ifdef MKSH_NO_SIGSUSPEND
1429c84f3f3cSopenharmony_ci	sigprocmask(SIG_SETMASK, &omask, NULL);
1430c84f3f3cSopenharmony_ci#endif
1431c84f3f3cSopenharmony_ci	errno = saved_errno;
1432c84f3f3cSopenharmony_ci}
1433c84f3f3cSopenharmony_ci
1434c84f3f3cSopenharmony_ci/*
1435c84f3f3cSopenharmony_ci * Called only when a process in j has exited/stopped (ie, called only
1436c84f3f3cSopenharmony_ci * from j_sigchld()). If no processes are running, the job status
1437c84f3f3cSopenharmony_ci * and state are updated, asynchronous job notification is done and,
1438c84f3f3cSopenharmony_ci * if unneeded, the job is removed.
1439c84f3f3cSopenharmony_ci *
1440c84f3f3cSopenharmony_ci * If jobs are compiled in then this routine expects sigchld to be blocked.
1441c84f3f3cSopenharmony_ci */
1442c84f3f3cSopenharmony_cistatic void
1443c84f3f3cSopenharmony_cicheck_job(Job *j)
1444c84f3f3cSopenharmony_ci{
1445c84f3f3cSopenharmony_ci	int jstate;
1446c84f3f3cSopenharmony_ci	Proc *p;
1447c84f3f3cSopenharmony_ci
1448c84f3f3cSopenharmony_ci	/* XXX debugging (nasty - interrupt routine using shl_out) */
1449c84f3f3cSopenharmony_ci	if (!(j->flags & JF_STARTED)) {
1450c84f3f3cSopenharmony_ci		internal_warningf("check_job: job started (flags 0x%X)",
1451c84f3f3cSopenharmony_ci		    (unsigned int)j->flags);
1452c84f3f3cSopenharmony_ci		return;
1453c84f3f3cSopenharmony_ci	}
1454c84f3f3cSopenharmony_ci
1455c84f3f3cSopenharmony_ci	jstate = PRUNNING;
1456c84f3f3cSopenharmony_ci	for (p=j->proc_list; p != NULL; p = p->next) {
1457c84f3f3cSopenharmony_ci		if (p->state == PRUNNING)
1458c84f3f3cSopenharmony_ci			/* some processes still running */
1459c84f3f3cSopenharmony_ci			return;
1460c84f3f3cSopenharmony_ci		if (p->state > jstate)
1461c84f3f3cSopenharmony_ci			jstate = p->state;
1462c84f3f3cSopenharmony_ci	}
1463c84f3f3cSopenharmony_ci	j->state = jstate;
1464c84f3f3cSopenharmony_ci	j->status = proc_errorlevel(j->last_proc);
1465c84f3f3cSopenharmony_ci
1466c84f3f3cSopenharmony_ci	/*
1467c84f3f3cSopenharmony_ci	 * Note when co-process dies: can't be done in j_wait() nor
1468c84f3f3cSopenharmony_ci	 * remove_job() since neither may be called for non-interactive
1469c84f3f3cSopenharmony_ci	 * shells.
1470c84f3f3cSopenharmony_ci	 */
1471c84f3f3cSopenharmony_ci	if (j->state == PEXITED || j->state == PSIGNALLED) {
1472c84f3f3cSopenharmony_ci		/*
1473c84f3f3cSopenharmony_ci		 * No need to keep co-process input any more
1474c84f3f3cSopenharmony_ci		 * (at least, this is what ksh93d thinks)
1475c84f3f3cSopenharmony_ci		 */
1476c84f3f3cSopenharmony_ci		if (coproc.job == j) {
1477c84f3f3cSopenharmony_ci			coproc.job = NULL;
1478c84f3f3cSopenharmony_ci			/*
1479c84f3f3cSopenharmony_ci			 * XXX would be nice to get the closes out of here
1480c84f3f3cSopenharmony_ci			 * so they aren't done in the signal handler.
1481c84f3f3cSopenharmony_ci			 * Would mean a check in coproc_getfd() to
1482c84f3f3cSopenharmony_ci			 * do "if job == 0 && write >= 0, close write".
1483c84f3f3cSopenharmony_ci			 */
1484c84f3f3cSopenharmony_ci			coproc_write_close(coproc.write);
1485c84f3f3cSopenharmony_ci		}
1486c84f3f3cSopenharmony_ci		/* Do we need to keep the output? */
1487c84f3f3cSopenharmony_ci		if (j->coproc_id && j->coproc_id == coproc.id &&
1488c84f3f3cSopenharmony_ci		    --coproc.njobs == 0)
1489c84f3f3cSopenharmony_ci			coproc_readw_close(coproc.read);
1490c84f3f3cSopenharmony_ci	}
1491c84f3f3cSopenharmony_ci
1492c84f3f3cSopenharmony_ci	j->flags |= JF_CHANGED;
1493c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
1494c84f3f3cSopenharmony_ci	if (Flag(FMONITOR) && !(j->flags & JF_XXCOM)) {
1495c84f3f3cSopenharmony_ci		/*
1496c84f3f3cSopenharmony_ci		 * Only put stopped jobs at the front to avoid confusing
1497c84f3f3cSopenharmony_ci		 * the user (don't want finished jobs effecting %+ or %-)
1498c84f3f3cSopenharmony_ci		 */
1499c84f3f3cSopenharmony_ci		if (j->state == PSTOPPED)
1500c84f3f3cSopenharmony_ci			put_job(j, PJ_ON_FRONT);
1501c84f3f3cSopenharmony_ci		if (Flag(FNOTIFY) &&
1502c84f3f3cSopenharmony_ci		    (j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY)) != JF_WAITING) {
1503c84f3f3cSopenharmony_ci			/* Look for the real file descriptor 2 */
1504c84f3f3cSopenharmony_ci			{
1505c84f3f3cSopenharmony_ci				struct env *ep;
1506c84f3f3cSopenharmony_ci				int fd = 2;
1507c84f3f3cSopenharmony_ci
1508c84f3f3cSopenharmony_ci				for (ep = e; ep; ep = ep->oenv)
1509c84f3f3cSopenharmony_ci					if (ep->savefd && ep->savefd[2])
1510c84f3f3cSopenharmony_ci						fd = ep->savefd[2];
1511c84f3f3cSopenharmony_ci				shf_reopen(fd, SHF_WR, shl_j);
1512c84f3f3cSopenharmony_ci			}
1513c84f3f3cSopenharmony_ci			/*
1514c84f3f3cSopenharmony_ci			 * Can't call j_notify() as it removes jobs. The job
1515c84f3f3cSopenharmony_ci			 * must stay in the job list as j_waitj() may be
1516c84f3f3cSopenharmony_ci			 * running with this job.
1517c84f3f3cSopenharmony_ci			 */
1518c84f3f3cSopenharmony_ci			j_print(j, JP_MEDIUM, shl_j);
1519c84f3f3cSopenharmony_ci			shf_flush(shl_j);
1520c84f3f3cSopenharmony_ci			if (!(j->flags & JF_WAITING) && j->state != PSTOPPED)
1521c84f3f3cSopenharmony_ci				remove_job(j, "notify");
1522c84f3f3cSopenharmony_ci		}
1523c84f3f3cSopenharmony_ci	}
1524c84f3f3cSopenharmony_ci#endif
1525c84f3f3cSopenharmony_ci	if (
1526c84f3f3cSopenharmony_ci#ifndef MKSH_UNEMPLOYED
1527c84f3f3cSopenharmony_ci	    !Flag(FMONITOR) &&
1528c84f3f3cSopenharmony_ci#endif
1529c84f3f3cSopenharmony_ci	    !(j->flags & (JF_WAITING|JF_FG)) &&
1530c84f3f3cSopenharmony_ci	    j->state != PSTOPPED) {
1531c84f3f3cSopenharmony_ci		if (j == async_job || (j->flags & JF_KNOWN)) {
1532c84f3f3cSopenharmony_ci			j->flags |= JF_ZOMBIE;
1533c84f3f3cSopenharmony_ci			j->job = -1;
1534c84f3f3cSopenharmony_ci			nzombie++;
1535c84f3f3cSopenharmony_ci		} else
1536c84f3f3cSopenharmony_ci			remove_job(j, "checkjob");
1537c84f3f3cSopenharmony_ci	}
1538c84f3f3cSopenharmony_ci}
1539c84f3f3cSopenharmony_ci
1540c84f3f3cSopenharmony_ci/*
1541c84f3f3cSopenharmony_ci * Print job status in either short, medium or long format.
1542c84f3f3cSopenharmony_ci *
1543c84f3f3cSopenharmony_ci * If jobs are compiled in then this routine expects sigchld to be blocked.
1544c84f3f3cSopenharmony_ci */
1545c84f3f3cSopenharmony_cistatic void
1546c84f3f3cSopenharmony_cij_print(Job *j, int how, struct shf *shf)
1547c84f3f3cSopenharmony_ci{
1548c84f3f3cSopenharmony_ci	Proc *p;
1549c84f3f3cSopenharmony_ci	int state;
1550c84f3f3cSopenharmony_ci	int status;
1551c84f3f3cSopenharmony_ci#ifdef WCOREDUMP
1552c84f3f3cSopenharmony_ci	bool coredumped;
1553c84f3f3cSopenharmony_ci#endif
1554c84f3f3cSopenharmony_ci	char jobchar = ' ';
1555c84f3f3cSopenharmony_ci	char buf[64];
1556c84f3f3cSopenharmony_ci	const char *filler;
1557c84f3f3cSopenharmony_ci	int output = 0;
1558c84f3f3cSopenharmony_ci
1559c84f3f3cSopenharmony_ci	if (how == JP_PGRP) {
1560c84f3f3cSopenharmony_ci		/*
1561c84f3f3cSopenharmony_ci		 * POSIX doesn't say what to do it there is no process
1562c84f3f3cSopenharmony_ci		 * group leader (ie, !FMONITOR). We arbitrarily return
1563c84f3f3cSopenharmony_ci		 * last pid (which is what $! returns).
1564c84f3f3cSopenharmony_ci		 */
1565c84f3f3cSopenharmony_ci		shf_fprintf(shf, Tf_dN, (int)(j->pgrp ? j->pgrp :
1566c84f3f3cSopenharmony_ci		    (j->last_proc ? j->last_proc->pid : 0)));
1567c84f3f3cSopenharmony_ci		return;
1568c84f3f3cSopenharmony_ci	}
1569c84f3f3cSopenharmony_ci	j->flags &= ~JF_CHANGED;
1570c84f3f3cSopenharmony_ci	filler = j->job > 10 ? "\n       " : "\n      ";
1571c84f3f3cSopenharmony_ci	if (j == job_list)
1572c84f3f3cSopenharmony_ci		jobchar = '+';
1573c84f3f3cSopenharmony_ci	else if (j == job_list->next)
1574c84f3f3cSopenharmony_ci		jobchar = '-';
1575c84f3f3cSopenharmony_ci
1576c84f3f3cSopenharmony_ci	for (p = j->proc_list; p != NULL;) {
1577c84f3f3cSopenharmony_ci#ifdef WCOREDUMP
1578c84f3f3cSopenharmony_ci		coredumped = false;
1579c84f3f3cSopenharmony_ci#endif
1580c84f3f3cSopenharmony_ci		switch (p->state) {
1581c84f3f3cSopenharmony_ci		case PRUNNING:
1582c84f3f3cSopenharmony_ci			memcpy(buf, "Running", 8);
1583c84f3f3cSopenharmony_ci			break;
1584c84f3f3cSopenharmony_ci		case PSTOPPED: {
1585c84f3f3cSopenharmony_ci			int stopsig = WSTOPSIG(p->status);
1586c84f3f3cSopenharmony_ci
1587c84f3f3cSopenharmony_ci			strlcpy(buf, stopsig > 0 && stopsig < ksh_NSIG ?
1588c84f3f3cSopenharmony_ci			    sigtraps[stopsig].mess : "Stopped", sizeof(buf));
1589c84f3f3cSopenharmony_ci			break;
1590c84f3f3cSopenharmony_ci		}
1591c84f3f3cSopenharmony_ci		case PEXITED: {
1592c84f3f3cSopenharmony_ci			int exitstatus = (WEXITSTATUS(p->status)) & 255;
1593c84f3f3cSopenharmony_ci
1594c84f3f3cSopenharmony_ci			if (how == JP_SHORT)
1595c84f3f3cSopenharmony_ci				buf[0] = '\0';
1596c84f3f3cSopenharmony_ci			else if (exitstatus == 0)
1597c84f3f3cSopenharmony_ci				memcpy(buf, "Done", 5);
1598c84f3f3cSopenharmony_ci			else
1599c84f3f3cSopenharmony_ci				shf_snprintf(buf, sizeof(buf), "Done (%d)",
1600c84f3f3cSopenharmony_ci				    exitstatus);
1601c84f3f3cSopenharmony_ci			break;
1602c84f3f3cSopenharmony_ci		}
1603c84f3f3cSopenharmony_ci		case PSIGNALLED: {
1604c84f3f3cSopenharmony_ci			int termsig = WTERMSIG(p->status);
1605c84f3f3cSopenharmony_ci#ifdef WCOREDUMP
1606c84f3f3cSopenharmony_ci			if (WCOREDUMP(p->status))
1607c84f3f3cSopenharmony_ci				coredumped = true;
1608c84f3f3cSopenharmony_ci#endif
1609c84f3f3cSopenharmony_ci			/*
1610c84f3f3cSopenharmony_ci			 * kludge for not reporting 'normal termination
1611c84f3f3cSopenharmony_ci			 * signals' (i.e. SIGINT, SIGPIPE)
1612c84f3f3cSopenharmony_ci			 */
1613c84f3f3cSopenharmony_ci			if (how == JP_SHORT &&
1614c84f3f3cSopenharmony_ci#ifdef WCOREDUMP
1615c84f3f3cSopenharmony_ci			    !coredumped &&
1616c84f3f3cSopenharmony_ci#endif
1617c84f3f3cSopenharmony_ci			    (termsig == SIGINT || termsig == SIGPIPE)) {
1618c84f3f3cSopenharmony_ci				buf[0] = '\0';
1619c84f3f3cSopenharmony_ci			} else
1620c84f3f3cSopenharmony_ci				strlcpy(buf, termsig > 0 && termsig < ksh_NSIG ?
1621c84f3f3cSopenharmony_ci				    sigtraps[termsig].mess : "Signalled",
1622c84f3f3cSopenharmony_ci				    sizeof(buf));
1623c84f3f3cSopenharmony_ci			break;
1624c84f3f3cSopenharmony_ci		}
1625c84f3f3cSopenharmony_ci		default:
1626c84f3f3cSopenharmony_ci			buf[0] = '\0';
1627c84f3f3cSopenharmony_ci		}
1628c84f3f3cSopenharmony_ci
1629c84f3f3cSopenharmony_ci		if (how != JP_SHORT) {
1630c84f3f3cSopenharmony_ci			if (p == j->proc_list)
1631c84f3f3cSopenharmony_ci				shf_fprintf(shf, "[%d] %c ", j->job, jobchar);
1632c84f3f3cSopenharmony_ci			else
1633c84f3f3cSopenharmony_ci				shf_puts(filler, shf);
1634c84f3f3cSopenharmony_ci		}
1635c84f3f3cSopenharmony_ci
1636c84f3f3cSopenharmony_ci		if (how == JP_LONG)
1637c84f3f3cSopenharmony_ci			shf_fprintf(shf, "%5d ", (int)p->pid);
1638c84f3f3cSopenharmony_ci
1639c84f3f3cSopenharmony_ci		if (how == JP_SHORT) {
1640c84f3f3cSopenharmony_ci			if (buf[0]) {
1641c84f3f3cSopenharmony_ci				output = 1;
1642c84f3f3cSopenharmony_ci#ifdef WCOREDUMP
1643c84f3f3cSopenharmony_ci				shf_fprintf(shf, "%s%s ",
1644c84f3f3cSopenharmony_ci				    buf, coredumped ? " (core dumped)" : null);
1645c84f3f3cSopenharmony_ci#else
1646c84f3f3cSopenharmony_ci				shf_puts(buf, shf);
1647c84f3f3cSopenharmony_ci				shf_putchar(' ', shf);
1648c84f3f3cSopenharmony_ci#endif
1649c84f3f3cSopenharmony_ci			}
1650c84f3f3cSopenharmony_ci		} else {
1651c84f3f3cSopenharmony_ci			output = 1;
1652c84f3f3cSopenharmony_ci			shf_fprintf(shf, "%-20s %s%s%s", buf, p->command,
1653c84f3f3cSopenharmony_ci			    p->next ? "|" : null,
1654c84f3f3cSopenharmony_ci#ifdef WCOREDUMP
1655c84f3f3cSopenharmony_ci			    coredumped ? " (core dumped)" :
1656c84f3f3cSopenharmony_ci#endif
1657c84f3f3cSopenharmony_ci			     null);
1658c84f3f3cSopenharmony_ci		}
1659c84f3f3cSopenharmony_ci
1660c84f3f3cSopenharmony_ci		state = p->state;
1661c84f3f3cSopenharmony_ci		status = p->status;
1662c84f3f3cSopenharmony_ci		p = p->next;
1663c84f3f3cSopenharmony_ci		while (p && p->state == state && p->status == status) {
1664c84f3f3cSopenharmony_ci			if (how == JP_LONG)
1665c84f3f3cSopenharmony_ci				shf_fprintf(shf, "%s%5d %-20s %s%s", filler,
1666c84f3f3cSopenharmony_ci				    (int)p->pid, T1space, p->command,
1667c84f3f3cSopenharmony_ci				    p->next ? "|" : null);
1668c84f3f3cSopenharmony_ci			else if (how == JP_MEDIUM)
1669c84f3f3cSopenharmony_ci				shf_fprintf(shf, Tf__ss, p->command,
1670c84f3f3cSopenharmony_ci				    p->next ? "|" : null);
1671c84f3f3cSopenharmony_ci			p = p->next;
1672c84f3f3cSopenharmony_ci		}
1673c84f3f3cSopenharmony_ci	}
1674c84f3f3cSopenharmony_ci	if (output)
1675c84f3f3cSopenharmony_ci		shf_putc('\n', shf);
1676c84f3f3cSopenharmony_ci}
1677c84f3f3cSopenharmony_ci
1678c84f3f3cSopenharmony_ci/*
1679c84f3f3cSopenharmony_ci * Convert % sequence to job
1680c84f3f3cSopenharmony_ci *
1681c84f3f3cSopenharmony_ci * If jobs are compiled in then this routine expects sigchld to be blocked.
1682c84f3f3cSopenharmony_ci */
1683c84f3f3cSopenharmony_cistatic Job *
1684c84f3f3cSopenharmony_cij_lookup(const char *cp, int *ecodep)
1685c84f3f3cSopenharmony_ci{
1686c84f3f3cSopenharmony_ci	Job *j, *last_match;
1687c84f3f3cSopenharmony_ci	Proc *p;
1688c84f3f3cSopenharmony_ci	size_t len;
1689c84f3f3cSopenharmony_ci	int job = 0;
1690c84f3f3cSopenharmony_ci
1691c84f3f3cSopenharmony_ci	if (ctype(*cp, C_DIGIT) && getn(cp, &job)) {
1692c84f3f3cSopenharmony_ci		/* Look for last_proc->pid (what $! returns) first... */
1693c84f3f3cSopenharmony_ci		for (j = job_list; j != NULL; j = j->next)
1694c84f3f3cSopenharmony_ci			if (j->last_proc && j->last_proc->pid == job)
1695c84f3f3cSopenharmony_ci				return (j);
1696c84f3f3cSopenharmony_ci		/*
1697c84f3f3cSopenharmony_ci		 * ...then look for process group (this is non-POSIX,
1698c84f3f3cSopenharmony_ci		 * but should not break anything
1699c84f3f3cSopenharmony_ci		 */
1700c84f3f3cSopenharmony_ci		for (j = job_list; j != NULL; j = j->next)
1701c84f3f3cSopenharmony_ci			if (j->pgrp && j->pgrp == job)
1702c84f3f3cSopenharmony_ci				return (j);
1703c84f3f3cSopenharmony_ci		goto j_lookup_nosuch;
1704c84f3f3cSopenharmony_ci	}
1705c84f3f3cSopenharmony_ci	if (*cp != '%') {
1706c84f3f3cSopenharmony_ci j_lookup_invalid:
1707c84f3f3cSopenharmony_ci		if (ecodep)
1708c84f3f3cSopenharmony_ci			*ecodep = JL_INVALID;
1709c84f3f3cSopenharmony_ci		return (NULL);
1710c84f3f3cSopenharmony_ci	}
1711c84f3f3cSopenharmony_ci	switch (*++cp) {
1712c84f3f3cSopenharmony_ci	case '\0': /* non-standard */
1713c84f3f3cSopenharmony_ci	case '+':
1714c84f3f3cSopenharmony_ci	case '%':
1715c84f3f3cSopenharmony_ci		if (job_list != NULL)
1716c84f3f3cSopenharmony_ci			return (job_list);
1717c84f3f3cSopenharmony_ci		break;
1718c84f3f3cSopenharmony_ci
1719c84f3f3cSopenharmony_ci	case '-':
1720c84f3f3cSopenharmony_ci		if (job_list != NULL && job_list->next)
1721c84f3f3cSopenharmony_ci			return (job_list->next);
1722c84f3f3cSopenharmony_ci		break;
1723c84f3f3cSopenharmony_ci
1724c84f3f3cSopenharmony_ci	case '0': case '1': case '2': case '3': case '4':
1725c84f3f3cSopenharmony_ci	case '5': case '6': case '7': case '8': case '9':
1726c84f3f3cSopenharmony_ci		if (!getn(cp, &job))
1727c84f3f3cSopenharmony_ci			goto j_lookup_invalid;
1728c84f3f3cSopenharmony_ci		for (j = job_list; j != NULL; j = j->next)
1729c84f3f3cSopenharmony_ci			if (j->job == job)
1730c84f3f3cSopenharmony_ci				return (j);
1731c84f3f3cSopenharmony_ci		break;
1732c84f3f3cSopenharmony_ci
1733c84f3f3cSopenharmony_ci	/* %?string */
1734c84f3f3cSopenharmony_ci	case '?':
1735c84f3f3cSopenharmony_ci		last_match = NULL;
1736c84f3f3cSopenharmony_ci		for (j = job_list; j != NULL; j = j->next)
1737c84f3f3cSopenharmony_ci			for (p = j->proc_list; p != NULL; p = p->next)
1738c84f3f3cSopenharmony_ci				if (strstr(p->command, cp+1) != NULL) {
1739c84f3f3cSopenharmony_ci					if (last_match) {
1740c84f3f3cSopenharmony_ci						if (ecodep)
1741c84f3f3cSopenharmony_ci							*ecodep = JL_AMBIG;
1742c84f3f3cSopenharmony_ci						return (NULL);
1743c84f3f3cSopenharmony_ci					}
1744c84f3f3cSopenharmony_ci					last_match = j;
1745c84f3f3cSopenharmony_ci				}
1746c84f3f3cSopenharmony_ci		if (last_match)
1747c84f3f3cSopenharmony_ci			return (last_match);
1748c84f3f3cSopenharmony_ci		break;
1749c84f3f3cSopenharmony_ci
1750c84f3f3cSopenharmony_ci	/* %string */
1751c84f3f3cSopenharmony_ci	default:
1752c84f3f3cSopenharmony_ci		len = strlen(cp);
1753c84f3f3cSopenharmony_ci		last_match = NULL;
1754c84f3f3cSopenharmony_ci		for (j = job_list; j != NULL; j = j->next)
1755c84f3f3cSopenharmony_ci			if (strncmp(cp, j->proc_list->command, len) == 0) {
1756c84f3f3cSopenharmony_ci				if (last_match) {
1757c84f3f3cSopenharmony_ci					if (ecodep)
1758c84f3f3cSopenharmony_ci						*ecodep = JL_AMBIG;
1759c84f3f3cSopenharmony_ci					return (NULL);
1760c84f3f3cSopenharmony_ci				}
1761c84f3f3cSopenharmony_ci				last_match = j;
1762c84f3f3cSopenharmony_ci			}
1763c84f3f3cSopenharmony_ci		if (last_match)
1764c84f3f3cSopenharmony_ci			return (last_match);
1765c84f3f3cSopenharmony_ci		break;
1766c84f3f3cSopenharmony_ci	}
1767c84f3f3cSopenharmony_ci j_lookup_nosuch:
1768c84f3f3cSopenharmony_ci	if (ecodep)
1769c84f3f3cSopenharmony_ci		*ecodep = JL_NOSUCH;
1770c84f3f3cSopenharmony_ci	return (NULL);
1771c84f3f3cSopenharmony_ci}
1772c84f3f3cSopenharmony_ci
1773c84f3f3cSopenharmony_cistatic Job	*free_jobs;
1774c84f3f3cSopenharmony_cistatic Proc	*free_procs;
1775c84f3f3cSopenharmony_ci
1776c84f3f3cSopenharmony_ci/*
1777c84f3f3cSopenharmony_ci * allocate a new job and fill in the job number.
1778c84f3f3cSopenharmony_ci *
1779c84f3f3cSopenharmony_ci * If jobs are compiled in then this routine expects sigchld to be blocked.
1780c84f3f3cSopenharmony_ci */
1781c84f3f3cSopenharmony_cistatic Job *
1782c84f3f3cSopenharmony_cinew_job(void)
1783c84f3f3cSopenharmony_ci{
1784c84f3f3cSopenharmony_ci	int i;
1785c84f3f3cSopenharmony_ci	Job *newj, *j;
1786c84f3f3cSopenharmony_ci
1787c84f3f3cSopenharmony_ci	if (free_jobs != NULL) {
1788c84f3f3cSopenharmony_ci		newj = free_jobs;
1789c84f3f3cSopenharmony_ci		free_jobs = free_jobs->next;
1790c84f3f3cSopenharmony_ci	} else {
1791c84f3f3cSopenharmony_ci		char *cp;
1792c84f3f3cSopenharmony_ci
1793c84f3f3cSopenharmony_ci		/*
1794c84f3f3cSopenharmony_ci		 * struct job includes ALLOC_ITEM for alignment constraints
1795c84f3f3cSopenharmony_ci		 * so first get the actually used memory, then assign it
1796c84f3f3cSopenharmony_ci		 */
1797c84f3f3cSopenharmony_ci		cp = alloc(sizeof(Job) - sizeof(ALLOC_ITEM), APERM);
1798c84f3f3cSopenharmony_ci		/* undo what alloc() did to the malloc result address */
1799c84f3f3cSopenharmony_ci		newj = (void *)(cp - sizeof(ALLOC_ITEM));
1800c84f3f3cSopenharmony_ci	}
1801c84f3f3cSopenharmony_ci
1802c84f3f3cSopenharmony_ci	/* brute force method */
1803c84f3f3cSopenharmony_ci	i = 0;
1804c84f3f3cSopenharmony_ci	do {
1805c84f3f3cSopenharmony_ci		++i;
1806c84f3f3cSopenharmony_ci		j = job_list;
1807c84f3f3cSopenharmony_ci		while (j && j->job != i)
1808c84f3f3cSopenharmony_ci			j = j->next;
1809c84f3f3cSopenharmony_ci	} while (j);
1810c84f3f3cSopenharmony_ci	newj->job = i;
1811c84f3f3cSopenharmony_ci
1812c84f3f3cSopenharmony_ci	return (newj);
1813c84f3f3cSopenharmony_ci}
1814c84f3f3cSopenharmony_ci
1815c84f3f3cSopenharmony_ci/*
1816c84f3f3cSopenharmony_ci * Allocate new process struct
1817c84f3f3cSopenharmony_ci *
1818c84f3f3cSopenharmony_ci * If jobs are compiled in then this routine expects sigchld to be blocked.
1819c84f3f3cSopenharmony_ci */
1820c84f3f3cSopenharmony_cistatic Proc *
1821c84f3f3cSopenharmony_cinew_proc(void)
1822c84f3f3cSopenharmony_ci{
1823c84f3f3cSopenharmony_ci	Proc *p;
1824c84f3f3cSopenharmony_ci
1825c84f3f3cSopenharmony_ci	if (free_procs != NULL) {
1826c84f3f3cSopenharmony_ci		p = free_procs;
1827c84f3f3cSopenharmony_ci		free_procs = free_procs->next;
1828c84f3f3cSopenharmony_ci	} else
1829c84f3f3cSopenharmony_ci		p = alloc(sizeof(Proc), APERM);
1830c84f3f3cSopenharmony_ci
1831c84f3f3cSopenharmony_ci	return (p);
1832c84f3f3cSopenharmony_ci}
1833c84f3f3cSopenharmony_ci
1834c84f3f3cSopenharmony_ci/*
1835c84f3f3cSopenharmony_ci * Take job out of job_list and put old structures into free list.
1836c84f3f3cSopenharmony_ci * Keeps nzombies, last_job and async_job up to date.
1837c84f3f3cSopenharmony_ci *
1838c84f3f3cSopenharmony_ci * If jobs are compiled in then this routine expects sigchld to be blocked.
1839c84f3f3cSopenharmony_ci */
1840c84f3f3cSopenharmony_cistatic void
1841c84f3f3cSopenharmony_ciremove_job(Job *j, const char *where)
1842c84f3f3cSopenharmony_ci{
1843c84f3f3cSopenharmony_ci	Proc *p, *tmp;
1844c84f3f3cSopenharmony_ci	Job **prev, *curr;
1845c84f3f3cSopenharmony_ci
1846c84f3f3cSopenharmony_ci	prev = &job_list;
1847c84f3f3cSopenharmony_ci	curr = job_list;
1848c84f3f3cSopenharmony_ci	while (curr && curr != j) {
1849c84f3f3cSopenharmony_ci		prev = &curr->next;
1850c84f3f3cSopenharmony_ci		curr = *prev;
1851c84f3f3cSopenharmony_ci	}
1852c84f3f3cSopenharmony_ci	if (curr != j) {
1853c84f3f3cSopenharmony_ci		internal_warningf("remove_job: job %s (%s)", Tnot_found, where);
1854c84f3f3cSopenharmony_ci		return;
1855c84f3f3cSopenharmony_ci	}
1856c84f3f3cSopenharmony_ci	*prev = curr->next;
1857c84f3f3cSopenharmony_ci
1858c84f3f3cSopenharmony_ci	/* free up proc structures */
1859c84f3f3cSopenharmony_ci	for (p = j->proc_list; p != NULL; ) {
1860c84f3f3cSopenharmony_ci		tmp = p;
1861c84f3f3cSopenharmony_ci		p = p->next;
1862c84f3f3cSopenharmony_ci		tmp->next = free_procs;
1863c84f3f3cSopenharmony_ci		free_procs = tmp;
1864c84f3f3cSopenharmony_ci	}
1865c84f3f3cSopenharmony_ci
1866c84f3f3cSopenharmony_ci	if ((j->flags & JF_ZOMBIE) && j->ppid == procpid)
1867c84f3f3cSopenharmony_ci		--nzombie;
1868c84f3f3cSopenharmony_ci	j->next = free_jobs;
1869c84f3f3cSopenharmony_ci	free_jobs = j;
1870c84f3f3cSopenharmony_ci
1871c84f3f3cSopenharmony_ci	if (j == last_job)
1872c84f3f3cSopenharmony_ci		last_job = NULL;
1873c84f3f3cSopenharmony_ci	if (j == async_job)
1874c84f3f3cSopenharmony_ci		async_job = NULL;
1875c84f3f3cSopenharmony_ci}
1876c84f3f3cSopenharmony_ci
1877c84f3f3cSopenharmony_ci/*
1878c84f3f3cSopenharmony_ci * put j in a particular location (taking it out job_list if it is there
1879c84f3f3cSopenharmony_ci * already)
1880c84f3f3cSopenharmony_ci *
1881c84f3f3cSopenharmony_ci * If jobs are compiled in then this routine expects sigchld to be blocked.
1882c84f3f3cSopenharmony_ci */
1883c84f3f3cSopenharmony_cistatic void
1884c84f3f3cSopenharmony_ciput_job(Job *j, int where)
1885c84f3f3cSopenharmony_ci{
1886c84f3f3cSopenharmony_ci	Job **prev, *curr;
1887c84f3f3cSopenharmony_ci
1888c84f3f3cSopenharmony_ci	/* Remove job from list (if there) */
1889c84f3f3cSopenharmony_ci	prev = &job_list;
1890c84f3f3cSopenharmony_ci	curr = job_list;
1891c84f3f3cSopenharmony_ci	while (curr && curr != j) {
1892c84f3f3cSopenharmony_ci		prev = &curr->next;
1893c84f3f3cSopenharmony_ci		curr = *prev;
1894c84f3f3cSopenharmony_ci	}
1895c84f3f3cSopenharmony_ci	if (curr == j)
1896c84f3f3cSopenharmony_ci		*prev = curr->next;
1897c84f3f3cSopenharmony_ci
1898c84f3f3cSopenharmony_ci	switch (where) {
1899c84f3f3cSopenharmony_ci	case PJ_ON_FRONT:
1900c84f3f3cSopenharmony_ci		j->next = job_list;
1901c84f3f3cSopenharmony_ci		job_list = j;
1902c84f3f3cSopenharmony_ci		break;
1903c84f3f3cSopenharmony_ci
1904c84f3f3cSopenharmony_ci	case PJ_PAST_STOPPED:
1905c84f3f3cSopenharmony_ci		prev = &job_list;
1906c84f3f3cSopenharmony_ci		curr = job_list;
1907c84f3f3cSopenharmony_ci		for (; curr && curr->state == PSTOPPED; prev = &curr->next,
1908c84f3f3cSopenharmony_ci		    curr = *prev)
1909c84f3f3cSopenharmony_ci			;
1910c84f3f3cSopenharmony_ci		j->next = curr;
1911c84f3f3cSopenharmony_ci		*prev = j;
1912c84f3f3cSopenharmony_ci		break;
1913c84f3f3cSopenharmony_ci	}
1914c84f3f3cSopenharmony_ci}
1915c84f3f3cSopenharmony_ci
1916c84f3f3cSopenharmony_ci/*
1917c84f3f3cSopenharmony_ci * nuke a job (called when unable to start full job).
1918c84f3f3cSopenharmony_ci *
1919c84f3f3cSopenharmony_ci * If jobs are compiled in then this routine expects sigchld to be blocked.
1920c84f3f3cSopenharmony_ci */
1921c84f3f3cSopenharmony_cistatic int
1922c84f3f3cSopenharmony_cikill_job(Job *j, int sig)
1923c84f3f3cSopenharmony_ci{
1924c84f3f3cSopenharmony_ci	Proc *p;
1925c84f3f3cSopenharmony_ci	int rval = 0;
1926c84f3f3cSopenharmony_ci
1927c84f3f3cSopenharmony_ci	for (p = j->proc_list; p != NULL; p = p->next)
1928c84f3f3cSopenharmony_ci		if (p->pid != 0)
1929c84f3f3cSopenharmony_ci			if (kill(p->pid, sig) < 0)
1930c84f3f3cSopenharmony_ci				rval = -1;
1931c84f3f3cSopenharmony_ci	return (rval);
1932c84f3f3cSopenharmony_ci}
1933c84f3f3cSopenharmony_ci
1934c84f3f3cSopenharmony_cistatic void
1935c84f3f3cSopenharmony_citty_init_talking(void)
1936c84f3f3cSopenharmony_ci{
1937c84f3f3cSopenharmony_ci	switch (tty_init_fd()) {
1938c84f3f3cSopenharmony_ci	case 0:
1939c84f3f3cSopenharmony_ci		break;
1940c84f3f3cSopenharmony_ci	case 1:
1941c84f3f3cSopenharmony_ci#ifndef MKSH_DISABLE_TTY_WARNING
1942c84f3f3cSopenharmony_ci		warningf(false, Tf_sD_s_sD_s,
1943c84f3f3cSopenharmony_ci		    "No controlling tty", Topen, T_devtty, cstrerror(errno));
1944c84f3f3cSopenharmony_ci#endif
1945c84f3f3cSopenharmony_ci		break;
1946c84f3f3cSopenharmony_ci	case 2:
1947c84f3f3cSopenharmony_ci#ifndef MKSH_DISABLE_TTY_WARNING
1948c84f3f3cSopenharmony_ci		warningf(false, Tf_s_sD_s, Tcant_find, Ttty_fd,
1949c84f3f3cSopenharmony_ci		    cstrerror(errno));
1950c84f3f3cSopenharmony_ci#endif
1951c84f3f3cSopenharmony_ci		break;
1952c84f3f3cSopenharmony_ci	case 3:
1953c84f3f3cSopenharmony_ci		warningf(false, Tf_ssfaileds, "j_ttyinit",
1954c84f3f3cSopenharmony_ci		    Ttty_fd_dupof, cstrerror(errno));
1955c84f3f3cSopenharmony_ci		break;
1956c84f3f3cSopenharmony_ci	case 4:
1957c84f3f3cSopenharmony_ci		warningf(false, Tf_sD_sD_s, "j_ttyinit",
1958c84f3f3cSopenharmony_ci		    "can't set close-on-exec flag", cstrerror(errno));
1959c84f3f3cSopenharmony_ci		break;
1960c84f3f3cSopenharmony_ci	}
1961c84f3f3cSopenharmony_ci}
1962c84f3f3cSopenharmony_ci
1963c84f3f3cSopenharmony_cistatic void
1964c84f3f3cSopenharmony_citty_init_state(void)
1965c84f3f3cSopenharmony_ci{
1966c84f3f3cSopenharmony_ci	if (tty_fd >= 0) {
1967c84f3f3cSopenharmony_ci		mksh_tcget(tty_fd, &tty_state);
1968c84f3f3cSopenharmony_ci		tty_hasstate = true;
1969c84f3f3cSopenharmony_ci	}
1970c84f3f3cSopenharmony_ci}
1971