Initial import from FreeBSD RELENG_4:
[dragonfly.git] / bin / sh / jobs.c
CommitLineData
984263bc
MD
1/*-
2 * Copyright (c) 1991, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38#if 0
39static char sccsid[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95";
40#endif
41#endif /* not lint */
42#include <sys/cdefs.h>
43__FBSDID("$FreeBSD: src/bin/sh/jobs.c,v 1.27.2.10 2003/04/04 08:16:26 tjr Exp $");
44
45#include <fcntl.h>
46#include <signal.h>
47#include <errno.h>
48#include <paths.h>
49#include <unistd.h>
50#include <stdlib.h>
51#include <sys/param.h>
52#include <sys/wait.h>
53#include <sys/time.h>
54#include <sys/resource.h>
55#include <paths.h>
56#include <sys/ioctl.h>
57
58#include "shell.h"
59#if JOBS
60#include <termios.h>
61#undef CEOF /* syntax.h redefines this */
62#endif
63#include "redir.h"
64#include "show.h"
65#include "main.h"
66#include "parser.h"
67#include "nodes.h"
68#include "jobs.h"
69#include "options.h"
70#include "trap.h"
71#include "syntax.h"
72#include "input.h"
73#include "output.h"
74#include "memalloc.h"
75#include "error.h"
76#include "mystring.h"
77
78
79struct job *jobtab; /* array of jobs */
80int njobs; /* size of array */
81MKINIT pid_t backgndpid = -1; /* pid of last background process */
82#if JOBS
83struct job *jobmru; /* most recently used job list */
84pid_t initialpgrp; /* pgrp of shell on invocation */
85#endif
86int in_waitcmd = 0; /* are we in waitcmd()? */
87int in_dowait = 0; /* are we in dowait()? */
88volatile sig_atomic_t breakwaitcmd = 0; /* should wait be terminated? */
89static int ttyfd = -1;
90
91#if JOBS
92STATIC void restartjob(struct job *);
93#endif
94STATIC void freejob(struct job *);
95STATIC struct job *getjob(char *);
96STATIC pid_t dowait(int, struct job *);
97STATIC pid_t waitproc(int, int *);
98STATIC void cmdtxt(union node *);
99STATIC void cmdputs(char *);
100#if JOBS
101STATIC void setcurjob(struct job *);
102STATIC void deljob(struct job *);
103STATIC struct job *getcurjob(struct job *);
104#endif
105STATIC void showjob(struct job *, pid_t, int, int);
106
107
108/*
109 * Turn job control on and off.
110 */
111
112MKINIT int jobctl;
113
114#if JOBS
115void
116setjobctl(int on)
117{
118 int i;
119
120 if (on == jobctl || rootshell == 0)
121 return;
122 if (on) {
123 if (ttyfd != -1)
124 close(ttyfd);
125 if ((ttyfd = open(_PATH_TTY, O_RDWR)) < 0) {
126 i = 0;
127 while (i <= 2 && !isatty(i))
128 i++;
129 if (i > 2 || (ttyfd = fcntl(i, F_DUPFD, 10)) < 0)
130 goto out;
131 }
132 if (ttyfd < 10) {
133 /*
134 * Keep our TTY file descriptor out of the way of
135 * the user's redirections.
136 */
137 if ((i = fcntl(ttyfd, F_DUPFD, 10)) < 0) {
138 close(ttyfd);
139 ttyfd = -1;
140 goto out;
141 }
142 close(ttyfd);
143 ttyfd = i;
144 }
145 if (fcntl(ttyfd, F_SETFD, FD_CLOEXEC) < 0) {
146 close(ttyfd);
147 ttyfd = -1;
148 goto out;
149 }
150 do { /* while we are in the background */
151 initialpgrp = tcgetpgrp(ttyfd);
152 if (initialpgrp < 0) {
153out: out2str("sh: can't access tty; job control turned off\n");
154 mflag = 0;
155 return;
156 }
157 if (initialpgrp == -1)
158 initialpgrp = getpgrp();
159 else if (initialpgrp != getpgrp()) {
160 killpg(0, SIGTTIN);
161 continue;
162 }
163 } while (0);
164 setsignal(SIGTSTP);
165 setsignal(SIGTTOU);
166 setsignal(SIGTTIN);
167 setpgid(0, rootpid);
168 tcsetpgrp(ttyfd, rootpid);
169 } else { /* turning job control off */
170 setpgid(0, initialpgrp);
171 tcsetpgrp(ttyfd, initialpgrp);
172 close(ttyfd);
173 ttyfd = -1;
174 setsignal(SIGTSTP);
175 setsignal(SIGTTOU);
176 setsignal(SIGTTIN);
177 }
178 jobctl = on;
179}
180#endif
181
182
183#ifdef mkinit
184INCLUDE <sys/types.h>
185INCLUDE <stdlib.h>
186
187SHELLPROC {
188 backgndpid = -1;
189#if JOBS
190 jobctl = 0;
191#endif
192}
193
194#endif
195
196
197
198#if JOBS
199int
200fgcmd(int argc __unused, char **argv)
201{
202 struct job *jp;
203 pid_t pgrp;
204 int status;
205
206 jp = getjob(argv[1]);
207 if (jp->jobctl == 0)
208 error("job not created under job control");
209 out1str(jp->ps[0].cmd);
210 out1c('\n');
211 flushout(&output);
212 pgrp = jp->ps[0].pid;
213 tcsetpgrp(ttyfd, pgrp);
214 restartjob(jp);
215 jp->foreground = 1;
216 INTOFF;
217 status = waitforjob(jp, (int *)NULL);
218 INTON;
219 return status;
220}
221
222
223int
224bgcmd(int argc, char **argv)
225{
226 char s[64];
227 struct job *jp;
228
229 do {
230 jp = getjob(*++argv);
231 if (jp->jobctl == 0)
232 error("job not created under job control");
233 if (jp->state == JOBDONE)
234 continue;
235 restartjob(jp);
236 jp->foreground = 0;
237 fmtstr(s, 64, "[%d] ", jp - jobtab + 1);
238 out1str(s);
239 out1str(jp->ps[0].cmd);
240 out1c('\n');
241 } while (--argc > 1);
242 return 0;
243}
244
245
246STATIC void
247restartjob(struct job *jp)
248{
249 struct procstat *ps;
250 int i;
251
252 if (jp->state == JOBDONE)
253 return;
254 setcurjob(jp);
255 INTOFF;
256 killpg(jp->ps[0].pid, SIGCONT);
257 for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
258 if (WIFSTOPPED(ps->status)) {
259 ps->status = -1;
260 jp->state = 0;
261 }
262 }
263 INTON;
264}
265#endif
266
267
268int
269jobscmd(int argc, char *argv[])
270{
271 char *id;
272 int ch, sformat, lformat;
273
274 optind = optreset = 1;
275 opterr = 0;
276 sformat = lformat = 0;
277 while ((ch = getopt(argc, argv, "ls")) != -1) {
278 switch (ch) {
279 case 'l':
280 lformat = 1;
281 break;
282 case 's':
283 sformat = 1;
284 break;
285 case '?':
286 default:
287 error("unknown option: -%c", optopt);
288 }
289 }
290 argc -= optind;
291 argv += optind;
292
293 if (argc == 0)
294 showjobs(0, sformat, lformat);
295 else
296 while ((id = *argv++) != NULL)
297 showjob(getjob(id), 0, sformat, lformat);
298
299 return (0);
300}
301
302STATIC void
303showjob(struct job *jp, pid_t pid, int sformat, int lformat)
304{
305 char s[64];
306 struct procstat *ps;
307 struct job *j;
308 int col, curr, i, jobno, prev, procno;
309 char c;
310
311 procno = jp->nprocs;
312 jobno = jp - jobtab + 1;
313 curr = prev = 0;
314#if JOBS
315 if ((j = getcurjob(NULL)) != NULL) {
316 curr = j - jobtab + 1;
317 if ((j = getcurjob(j)) != NULL)
318 prev = j - jobtab + 1;
319 }
320#endif
321 for (ps = jp->ps ; ; ps++) { /* for each process */
322 if (sformat) {
323 out1fmt("%d\n", (int)ps->pid);
324 goto skip;
325 }
326 if (!lformat && ps != jp->ps && pid == 0)
327 goto skip;
328 if (pid != 0 && pid != ps->pid)
329 goto skip;
330 if (jobno == curr && ps == jp->ps)
331 c = '+';
332 else if (jobno == prev && ps == jp->ps)
333 c = '-';
334 else
335 c = ' ';
336 if (ps == jp->ps)
337 fmtstr(s, 64, "[%d] %c ", jobno, c);
338 else
339 fmtstr(s, 64, " %c ", c);
340 out1str(s);
341 col = strlen(s);
342 if (lformat) {
343 fmtstr(s, 64, "%d ", (int)ps->pid);
344 out1str(s);
345 col += strlen(s);
346 }
347 s[0] = '\0';
348 if (ps != jp->ps) {
349 *s = '\0';
350 } else if (ps->status == -1) {
351 strcpy(s, "Running");
352 } else if (WIFEXITED(ps->status)) {
353 if (WEXITSTATUS(ps->status) == 0)
354 strcpy(s, "Done");
355 else
356 fmtstr(s, 64, "Done (%d)",
357 WEXITSTATUS(ps->status));
358 } else {
359#if JOBS
360 if (WIFSTOPPED(ps->status))
361 i = WSTOPSIG(ps->status);
362 else
363#endif
364 i = WTERMSIG(ps->status);
365 if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F])
366 scopy(sys_siglist[i & 0x7F], s);
367 else
368 fmtstr(s, 64, "Signal %d", i & 0x7F);
369 if (WCOREDUMP(ps->status))
370 strcat(s, " (core dumped)");
371 }
372 out1str(s);
373 col += strlen(s);
374 do {
375 out1c(' ');
376 col++;
377 } while (col < 30);
378 out1str(ps->cmd);
379 out1c('\n');
380skip: if (--procno <= 0)
381 break;
382 }
383}
384
385/*
386 * Print a list of jobs. If "change" is nonzero, only print jobs whose
387 * statuses have changed since the last call to showjobs.
388 *
389 * If the shell is interrupted in the process of creating a job, the
390 * result may be a job structure containing zero processes. Such structures
391 * will be freed here.
392 */
393
394void
395showjobs(int change, int sformat, int lformat)
396{
397 int jobno;
398 struct job *jp;
399
400 TRACE(("showjobs(%d) called\n", change));
401 while (dowait(0, (struct job *)NULL) > 0);
402 for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
403 if (! jp->used)
404 continue;
405 if (jp->nprocs == 0) {
406 freejob(jp);
407 continue;
408 }
409 if (change && ! jp->changed)
410 continue;
411 showjob(jp, 0, sformat, lformat);
412 jp->changed = 0;
413 if (jp->state == JOBDONE) {
414 freejob(jp);
415 }
416 }
417}
418
419
420/*
421 * Mark a job structure as unused.
422 */
423
424STATIC void
425freejob(struct job *jp)
426{
427 struct procstat *ps;
428 int i;
429
430 INTOFF;
431 for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
432 if (ps->cmd != nullstr)
433 ckfree(ps->cmd);
434 }
435 if (jp->ps != &jp->ps0)
436 ckfree(jp->ps);
437 jp->used = 0;
438#if JOBS
439 deljob(jp);
440#endif
441 INTON;
442}
443
444
445
446int
447waitcmd(int argc, char **argv)
448{
449 struct job *job;
450 int status, retval;
451 struct job *jp;
452
453 if (argc > 1) {
454 job = getjob(argv[1]);
455 } else {
456 job = NULL;
457 }
458
459 /*
460 * Loop until a process is terminated or stopped, or a SIGINT is
461 * received.
462 */
463
464 in_waitcmd++;
465 do {
466 if (job != NULL) {
467 if (job->state) {
468 status = job->ps[job->nprocs - 1].status;
469 if (WIFEXITED(status))
470 retval = WEXITSTATUS(status);
471#if JOBS
472 else if (WIFSTOPPED(status))
473 retval = WSTOPSIG(status) + 128;
474#endif
475 else
476 retval = WTERMSIG(status) + 128;
477 if (! iflag)
478 freejob(job);
479 in_waitcmd--;
480 return retval;
481 }
482 } else {
483 for (jp = jobtab ; ; jp++) {
484 if (jp >= jobtab + njobs) { /* no running procs */
485 in_waitcmd--;
486 return 0;
487 }
488 if (jp->used && jp->state == 0)
489 break;
490 }
491 }
492 } while (dowait(1, (struct job *)NULL) != -1);
493 in_waitcmd--;
494
495 return 0;
496}
497
498
499
500int
501jobidcmd(int argc __unused, char **argv)
502{
503 struct job *jp;
504 int i;
505
506 jp = getjob(argv[1]);
507 for (i = 0 ; i < jp->nprocs ; ) {
508 out1fmt("%d", (int)jp->ps[i].pid);
509 out1c(++i < jp->nprocs? ' ' : '\n');
510 }
511 return 0;
512}
513
514
515
516/*
517 * Convert a job name to a job structure.
518 */
519
520STATIC struct job *
521getjob(char *name)
522{
523 int jobno;
524 struct job *found, *jp;
525 pid_t pid;
526 int i;
527
528 if (name == NULL) {
529#if JOBS
530currentjob: if ((jp = getcurjob(NULL)) == NULL)
531 error("No current job");
532 return (jp);
533#else
534 error("No current job");
535#endif
536 } else if (name[0] == '%') {
537 if (is_digit(name[1])) {
538 jobno = number(name + 1);
539 if (jobno > 0 && jobno <= njobs
540 && jobtab[jobno - 1].used != 0)
541 return &jobtab[jobno - 1];
542#if JOBS
543 } else if (name[1] == '%' && name[2] == '\0') {
544 goto currentjob;
545 } else if (name[1] == '+' && name[2] == '\0') {
546 goto currentjob;
547 } else if (name[1] == '-' && name[2] == '\0') {
548 if ((jp = getcurjob(NULL)) == NULL ||
549 (jp = getcurjob(jp)) == NULL)
550 error("No previous job");
551 return (jp);
552#endif
553 } else if (name[1] == '?') {
554 found = NULL;
555 for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
556 if (jp->used && jp->nprocs > 0
557 && strstr(jp->ps[0].cmd, name + 2) != NULL) {
558 if (found)
559 error("%s: ambiguous", name);
560 found = jp;
561 }
562 }
563 if (found != NULL)
564 return (found);
565 } else {
566 found = NULL;
567 for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
568 if (jp->used && jp->nprocs > 0
569 && prefix(name + 1, jp->ps[0].cmd)) {
570 if (found)
571 error("%s: ambiguous", name);
572 found = jp;
573 }
574 }
575 if (found)
576 return found;
577 }
578 } else if (is_number(name)) {
579 pid = (pid_t)number(name);
580 for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
581 if (jp->used && jp->nprocs > 0
582 && jp->ps[jp->nprocs - 1].pid == pid)
583 return jp;
584 }
585 }
586 error("No such job: %s", name);
587 /*NOTREACHED*/
588 return NULL;
589}
590
591
592
593/*
594 * Return a new job structure,
595 */
596
597struct job *
598makejob(union node *node __unused, int nprocs)
599{
600 int i;
601 struct job *jp;
602
603 for (i = njobs, jp = jobtab ; ; jp++) {
604 if (--i < 0) {
605 INTOFF;
606 if (njobs == 0) {
607 jobtab = ckmalloc(4 * sizeof jobtab[0]);
608#if JOBS
609 jobmru = NULL;
610#endif
611 } else {
612 jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
613 memcpy(jp, jobtab, njobs * sizeof jp[0]);
614#if JOBS
615 /* Relocate `next' pointers and list head */
616 if (jobmru != NULL)
617 jobmru = &jp[jobmru - jobtab];
618 for (i = 0; i < njobs; i++)
619 if (jp[i].next != NULL)
620 jp[i].next = &jp[jp[i].next -
621 jobtab];
622#endif
623 /* Relocate `ps' pointers */
624 for (i = 0; i < njobs; i++)
625 if (jp[i].ps == &jobtab[i].ps0)
626 jp[i].ps = &jp[i].ps0;
627 ckfree(jobtab);
628 jobtab = jp;
629 }
630 jp = jobtab + njobs;
631 for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0);
632 INTON;
633 break;
634 }
635 if (jp->used == 0)
636 break;
637 }
638 INTOFF;
639 jp->state = 0;
640 jp->used = 1;
641 jp->changed = 0;
642 jp->nprocs = 0;
643 jp->foreground = 0;
644#if JOBS
645 jp->jobctl = jobctl;
646 jp->next = NULL;
647#endif
648 if (nprocs > 1) {
649 jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
650 } else {
651 jp->ps = &jp->ps0;
652 }
653 INTON;
654 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
655 jp - jobtab + 1));
656 return jp;
657}
658
659#if JOBS
660STATIC void
661setcurjob(struct job *cj)
662{
663 struct job *jp, *prev;
664
665 for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) {
666 if (jp == cj) {
667 if (prev != NULL)
668 prev->next = jp->next;
669 else
670 jobmru = jp->next;
671 jp->next = jobmru;
672 jobmru = cj;
673 return;
674 }
675 }
676 cj->next = jobmru;
677 jobmru = cj;
678}
679
680STATIC void
681deljob(struct job *j)
682{
683 struct job *jp, *prev;
684
685 for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) {
686 if (jp == j) {
687 if (prev != NULL)
688 prev->next = jp->next;
689 else
690 jobmru = jp->next;
691 return;
692 }
693 }
694}
695
696/*
697 * Return the most recently used job that isn't `nj', and preferably one
698 * that is stopped.
699 */
700STATIC struct job *
701getcurjob(struct job *nj)
702{
703 struct job *jp;
704
705 /* Try to find a stopped one.. */
706 for (jp = jobmru; jp != NULL; jp = jp->next)
707 if (jp->used && jp != nj && jp->state == JOBSTOPPED)
708 return (jp);
709 /* Otherwise the most recently used job that isn't `nj' */
710 for (jp = jobmru; jp != NULL; jp = jp->next)
711 if (jp->used && jp != nj)
712 return (jp);
713
714 return (NULL);
715}
716
717#endif
718
719/*
720 * Fork of a subshell. If we are doing job control, give the subshell its
721 * own process group. Jp is a job structure that the job is to be added to.
722 * N is the command that will be evaluated by the child. Both jp and n may
723 * be NULL. The mode parameter can be one of the following:
724 * FORK_FG - Fork off a foreground process.
725 * FORK_BG - Fork off a background process.
726 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
727 * process group even if job control is on.
728 *
729 * When job control is turned off, background processes have their standard
730 * input redirected to /dev/null (except for the second and later processes
731 * in a pipeline).
732 */
733
734pid_t
735forkshell(struct job *jp, union node *n, int mode)
736{
737 pid_t pid;
738 pid_t pgrp;
739
740 TRACE(("forkshell(%%%d, 0x%lx, %d) called\n", jp - jobtab, (long)n,
741 mode));
742 INTOFF;
743 flushall();
744 pid = fork();
745 if (pid == -1) {
746 TRACE(("Fork failed, errno=%d\n", errno));
747 INTON;
748 error("Cannot fork: %s", strerror(errno));
749 }
750 if (pid == 0) {
751 struct job *p;
752 int wasroot;
753 int i;
754
755 TRACE(("Child shell %d\n", (int)getpid()));
756 wasroot = rootshell;
757 rootshell = 0;
758 closescript();
759 INTON;
760 clear_traps();
761#if JOBS
762 jobctl = 0; /* do job control only in root shell */
763 if (wasroot && mode != FORK_NOJOB && mflag) {
764 if (jp == NULL || jp->nprocs == 0)
765 pgrp = getpid();
766 else
767 pgrp = jp->ps[0].pid;
768 if (setpgid(0, pgrp) == 0 && mode == FORK_FG) {
769 /*** this causes superfluous TIOCSPGRPS ***/
770 if (tcsetpgrp(ttyfd, pgrp) < 0)
771 error("tcsetpgrp failed, errno=%d", errno);
772 }
773 setsignal(SIGTSTP);
774 setsignal(SIGTTOU);
775 } else if (mode == FORK_BG) {
776 ignoresig(SIGINT);
777 ignoresig(SIGQUIT);
778 if ((jp == NULL || jp->nprocs == 0) &&
779 ! fd0_redirected_p ()) {
780 close(0);
781 if (open(_PATH_DEVNULL, O_RDONLY) != 0)
782 error("Can't open %s: %s",
783 _PATH_DEVNULL, strerror(errno));
784 }
785 }
786#else
787 if (mode == FORK_BG) {
788 ignoresig(SIGINT);
789 ignoresig(SIGQUIT);
790 if ((jp == NULL || jp->nprocs == 0) &&
791 ! fd0_redirected_p ()) {
792 close(0);
793 if (open(_PATH_DEVNULL, O_RDONLY) != 0)
794 error("Can't open %s: %s",
795 _PATH_DEVNULL, strerror(errno));
796 }
797 }
798#endif
799 INTOFF;
800 for (i = njobs, p = jobtab ; --i >= 0 ; p++)
801 if (p->used)
802 freejob(p);
803 INTON;
804 if (wasroot && iflag) {
805 setsignal(SIGINT);
806 setsignal(SIGQUIT);
807 setsignal(SIGTERM);
808 }
809 return pid;
810 }
811 if (rootshell && mode != FORK_NOJOB && mflag) {
812 if (jp == NULL || jp->nprocs == 0)
813 pgrp = pid;
814 else
815 pgrp = jp->ps[0].pid;
816 setpgid(pid, pgrp);
817 }
818 if (mode == FORK_BG)
819 backgndpid = pid; /* set $! */
820 if (jp) {
821 struct procstat *ps = &jp->ps[jp->nprocs++];
822 ps->pid = pid;
823 ps->status = -1;
824 ps->cmd = nullstr;
825 if (iflag && rootshell && n)
826 ps->cmd = commandtext(n);
827 jp->foreground = mode == FORK_FG;
828#if JOBS
829 setcurjob(jp);
830#endif
831 }
832 INTON;
833 TRACE(("In parent shell: child = %d\n", (int)pid));
834 return pid;
835}
836
837
838
839/*
840 * Wait for job to finish.
841 *
842 * Under job control we have the problem that while a child process is
843 * running interrupts generated by the user are sent to the child but not
844 * to the shell. This means that an infinite loop started by an inter-
845 * active user may be hard to kill. With job control turned off, an
846 * interactive user may place an interactive program inside a loop. If
847 * the interactive program catches interrupts, the user doesn't want
848 * these interrupts to also abort the loop. The approach we take here
849 * is to have the shell ignore interrupt signals while waiting for a
850 * foreground process to terminate, and then send itself an interrupt
851 * signal if the child process was terminated by an interrupt signal.
852 * Unfortunately, some programs want to do a bit of cleanup and then
853 * exit on interrupt; unless these processes terminate themselves by
854 * sending a signal to themselves (instead of calling exit) they will
855 * confuse this approach.
856 */
857
858int
859waitforjob(struct job *jp, int *origstatus)
860{
861#if JOBS
862 pid_t mypgrp = getpgrp();
863#endif
864 int status;
865 int st;
866
867 INTOFF;
868 TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
869 while (jp->state == 0)
870 if (dowait(1, jp) == -1)
871 dotrap();
872#if JOBS
873 if (jp->jobctl) {
874 if (tcsetpgrp(ttyfd, mypgrp) < 0)
875 error("tcsetpgrp failed, errno=%d\n", errno);
876 }
877 if (jp->state == JOBSTOPPED)
878 setcurjob(jp);
879#endif
880 status = jp->ps[jp->nprocs - 1].status;
881 if (origstatus != NULL)
882 *origstatus = status;
883 /* convert to 8 bits */
884 if (WIFEXITED(status))
885 st = WEXITSTATUS(status);
886#if JOBS
887 else if (WIFSTOPPED(status))
888 st = WSTOPSIG(status) + 128;
889#endif
890 else
891 st = WTERMSIG(status) + 128;
892 if (! JOBS || jp->state == JOBDONE)
893 freejob(jp);
894 if (int_pending()) {
895 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
896 kill(getpid(), SIGINT);
897 else
898 CLEAR_PENDING_INT;
899 }
900 INTON;
901 return st;
902}
903
904
905
906/*
907 * Wait for a process to terminate.
908 */
909
910STATIC pid_t
911dowait(int block, struct job *job)
912{
913 pid_t pid;
914 int status;
915 struct procstat *sp;
916 struct job *jp;
917 struct job *thisjob;
918 int done;
919 int stopped;
920 int sig;
921 int i;
922
923 in_dowait++;
924 TRACE(("dowait(%d) called\n", block));
925 do {
926 pid = waitproc(block, &status);
927 TRACE(("wait returns %d, status=%d\n", (int)pid, status));
928 } while ((pid == -1 && errno == EINTR && breakwaitcmd == 0) ||
929 (WIFSTOPPED(status) && !iflag));
930 in_dowait--;
931 if (breakwaitcmd != 0) {
932 breakwaitcmd = 0;
933 return -1;
934 }
935 if (pid <= 0)
936 return pid;
937 INTOFF;
938 thisjob = NULL;
939 for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
940 if (jp->used) {
941 done = 1;
942 stopped = 1;
943 for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
944 if (sp->pid == -1)
945 continue;
946 if (sp->pid == pid) {
947 TRACE(("Changing status of proc %d from 0x%x to 0x%x\n",
948 (int)pid, sp->status,
949 status));
950 sp->status = status;
951 thisjob = jp;
952 }
953 if (sp->status == -1)
954 stopped = 0;
955 else if (WIFSTOPPED(sp->status))
956 done = 0;
957 }
958 if (stopped) { /* stopped or done */
959 int state = done? JOBDONE : JOBSTOPPED;
960 if (jp->state != state) {
961 TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
962 jp->state = state;
963#if JOBS
964 if (done)
965 deljob(jp);
966#endif
967 }
968 }
969 }
970 }
971 INTON;
972 if (! rootshell || ! iflag || (job && thisjob == job)) {
973#if JOBS
974 if (WIFSTOPPED(status))
975 sig = WSTOPSIG(status);
976 else
977#endif
978 {
979 if (WIFEXITED(status))
980 sig = 0;
981 else
982 sig = WTERMSIG(status);
983 }
984 if (sig != 0 && sig != SIGINT && sig != SIGPIPE) {
985 if (!mflag ||
986 (thisjob->foreground && !WIFSTOPPED(status))) {
987 i = WTERMSIG(status);
988 if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F])
989 out1str(sys_siglist[i & 0x7F]);
990 else
991 out1fmt("Signal %d", i & 0x7F);
992 if (WCOREDUMP(status))
993 out1str(" (core dumped)");
994 out1c('\n');
995 } else
996 showjob(thisjob, pid, 0, 0);
997 }
998 } else {
999 TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job));
1000 if (thisjob)
1001 thisjob->changed = 1;
1002 }
1003 return pid;
1004}
1005
1006
1007
1008/*
1009 * Do a wait system call. If job control is compiled in, we accept
1010 * stopped processes. If block is zero, we return a value of zero
1011 * rather than blocking.
1012 */
1013STATIC pid_t
1014waitproc(int block, int *status)
1015{
1016 int flags;
1017
1018#if JOBS
1019 flags = WUNTRACED;
1020#else
1021 flags = 0;
1022#endif
1023 if (block == 0)
1024 flags |= WNOHANG;
1025 return wait3(status, flags, (struct rusage *)NULL);
1026}
1027
1028/*
1029 * return 1 if there are stopped jobs, otherwise 0
1030 */
1031int job_warning = 0;
1032int
1033stoppedjobs(void)
1034{
1035 int jobno;
1036 struct job *jp;
1037
1038 if (job_warning)
1039 return (0);
1040 for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) {
1041 if (jp->used == 0)
1042 continue;
1043 if (jp->state == JOBSTOPPED) {
1044 out2str("You have stopped jobs.\n");
1045 job_warning = 2;
1046 return (1);
1047 }
1048 }
1049
1050 return (0);
1051}
1052
1053/*
1054 * Return a string identifying a command (to be printed by the
1055 * jobs command.
1056 */
1057
1058STATIC char *cmdnextc;
1059STATIC int cmdnleft;
1060#define MAXCMDTEXT 200
1061
1062char *
1063commandtext(union node *n)
1064{
1065 char *name;
1066
1067 cmdnextc = name = ckmalloc(MAXCMDTEXT);
1068 cmdnleft = MAXCMDTEXT - 4;
1069 cmdtxt(n);
1070 *cmdnextc = '\0';
1071 return name;
1072}
1073
1074
1075STATIC void
1076cmdtxt(union node *n)
1077{
1078 union node *np;
1079 struct nodelist *lp;
1080 char *p;
1081 int i;
1082 char s[2];
1083
1084 if (n == NULL)
1085 return;
1086 switch (n->type) {
1087 case NSEMI:
1088 cmdtxt(n->nbinary.ch1);
1089 cmdputs("; ");
1090 cmdtxt(n->nbinary.ch2);
1091 break;
1092 case NAND:
1093 cmdtxt(n->nbinary.ch1);
1094 cmdputs(" && ");
1095 cmdtxt(n->nbinary.ch2);
1096 break;
1097 case NOR:
1098 cmdtxt(n->nbinary.ch1);
1099 cmdputs(" || ");
1100 cmdtxt(n->nbinary.ch2);
1101 break;
1102 case NPIPE:
1103 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
1104 cmdtxt(lp->n);
1105 if (lp->next)
1106 cmdputs(" | ");
1107 }
1108 break;
1109 case NSUBSHELL:
1110 cmdputs("(");
1111 cmdtxt(n->nredir.n);
1112 cmdputs(")");
1113 break;
1114 case NREDIR:
1115 case NBACKGND:
1116 cmdtxt(n->nredir.n);
1117 break;
1118 case NIF:
1119 cmdputs("if ");
1120 cmdtxt(n->nif.test);
1121 cmdputs("; then ");
1122 cmdtxt(n->nif.ifpart);
1123 cmdputs("...");
1124 break;
1125 case NWHILE:
1126 cmdputs("while ");
1127 goto until;
1128 case NUNTIL:
1129 cmdputs("until ");
1130until:
1131 cmdtxt(n->nbinary.ch1);
1132 cmdputs("; do ");
1133 cmdtxt(n->nbinary.ch2);
1134 cmdputs("; done");
1135 break;
1136 case NFOR:
1137 cmdputs("for ");
1138 cmdputs(n->nfor.var);
1139 cmdputs(" in ...");
1140 break;
1141 case NCASE:
1142 cmdputs("case ");
1143 cmdputs(n->ncase.expr->narg.text);
1144 cmdputs(" in ...");
1145 break;
1146 case NDEFUN:
1147 cmdputs(n->narg.text);
1148 cmdputs("() ...");
1149 break;
1150 case NCMD:
1151 for (np = n->ncmd.args ; np ; np = np->narg.next) {
1152 cmdtxt(np);
1153 if (np->narg.next)
1154 cmdputs(" ");
1155 }
1156 for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
1157 cmdputs(" ");
1158 cmdtxt(np);
1159 }
1160 break;
1161 case NARG:
1162 cmdputs(n->narg.text);
1163 break;
1164 case NTO:
1165 p = ">"; i = 1; goto redir;
1166 case NAPPEND:
1167 p = ">>"; i = 1; goto redir;
1168 case NTOFD:
1169 p = ">&"; i = 1; goto redir;
1170 case NCLOBBER:
1171 p = ">|"; i = 1; goto redir;
1172 case NFROM:
1173 p = "<"; i = 0; goto redir;
1174 case NFROMTO:
1175 p = "<>"; i = 0; goto redir;
1176 case NFROMFD:
1177 p = "<&"; i = 0; goto redir;
1178redir:
1179 if (n->nfile.fd != i) {
1180 s[0] = n->nfile.fd + '0';
1181 s[1] = '\0';
1182 cmdputs(s);
1183 }
1184 cmdputs(p);
1185 if (n->type == NTOFD || n->type == NFROMFD) {
1186 if (n->ndup.dupfd >= 0)
1187 s[0] = n->ndup.dupfd + '0';
1188 else
1189 s[0] = '-';
1190 s[1] = '\0';
1191 cmdputs(s);
1192 } else {
1193 cmdtxt(n->nfile.fname);
1194 }
1195 break;
1196 case NHERE:
1197 case NXHERE:
1198 cmdputs("<<...");
1199 break;
1200 default:
1201 cmdputs("???");
1202 break;
1203 }
1204}
1205
1206
1207
1208STATIC void
1209cmdputs(char *s)
1210{
1211 char *p, *q;
1212 char c;
1213 int subtype = 0;
1214
1215 if (cmdnleft <= 0)
1216 return;
1217 p = s;
1218 q = cmdnextc;
1219 while ((c = *p++) != '\0') {
1220 if (c == CTLESC)
1221 *q++ = *p++;
1222 else if (c == CTLVAR) {
1223 *q++ = '$';
1224 if (--cmdnleft > 0)
1225 *q++ = '{';
1226 subtype = *p++;
1227 } else if (c == '=' && subtype != 0) {
1228 *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
1229 subtype = 0;
1230 } else if (c == CTLENDVAR) {
1231 *q++ = '}';
1232 } else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE)
1233 cmdnleft++; /* ignore it */
1234 else
1235 *q++ = c;
1236 if (--cmdnleft <= 0) {
1237 *q++ = '.';
1238 *q++ = '.';
1239 *q++ = '.';
1240 break;
1241 }
1242 }
1243 cmdnextc = q;
1244}