xref: /illumos-gate/usr/src/cmd/sh/jobs.c (revision 09f67678c27dda8a89f87f1f408a87dd49ceb0e1)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright (c) 1996, by Sun Microsystems, Inc.
28  * All rights reserved.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 /*
33  * Job control for UNIX Shell
34  */
35 
36 #include	<sys/termio.h>
37 #include	<sys/types.h>
38 #include	<sys/wait.h>
39 #include	<sys/param.h>
40 #include	<fcntl.h>
41 #include	<errno.h>
42 #include	"defs.h"
43 
44 /*
45  * one of these for each active job
46  */
47 
48 struct job
49 {
50 	struct job *j_nxtp;	/* next job in job ID order */
51 	struct job *j_curp;	/* next job in job currency order */
52 	struct termios j_stty;	/* termio save area when job stops */
53 	pid_t  j_pid;		/* job leader's process ID */
54 	pid_t  j_pgid;		/* job's process group ID */
55 	pid_t  j_tgid;		/* job's foreground process group ID */
56 	uint   j_jid;		/* job ID */
57 	ushort j_xval;		/* exit code, or exit or stop signal */
58 	ushort j_flag;		/* various status flags defined below */
59 	char  *j_pwd;		/* job's working directory */
60 	char  *j_cmd;		/* cmd used to invoke this job */
61 };
62 
63 /* defines for j_flag */
64 
65 #define	J_DUMPED	0001	/* job has core dumped */
66 #define	J_NOTIFY	0002	/* job has changed status */
67 #define	J_SAVETTY	0004	/* job was stopped in foreground, and its */
68 				/*   termio settings were saved */
69 #define	J_STOPPED	0010	/* job has been stopped */
70 #define	J_SIGNALED	0020	/* job has received signal; j_xval has it */
71 #define	J_DONE		0040	/* job has finished */
72 #define	J_RUNNING	0100	/* job is currently running */
73 #define	J_FOREGND	0200	/* job was put in foreground by shell */
74 
75 /* options to the printjob() function defined below */
76 
77 #define	PR_CUR		00001	/* print job currency ('+', '-', or ' ') */
78 #define	PR_JID		00002	/* print job ID				 */
79 #define	PR_PGID		00004	/* print job's process group ID		 */
80 #define	PR_STAT		00010	/* print status obtained from wait	 */
81 #define	PR_CMD		00020	/* print cmd that invoked job		 */
82 #define	PR_AMP		00040	/* print a '&' if in the background	 */
83 #define	PR_PWD		00100	/* print jobs present working directory	 */
84 
85 #define	PR_DFL		(PR_CUR|PR_JID|PR_STAT|PR_CMD) /* default options */
86 #define	PR_LONG		(PR_DFL|PR_PGID|PR_PWD)	/* long options */
87 
88 static struct termios 	mystty;	 /* default termio settings		 */
89 static int 		eofflg,
90 			jobcnt,	 /* number of active jobs		 */
91 			jobdone, /* number of active but finished jobs	 */
92 			jobnote; /* jobs requiring notification		 */
93 static pid_t		svpgid,	 /* saved process group ID		 */
94 			svtgid;	 /* saved foreground process group ID	 */
95 static struct job 	*jobcur, /* active jobs listed in currency order */
96 			**nextjob,
97 			*thisjob,
98 			*joblst; /* active jobs listed in job ID order	 */
99 
100 pid_t
101 tcgetpgrp(fd)
102 {
103 	pid_t pgid;
104 	if (ioctl(fd, TIOCGPGRP, &pgid) == 0)
105 		return (pgid);
106 	return ((pid_t)-1);
107 }
108 
109 int
110 tcsetpgrp(fd, pgid)
111 int fd;
112 pid_t pgid;
113 {
114 	return (ioctl(fd, TIOCSPGRP, &pgid));
115 }
116 
117 static struct job *
118 pgid2job(pgid)
119 register pid_t pgid;
120 {
121 	register struct job *jp;
122 
123 	for (jp = joblst; jp != 0 && jp->j_pid != pgid; jp = jp->j_nxtp)
124 		continue;
125 
126 	return (jp);
127 }
128 
129 static struct job *
130 str2job(cmd, job, mustbejob)
131 register char *cmd;
132 register char *job;
133 int mustbejob;
134 {
135 	register struct job *jp, *njp;
136 	register i;
137 
138 	if (*job != '%')
139 		jp = pgid2job(stoi(job));
140 	else if (*++job == 0 || *job == '+' || *job == '%' || *job == '-') {
141 		jp = jobcur;
142 		if (*job == '-' && jp)
143 			jp = jp->j_curp;
144 	} else if (*job >= '0' && *job <= '9') {
145 		i = stoi(job);
146 		for (jp = joblst; jp && jp->j_jid != i; jp = jp->j_nxtp)
147 			continue;
148 	} else if (*job == '?') {
149 		register j;
150 		register char *p;
151 		i = strlen(++job);
152 		jp = 0;
153 		for (njp = jobcur; njp; njp = njp->j_curp) {
154 			if (njp->j_jid == 0)
155 				continue;
156 			for (p = njp->j_cmd, j = strlen(p); j >= i; p++, j--) {
157 				if (strncmp(job, p, i) == 0) {
158 					if (jp != 0)
159 						failed(cmd, ambiguous);
160 					jp = njp;
161 					break;
162 				}
163 			}
164 		}
165 	} else {
166 		i = strlen(job);
167 		jp = 0;
168 		for (njp = jobcur; njp; njp = njp->j_curp) {
169 			if (njp->j_jid == 0)
170 				continue;
171 			if (strncmp(job, njp->j_cmd, i) == 0) {
172 				if (jp != 0)
173 					failed(cmd, ambiguous);
174 				jp = njp;
175 			}
176 		}
177 	}
178 
179 	if (mustbejob && (jp == 0 || jp->j_jid == 0))
180 		failed(cmd, nosuchjob);
181 
182 	return (jp);
183 }
184 
185 static void
186 freejob(jp)
187 register struct job *jp;
188 {
189 	register struct job **njp;
190 	register struct job **cjp;
191 
192 	for (njp = &joblst; *njp != jp; njp = &(*njp)->j_nxtp)
193 		continue;
194 
195 	for (cjp = &jobcur; *cjp != jp; cjp = &(*cjp)->j_curp)
196 		continue;
197 
198 	*njp = jp->j_nxtp;
199 	*cjp = jp->j_curp;
200 	free(jp);
201 	jobcnt--;
202 	jobdone--;
203 }
204 
205 /*
206  * Collect the foreground job.
207  * Used in the case where the subshell wants
208  * to exit, but needs to wait until the fg job
209  * is done.
210  */
211 collect_fg_job()
212 {
213 	register struct job *jp;
214 	register pid_t pid;
215 	int stat;
216 
217 	for (jp = joblst; jp; jp = jp->j_nxtp)
218 		if (jp->j_flag & J_FOREGND)
219 			break;
220 
221 	if (!jp)
222 		/* no foreground job */
223 		return;
224 
225 	/*
226 	 * Wait on fg job until wait succeeds
227 	 * or it fails due to no waitable children.
228 	 */
229 
230 	while (1) {
231 		errno = 0;
232 		pid = waitpid(jp->j_pid, &stat, 0);
233 		if (pid == jp->j_pid || (pid == -1 && errno == ECHILD))
234 			break;
235 	}
236 }
237 
238 /*
239  * analyze the status of a job
240  */
241 
242 static int
243 statjob(jp, stat, fg, rc)
244 register struct job *jp;
245 register stat;
246 int fg;
247 int rc;
248 {
249 	pid_t tgid;
250 	int done = 0;
251 
252 	if (WIFCONTINUED(stat)) {
253 		if (jp->j_flag & J_STOPPED) {
254 			jp->j_flag &= ~(J_STOPPED|J_SIGNALED|J_SAVETTY);
255 			jp->j_flag |= J_RUNNING;
256 			if (!fg && jp->j_jid) {
257 				jp->j_flag |= J_NOTIFY;
258 				jobnote++;
259 			}
260 		}
261 	} else if (WIFSTOPPED(stat)) {
262 		jp->j_xval = WSTOPSIG(stat);
263 		jp->j_flag &= ~J_RUNNING;
264 		jp->j_flag |= (J_SIGNALED|J_STOPPED);
265 		jp->j_pgid = getpgid(jp->j_pid);
266 		jp->j_tgid = jp->j_pgid;
267 		if (fg) {
268 			if (tgid = settgid(mypgid, jp->j_pgid))
269 				jp->j_tgid = tgid;
270 			else {
271 				jp->j_flag |= J_SAVETTY;
272 				tcgetattr(0, &jp->j_stty);
273 				(void) tcsetattr(0, TCSANOW, &mystty);
274 			}
275 		}
276 		if (jp->j_jid) {
277 			jp->j_flag |= J_NOTIFY;
278 			jobnote++;
279 		}
280 	} else {
281 		jp->j_flag &= ~J_RUNNING;
282 		jp->j_flag |= J_DONE;
283 		done++;
284 		jobdone++;
285 		if (WIFSIGNALED(stat)) {
286 			jp->j_xval = WTERMSIG(stat);
287 			jp->j_flag |= J_SIGNALED;
288 			if (WCOREDUMP(stat))
289 				jp->j_flag |= J_DUMPED;
290 			if (!fg || jp->j_xval != SIGINT) {
291 				jp->j_flag |= J_NOTIFY;
292 				jobnote++;
293 			}
294 		} else { /* WIFEXITED */
295 			jp->j_xval = WEXITSTATUS(stat);
296 			jp->j_flag &= ~J_SIGNALED;
297 			if (!fg && jp->j_jid) {
298 				jp->j_flag |= J_NOTIFY;
299 				jobnote++;
300 			}
301 		}
302 		if (fg) {
303 			if (!settgid(mypgid, jp->j_pgid) ||
304 			    !settgid(mypgid, getpgid(jp->j_pid)))
305 				tcgetattr(0, &mystty);
306 		}
307 	}
308 	if (rc) {
309 		exitval = jp->j_xval;
310 		if (jp->j_flag & J_SIGNALED)
311 			exitval |= SIGFLG;
312 		exitset();
313 	}
314 	if (done && !(jp->j_flag & J_NOTIFY))
315 		freejob(jp);
316 	return (done);
317 }
318 
319 /*
320  * collect the status of jobs that have recently exited or stopped -
321  * if wnohang == WNOHANG, wait until error, or all jobs are accounted for;
322  *
323  * called after each command is executed, with wnohang == 0, and as part
324  * of "wait" builtin with wnohang == WNOHANG
325  *
326  * We do not need to call chktrap here if waitpid(2) is called with
327  * wnohang == 0, because that only happens from syswait() which is called
328  * from builtin() where chktrap() is already called.
329  */
330 
331 static void
332 collectjobs(wnohang)
333 {
334 	pid_t pid;
335 	register struct job *jp;
336 	int stat, n;
337 	int wflags;
338 
339 	if ((flags & (monitorflg|jcflg|jcoff)) == (monitorflg|jcflg))
340 		wflags = WUNTRACED|WCONTINUED;
341 	else
342 		wflags = 0;
343 
344 	for (n = jobcnt - jobdone; n > 0; n--) {
345 		if ((pid = waitpid(-1, &stat, wnohang|wflags)) <= 0)
346 			break;
347 		if (jp = pgid2job(pid))
348 			(void) statjob(jp, stat, 0, 0);
349 	}
350 
351 }
352 
353 void
354 freejobs()
355 {
356 	register struct job *jp;
357 
358 	collectjobs(WNOHANG);
359 
360 	if (jobnote) {
361 		register int savefd = setb(2);
362 		for (jp = joblst; jp; jp = jp->j_nxtp) {
363 			if (jp->j_flag & J_NOTIFY) {
364 				if (jp->j_jid)
365 					printjob(jp, PR_DFL);
366 				else if (jp->j_flag & J_FOREGND)
367 					printjob(jp, PR_STAT);
368 				else
369 					printjob(jp, PR_STAT|PR_PGID);
370 			}
371 		}
372 		(void) setb(savefd);
373 	}
374 
375 	if (jobdone) {
376 		for (jp = joblst; jp; jp = jp->j_nxtp) {
377 			if (jp->j_flag & J_DONE)
378 				freejob(jp);
379 		}
380 	}
381 }
382 
383 static void
384 waitjob(jp)
385 register struct job *jp;
386 {
387 	int stat;
388 	int done;
389 	pid_t pid = jp->j_pid;
390 	int wflags;
391 	int ret = 0;
392 	int	err = 0;
393 
394 	if ((flags & (monitorflg|jcflg|jcoff)) == (monitorflg|jcflg))
395 		wflags = WUNTRACED;
396 	else
397 		wflags = 0;
398 	do {
399 		errno = 0;
400 		ret = waitpid(pid, &stat, wflags|WNOWAIT);
401 		err = errno;
402 		if (ret == -1 && err == ECHILD) {
403 			stat = 0;
404 			break;
405 		}
406 	} while (ret != pid);
407 
408 	done = statjob(jp, stat, 1, 1);
409 	waitpid(pid, 0, wflags);
410 	if (done && exitval && (flags & errflg))
411 		exitsh(exitval);
412 	flags |= eflag;
413 }
414 
415 /*
416  * modify the foreground process group to *new* only if the
417  * current foreground process group is equal to *expected*
418  */
419 
420 int
421 settgid(new, expected)
422 pid_t new, expected;
423 {
424 	register pid_t current = tcgetpgrp(0);
425 
426 	if (current != expected)
427 		return (current);
428 
429 	if (new != current)
430 		tcsetpgrp(0, new);
431 
432 	return (0);
433 }
434 
435 static void
436 restartjob(jp, fg)
437 register struct job *jp;
438 {
439 	if (jp != jobcur) {
440 		register struct job *t;
441 		for (t = jobcur; t->j_curp != jp; t = t->j_curp);
442 		t->j_curp = jp->j_curp;
443 		jp->j_curp = jobcur;
444 		jobcur = jp;
445 	}
446 	if (fg) {
447 		if (jp->j_flag & J_SAVETTY) {
448 			jp->j_stty.c_lflag &= ~TOSTOP;
449 			jp->j_stty.c_lflag |= (mystty.c_lflag&TOSTOP);
450 			jp->j_stty.c_cc[VSUSP] = mystty.c_cc[VSUSP];
451 			jp->j_stty.c_cc[VDSUSP] = mystty.c_cc[VDSUSP];
452 			(void) tcsetattr(0, TCSADRAIN, &jp->j_stty);
453 		}
454 		(void) settgid(jp->j_tgid, mypgid);
455 	}
456 	(void) kill(-(jp->j_pgid), SIGCONT);
457 	if (jp->j_tgid != jp->j_pgid)
458 		(void) kill(-(jp->j_tgid), SIGCONT);
459 	jp->j_flag &= ~(J_STOPPED|J_SIGNALED|J_SAVETTY);
460 	jp->j_flag |= J_RUNNING;
461 	if (fg)  {
462 		jp->j_flag |= J_FOREGND;
463 		printjob(jp, PR_JID|PR_CMD);
464 		waitjob(jp);
465 	} else {
466 		jp->j_flag &= ~J_FOREGND;
467 		printjob(jp, PR_JID|PR_CMD|PR_AMP);
468 	}
469 }
470 
471 static
472 printjob(jp, propts)
473 register struct job *jp;
474 {
475 	int sp = 0;
476 
477 	if (jp->j_flag & J_NOTIFY) {
478 		jobnote--;
479 		jp->j_flag &= ~J_NOTIFY;
480 	}
481 
482 	if (propts & PR_JID) {
483 		prc_buff('[');
484 		prn_buff(jp->j_jid);
485 		prc_buff(']');
486 		sp = 1;
487 	}
488 
489 	if (propts & PR_CUR) {
490 		while (sp-- > 0)
491 			prc_buff(SPACE);
492 		sp = 1;
493 		if (jobcur == jp)
494 			prc_buff('+');
495 		else if (jobcur != 0 && jobcur->j_curp == jp)
496 			prc_buff('-');
497 		else
498 			sp++;
499 	}
500 
501 	if (propts & PR_PGID) {
502 		while (sp-- > 0)
503 			prc_buff(SPACE);
504 		prn_buff(jp->j_pid);
505 		sp = 1;
506 	}
507 
508 	if (propts & PR_STAT) {
509 		while (sp-- > 0)
510 			prc_buff(SPACE);
511 		sp = 28;
512 		if (jp->j_flag & J_SIGNALED) {
513 			char *sigstr, *strsignal();
514 			if ((sigstr = strsignal(jp->j_xval)) != NULL) {
515 				sp -= strlen(sigstr);
516 				prs_buff(sigstr);
517 			} else {
518 				itos(jp->j_xval);
519 				sp -= strlen(numbuf) + 7;
520 				prs_buff("Signal ");
521 				prs_buff(numbuf);
522 			}
523 			if (jp->j_flag & J_DUMPED) {
524 				sp -= strlen(coredump);
525 				prs_buff(coredump);
526 			}
527 		} else if (jp->j_flag & J_DONE) {
528 			itos(jp->j_xval);
529 			sp -= strlen(exited) + strlen(numbuf) + 2;
530 			prs_buff(exited);
531 			prc_buff('(');
532 			itos(jp->j_xval);
533 			prs_buff(numbuf);
534 			prc_buff(')');
535 		} else {
536 			sp -= strlen(running);
537 			prs_buff(running);
538 		}
539 		if (sp < 1)
540 			sp = 1;
541 	}
542 
543 	if (propts & PR_CMD) {
544 		while (sp-- > 0)
545 			prc_buff(SPACE);
546 		prs_buff(jp->j_cmd);
547 		sp = 1;
548 	}
549 
550 	if (propts & PR_AMP) {
551 		while (sp-- > 0)
552 			prc_buff(SPACE);
553 		prc_buff('&');
554 		sp = 1;
555 	}
556 
557 	if (propts & PR_PWD) {
558 		while (sp-- > 0)
559 			prc_buff(SPACE);
560 		prs_buff("(wd: ");
561 		prs_buff(jp->j_pwd);
562 		prc_buff(')');
563 	}
564 
565 	prc_buff(NL);
566 	flushb();
567 
568 }
569 
570 
571 /*
572  * called to initialize job control for each new input file to the shell,
573  * and after the "exec" builtin
574  */
575 
576 void
577 startjobs()
578 {
579 	svpgid = mypgid;
580 
581 	if (tcgetattr(0, &mystty) == -1 || (svtgid = tcgetpgrp(0)) == -1) {
582 		flags &= ~jcflg;
583 		return;
584 	}
585 
586 	flags |= jcflg;
587 
588 	handle(SIGTTOU, SIG_IGN);
589 	handle(SIGTSTP, SIG_DFL);
590 
591 	if (mysid != mypgid) {
592 		setpgid(0, 0);
593 		mypgid = mypid;
594 		(void) settgid(mypgid, svpgid);
595 	}
596 
597 }
598 
599 int
600 endjobs(check_if)
601 int check_if;
602 {
603 	if ((flags & (jcoff|jcflg)) != jcflg)
604 		return (1);
605 
606 	if (check_if && jobcnt && eofflg++ == 0) {
607 		register struct job *jp;
608 		if (check_if & JOB_STOPPED) {
609 			for (jp = joblst; jp; jp = jp->j_nxtp) {
610 				if (jp->j_jid && (jp->j_flag & J_STOPPED)) {
611 					prs(jobsstopped);
612 					prc(NL);
613 					return (0);
614 				}
615 			}
616 		}
617 		if (check_if & JOB_RUNNING) {
618 			for (jp = joblst; jp; jp = jp->j_nxtp) {
619 				if (jp->j_jid && (jp->j_flag & J_RUNNING)) {
620 					prs(jobsrunning);
621 					prc(NL);
622 					return (0);
623 				}
624 			}
625 		}
626 	}
627 
628 	if (svpgid != mypgid) {
629 		(void) settgid(svtgid, mypgid);
630 		setpgid(0, svpgid);
631 	}
632 
633 	return (1);
634 }
635 
636 
637 /*
638  * called by the shell to reserve a job slot for a job about to be spawned
639  */
640 
641 void
642 deallocjob()
643 {
644 	free(thisjob);
645 	jobcnt--;
646 }
647 
648 allocjob(cmd, cwd, monitor)
649 register char *cmd;
650 register unchar *cwd;
651 int monitor;
652 {
653 	register struct job *jp, **jpp;
654 	register int jid, cmdlen, cwdlen;
655 
656 	cmdlen = strlen(cmd) + 1;
657 	if (cmd[cmdlen-2] == '&') {
658 		cmd[cmdlen-3] = 0;
659 		cmdlen -= 2;
660 	}
661 	cwdlen = strlen(cwd) + 1;
662 	jp = (struct job *) alloc(sizeof (struct job) + cmdlen + cwdlen);
663 	if (jp == 0)
664 		error(nostack);
665 	jobcnt++;
666 	jp->j_cmd = ((char *)jp) + sizeof (struct job);
667 	strcpy(jp->j_cmd, cmd);
668 	jp->j_pwd = jp->j_cmd + cmdlen;
669 	strcpy(jp->j_pwd, cwd);
670 
671 	jpp = &joblst;
672 
673 	if (monitor) {
674 		for (; *jpp; jpp = &(*jpp)->j_nxtp)
675 			if ((*jpp)->j_jid != 0)
676 				break;
677 		for (jid = 1; *jpp; jpp = &(*jpp)->j_nxtp, jid++)
678 			if ((*jpp)->j_jid != jid)
679 				break;
680 	} else
681 		jid = 0;
682 
683 	jp->j_jid = jid;
684 	nextjob = jpp;
685 	thisjob = jp;
686 }
687 
688 clearjobs()
689 {
690 	register struct job *jp, *sjp;
691 
692 	for (jp = joblst; jp; jp = sjp) {
693 		sjp = jp->j_nxtp;
694 		free(jp);
695 	}
696 	joblst = NULL;
697 	jobcnt = 0;
698 	jobnote = 0;
699 	jobdone = 0;
700 
701 }
702 
703 makejob(monitor, fg)
704 int monitor, fg;
705 {
706 	if (monitor) {
707 		mypgid = mypid;
708 		setpgid(0, 0);
709 		if (fg)
710 			tcsetpgrp(0, mypid);
711 		handle(SIGTTOU, SIG_DFL);
712 		handle(SIGTSTP, SIG_DFL);
713 	} else if (!fg) {
714 #ifdef NICE
715 		nice(NICE);
716 #endif
717 		handle(SIGTTIN, SIG_IGN);
718 		handle(SIGINT,  SIG_IGN);
719 		handle(SIGQUIT, SIG_IGN);
720 		if (!ioset)
721 			renamef(chkopen(devnull, 0), 0);
722 	}
723 }
724 
725 /*
726  * called by the shell after job has been spawned, to fill in the
727  * job slot, and wait for the job if in the foreground
728  */
729 
730 void
731 postjob(pid, fg)
732 pid_t pid;
733 int fg;
734 {
735 
736 	register propts;
737 
738 	thisjob->j_nxtp = *nextjob;
739 	*nextjob = thisjob;
740 	thisjob->j_curp = jobcur;
741 	jobcur = thisjob;
742 
743 	if (thisjob->j_jid) {
744 		thisjob->j_pgid = pid;
745 		propts = PR_JID|PR_PGID;
746 	} else {
747 		thisjob->j_pgid = mypgid;
748 		propts = PR_PGID;
749 	}
750 
751 	thisjob->j_flag = J_RUNNING;
752 	thisjob->j_tgid = thisjob->j_pgid;
753 	thisjob->j_pid = pid;
754 	eofflg = 0;
755 
756 	if (fg) {
757 		thisjob->j_flag |= J_FOREGND;
758 		waitjob(thisjob);
759 	} else  {
760 		if (flags & ttyflg)
761 			printjob(thisjob, propts);
762 		assnum(&pcsadr, (long)pid);
763 	}
764 }
765 
766 /*
767  * the builtin "jobs" command
768  */
769 
770 void
771 sysjobs(argc, argv)
772 int argc;
773 char *argv[];
774 {
775 	register char *cmd = *argv;
776 	register struct job *jp;
777 	register propts, c;
778 	extern int opterr, i;
779 	int savoptind = optind;
780 	int loptind = -1;
781 	int savopterr = opterr;
782 	int savsp = _sp;
783 	char *savoptarg = optarg;
784 	optind = 1;
785 	opterr = 0;
786 	_sp = 1;
787 	propts = 0;
788 
789 	if ((flags & jcflg) == 0)
790 		failed(cmd, nojc);
791 
792 	while ((c = getopt(argc, argv, "lpx")) != -1) {
793 		if (propts) {
794 			failure(usage, jobsuse);
795 			goto err;
796 		}
797 		switch (c) {
798 			case 'x':
799 				propts = -1;
800 				break;
801 			case 'p':
802 				propts = PR_PGID;
803 				break;
804 			case 'l':
805 				propts = PR_LONG;
806 				break;
807 			case '?':
808 				failure(usage, jobsuse);
809 				goto err;
810 		}
811 	}
812 
813 	loptind = optind;
814 err:
815 	optind = savoptind;
816 	optarg = savoptarg;
817 	opterr = savopterr;
818 	_sp = savsp;
819 	if (loptind == -1)
820 		return;
821 
822 	if (propts == -1) {
823 		register unsigned char *bp;
824 		register char *cp;
825 		unsigned char *savebp;
826 		for (savebp = bp = locstak(); loptind < argc; loptind++) {
827 			cp = argv[loptind];
828 			if (*cp == '%') {
829 				jp = str2job(cmd, cp, 1);
830 				itos(jp->j_pid);
831 				cp = (char *)numbuf;
832 			}
833 			while (*cp) {
834 				if (bp >= brkend)
835 					growstak(bp);
836 				*bp++ = *cp++;
837 			}
838 			if (bp >= brkend)
839 				growstak(bp);
840 			*bp++ = SPACE;
841 		}
842 		endstak(bp);
843 		execexp(savebp, 0);
844 		return;
845 	}
846 
847 	collectjobs(WNOHANG);
848 
849 	if (propts == 0)
850 		propts = PR_DFL;
851 
852 	if (loptind == argc) {
853 		for (jp = joblst; jp; jp = jp->j_nxtp) {
854 			if (jp->j_jid)
855 				printjob(jp, propts);
856 		}
857 	} else do
858 		printjob(str2job(cmd, argv[loptind++], 1), propts);
859 	while (loptind < argc);
860 
861 }
862 
863 /*
864  * the builtin "fg" and "bg" commands
865  */
866 
867 sysfgbg(argc, argv)
868 int argc;
869 char *argv[];
870 {
871 	register char *cmd = *argv;
872 	register fg;
873 
874 	if ((flags & jcflg) == 0)
875 		failed(cmd, nojc);
876 
877 	fg = eq("fg", cmd);
878 
879 	if (*++argv == 0) {
880 		struct job *jp;
881 		for (jp = jobcur; ; jp = jp->j_curp) {
882 			if (jp == 0)
883 				failed(cmd, nocurjob);
884 			if (jp->j_jid)
885 				break;
886 		}
887 		restartjob(jp, fg);
888 	}
889 
890 	else do
891 		restartjob(str2job(cmd, *argv, 1), fg);
892 	while (*++argv);
893 
894 }
895 
896 /*
897  * the builtin "wait" commands
898  */
899 
900 void
901 syswait(argc, argv)
902 int argc;
903 char *argv[];
904 {
905 	register char *cmd = *argv;
906 	register struct job *jp;
907 	int stat;
908 	int wflags;
909 
910 	if ((flags & (monitorflg|jcflg|jcoff)) == (monitorflg|jcflg))
911 		wflags = WUNTRACED;
912 	else
913 		wflags = 0;
914 
915 	if (argc == 1)
916 		collectjobs(0);
917 	else while (--argc) {
918 		if ((jp = str2job(cmd, *++argv, 0)) == 0)
919 			continue;
920 		if (!(jp->j_flag & J_RUNNING))
921 			continue;
922 		if (waitpid(jp->j_pid, &stat, wflags) <= 0)
923 			break;
924 		(void) statjob(jp, stat, 0, 1);
925 	}
926 }
927 
928 static
929 sigv(cmd, sig, args)
930 	char *cmd;
931 	int sig;
932 	char *args;
933 {
934 	int pgrp = 0;
935 	int stopme = 0;
936 	pid_t id;
937 
938 	if (*args == '%') {
939 		register struct job *jp;
940 		jp = str2job(cmd, args, 1);
941 		id = jp->j_pgid;
942 		pgrp++;
943 	} else {
944 		if (*args == '-') {
945 			pgrp++;
946 			args++;
947 		}
948 		id = 0;
949 		do {
950 			if (*args < '0' || *args > '9') {
951 				failure(cmd, badid);
952 				return;
953 			}
954 			id = (id * 10) + (*args - '0');
955 		} while (*++args);
956 		if (id == 0) {
957 			id = mypgid;
958 			pgrp++;
959 		}
960 	}
961 
962 	if (sig == SIGSTOP) {
963 		if (id == mysid || id == mypid && mypgid == mysid) {
964 			failure(cmd, loginsh);
965 			return;
966 		}
967 		if (id == mypgid && mypgid != svpgid) {
968 			(void) settgid(svtgid, mypgid);
969 			setpgid(0, svpgid);
970 			stopme++;
971 		}
972 	}
973 
974 	if (pgrp)
975 		id = -id;
976 
977 	if (kill(id, sig) < 0) {
978 
979 		switch (errno) {
980 			case EPERM:
981 				failure(cmd, eacces);
982 				break;
983 
984 			case EINVAL:
985 				failure(cmd, badsig);
986 				break;
987 
988 			default:
989 				if (pgrp)
990 					failure(cmd, nosuchpgid);
991 				else
992 					failure(cmd, nosuchpid);
993 				break;
994 		}
995 
996 	} else if (sig == SIGTERM && pgrp)
997 		(void) kill(id, SIGCONT);
998 
999 	if (stopme) {
1000 		setpgid(0, mypgid);
1001 		(void) settgid(mypgid, svpgid);
1002 	}
1003 
1004 }
1005 
1006 sysstop(argc, argv)
1007 int argc;
1008 char *argv[];
1009 {
1010 	char *cmd = *argv;
1011 	if (argc <= 1)
1012 		failed(usage, stopuse);
1013 	while (*++argv)
1014 		sigv(cmd, SIGSTOP, *argv);
1015 }
1016 
1017 syskill(argc, argv)
1018 int argc;
1019 char *argv[];
1020 {
1021 	char *cmd = *argv;
1022 	int sig = SIGTERM;
1023 
1024 	if (argc == 1) {
1025 		failure(usage, killuse);
1026 		return;
1027 	}
1028 
1029 	if (argv[1][0] == '-') {
1030 
1031 		if (argc == 2) {
1032 
1033 			register i;
1034 			register cnt = 0;
1035 			register char sep = 0;
1036 			char buf[12];
1037 
1038 			if (!eq(argv[1], "-l")) {
1039 				failure(usage, killuse);
1040 				return;
1041 			}
1042 
1043 			for (i = 1; i < MAXTRAP; i++) {
1044 				if (sig2str(i, buf) < 0)
1045 					continue;
1046 				if (sep)
1047 					prc_buff(sep);
1048 				prs_buff(buf);
1049 				if ((flags & ttyflg) && (++cnt % 10))
1050 					sep = TAB;
1051 				else
1052 					sep = NL;
1053 			}
1054 			prc_buff(NL);
1055 			return;
1056 		}
1057 
1058 		if (str2sig(&argv[1][1], &sig)) {
1059 			failure(cmd, badsig);
1060 			return;
1061 		}
1062 		argv++;
1063 	}
1064 
1065 	while (*++argv)
1066 		sigv(cmd, sig, *argv);
1067 
1068 }
1069 
1070 syssusp(argc, argv)
1071 int argc;
1072 char *argv[];
1073 {
1074 	if (argc != 1)
1075 		failed(argv[0], badopt);
1076 	sigv(argv[0], SIGSTOP, "0");
1077 }
1078