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