jobs.c revision 1.1 1 /*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * 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 static char sccsid[] = "@(#)jobs.c 5.1 (Berkeley) 3/7/91";
39 #endif /* not lint */
40
41 #include "shell.h"
42 #if JOBS
43 #include "sgtty.h"
44 #undef CEOF /* syntax.h redefines this */
45 #endif
46 #include "main.h"
47 #include "parser.h"
48 #include "nodes.h"
49 #include "jobs.h"
50 #include "options.h"
51 #include "trap.h"
52 #include "signames.h"
53 #include "syntax.h"
54 #include "input.h"
55 #include "output.h"
56 #include "memalloc.h"
57 #include "error.h"
58 #include "mystring.h"
59 #include <fcntl.h>
60 #include <signal.h>
61 #include <errno.h>
62 #ifdef BSD
63 #include <sys/types.h>
64 #include <sys/wait.h>
65 #include <sys/time.h>
66 #include <sys/resource.h>
67 #endif
68
69
70
71 struct job *jobtab; /* array of jobs */
72 int njobs; /* size of array */
73 MKINIT short backgndpid = -1; /* pid of last background process */
74 #if JOBS
75 int initialpgrp; /* pgrp of shell on invocation */
76 short curjob; /* current job */
77 #endif
78
79 #ifdef __STDC__
80 STATIC void restartjob(struct job *);
81 STATIC struct job *getjob(char *);
82 STATIC void freejob(struct job *);
83 STATIC int procrunning(int);
84 STATIC int dowait(int, struct job *);
85 STATIC int waitproc(int, int *);
86 STATIC char *commandtext(union node *);
87 #else
88 STATIC void restartjob();
89 STATIC struct job *getjob();
90 STATIC void freejob();
91 STATIC int procrunning();
92 STATIC int dowait();
93 STATIC int waitproc();
94 STATIC char *commandtext();
95 #endif
96
97
98
99 #if JOBS
100 /*
101 * Turn job control on and off.
102 *
103 * Note: This code assumes that the third arg to ioctl is a character
104 * pointer, which is true on Berkeley systems but not System V. Since
105 * System V doesn't have job control yet, this isn't a problem now.
106 */
107
108 MKINIT int jobctl;
109
110 void
111 setjobctl(on) {
112 int ldisc;
113
114 if (on == jobctl || rootshell == 0)
115 return;
116 if (on) {
117 do { /* while we are in the background */
118 if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) {
119 out2str("ash: can't access tty; job control turned off\n");
120 jflag = 0;
121 return;
122 }
123 if (initialpgrp == -1)
124 initialpgrp = getpgrp(0);
125 else if (initialpgrp != getpgrp(0)) {
126 killpg(initialpgrp, SIGTTIN);
127 continue;
128 }
129 } while (0);
130 if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
131 out2str("ash: need new tty driver to run job control; job control turned off\n");
132 jflag = 0;
133 return;
134 }
135 setsignal(SIGTSTP);
136 setsignal(SIGTTOU);
137 setpgrp(0, rootpid);
138 ioctl(2, TIOCSPGRP, (char *)&rootpid);
139 } else { /* turning job control off */
140 setpgrp(0, initialpgrp);
141 ioctl(2, TIOCSPGRP, (char *)&initialpgrp);
142 setsignal(SIGTSTP);
143 setsignal(SIGTTOU);
144 }
145 jobctl = on;
146 }
147 #endif
148
149
150 #ifdef mkinit
151
152 SHELLPROC {
153 backgndpid = -1;
154 #if JOBS
155 jobctl = 0;
156 #endif
157 }
158
159 #endif
160
161
162
163 #if JOBS
164 fgcmd(argc, argv) char **argv; {
165 struct job *jp;
166 int pgrp;
167 int status;
168
169 jp = getjob(argv[1]);
170 if (jp->jobctl == 0)
171 error("job not created under job control");
172 pgrp = jp->ps[0].pid;
173 ioctl(2, TIOCSPGRP, (char *)&pgrp);
174 restartjob(jp);
175 INTOFF;
176 status = waitforjob(jp);
177 INTON;
178 return status;
179 }
180
181
182 bgcmd(argc, argv) char **argv; {
183 struct job *jp;
184
185 do {
186 jp = getjob(*++argv);
187 if (jp->jobctl == 0)
188 error("job not created under job control");
189 restartjob(jp);
190 } while (--argc > 1);
191 return 0;
192 }
193
194
195 STATIC void
196 restartjob(jp)
197 struct job *jp;
198 {
199 struct procstat *ps;
200 int i;
201
202 if (jp->state == JOBDONE)
203 return;
204 INTOFF;
205 killpg(jp->ps[0].pid, SIGCONT);
206 for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
207 if ((ps->status & 0377) == 0177) {
208 ps->status = -1;
209 jp->state = 0;
210 }
211 }
212 INTON;
213 }
214 #endif
215
216
217 int
218 jobscmd(argc, argv) char **argv; {
219 showjobs(0);
220 return 0;
221 }
222
223
224 /*
225 * Print a list of jobs. If "change" is nonzero, only print jobs whose
226 * statuses have changed since the last call to showjobs.
227 *
228 * If the shell is interrupted in the process of creating a job, the
229 * result may be a job structure containing zero processes. Such structures
230 * will be freed here.
231 */
232
233 void
234 showjobs(change) {
235 int jobno;
236 int procno;
237 int i;
238 struct job *jp;
239 struct procstat *ps;
240 int col;
241 char s[64];
242
243 TRACE(("showjobs(%d) called\n", change));
244 while (dowait(0, (struct job *)NULL) > 0);
245 for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
246 if (! jp->used)
247 continue;
248 if (jp->nprocs == 0) {
249 freejob(jp);
250 continue;
251 }
252 if (change && ! jp->changed)
253 continue;
254 procno = jp->nprocs;
255 for (ps = jp->ps ; ; ps++) { /* for each process */
256 if (ps == jp->ps)
257 fmtstr(s, 64, "[%d] %d ", jobno, ps->pid);
258 else
259 fmtstr(s, 64, " %d ", ps->pid);
260 out1str(s);
261 col = strlen(s);
262 s[0] = '\0';
263 if (ps->status == -1) {
264 /* don't print anything */
265 } else if ((ps->status & 0xFF) == 0) {
266 fmtstr(s, 64, "Exit %d", ps->status >> 8);
267 } else {
268 i = ps->status;
269 #if JOBS
270 if ((i & 0xFF) == 0177)
271 i >>= 8;
272 #endif
273 if ((i & 0x7F) <= MAXSIG && sigmesg[i & 0x7F])
274 scopy(sigmesg[i & 0x7F], s);
275 else
276 fmtstr(s, 64, "Signal %d", i & 0x7F);
277 if (i & 0x80)
278 strcat(s, " (core dumped)");
279 }
280 out1str(s);
281 col += strlen(s);
282 do {
283 out1c(' ');
284 col++;
285 } while (col < 30);
286 out1str(ps->cmd);
287 out1c('\n');
288 if (--procno <= 0)
289 break;
290 }
291 jp->changed = 0;
292 if (jp->state == JOBDONE) {
293 freejob(jp);
294 }
295 }
296 }
297
298
299 /*
300 * Mark a job structure as unused.
301 */
302
303 STATIC void
304 freejob(jp)
305 struct job *jp;
306 {
307 struct procstat *ps;
308 int i;
309
310 INTOFF;
311 for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
312 if (ps->cmd != nullstr)
313 ckfree(ps->cmd);
314 }
315 if (jp->ps != &jp->ps0)
316 ckfree(jp->ps);
317 jp->used = 0;
318 #if JOBS
319 if (curjob == jp - jobtab + 1)
320 curjob = 0;
321 #endif
322 INTON;
323 }
324
325
326
327 int
328 waitcmd(argc, argv) char **argv; {
329 struct job *job;
330 int status;
331 struct job *jp;
332
333 if (argc > 1) {
334 job = getjob(argv[1]);
335 } else {
336 job = NULL;
337 }
338 for (;;) { /* loop until process terminated or stopped */
339 if (job != NULL) {
340 if (job->state) {
341 status = job->ps[job->nprocs - 1].status;
342 if ((status & 0xFF) == 0)
343 status = status >> 8 & 0xFF;
344 #if JOBS
345 else if ((status & 0xFF) == 0177)
346 status = (status >> 8 & 0x7F) + 128;
347 #endif
348 else
349 status = (status & 0x7F) + 128;
350 if (! iflag)
351 freejob(job);
352 return status;
353 }
354 } else {
355 for (jp = jobtab ; ; jp++) {
356 if (jp >= jobtab + njobs) { /* no running procs */
357 return 0;
358 }
359 if (jp->used && jp->state == 0)
360 break;
361 }
362 }
363 dowait(1, (struct job *)NULL);
364 }
365 }
366
367
368
369 jobidcmd(argc, argv) char **argv; {
370 struct job *jp;
371 int i;
372
373 jp = getjob(argv[1]);
374 for (i = 0 ; i < jp->nprocs ; ) {
375 out1fmt("%d", jp->ps[i].pid);
376 out1c(++i < jp->nprocs? ' ' : '\n');
377 }
378 return 0;
379 }
380
381
382
383 /*
384 * Convert a job name to a job structure.
385 */
386
387 STATIC struct job *
388 getjob(name)
389 char *name;
390 {
391 int jobno;
392 register struct job *jp;
393 int pid;
394 int i;
395
396 if (name == NULL) {
397 #if JOBS
398 currentjob:
399 if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0)
400 error("No current job");
401 return &jobtab[jobno - 1];
402 #else
403 error("No current job");
404 #endif
405 } else if (name[0] == '%') {
406 if (is_digit(name[1])) {
407 jobno = number(name + 1);
408 if (jobno > 0 && jobno <= njobs
409 && jobtab[jobno - 1].used != 0)
410 return &jobtab[jobno - 1];
411 #if JOBS
412 } else if (name[1] == '%' && name[2] == '\0') {
413 goto currentjob;
414 #endif
415 } else {
416 register struct job *found = NULL;
417 for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
418 if (jp->used && jp->nprocs > 0
419 && prefix(name + 1, jp->ps[0].cmd)) {
420 if (found)
421 error("%s: ambiguous", name);
422 found = jp;
423 }
424 }
425 if (found)
426 return found;
427 }
428 } else if (is_number(name)) {
429 pid = number(name);
430 for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
431 if (jp->used && jp->nprocs > 0
432 && jp->ps[jp->nprocs - 1].pid == pid)
433 return jp;
434 }
435 }
436 error("No such job: %s", name);
437 }
438
439
440
441 /*
442 * Return a new job structure,
443 */
444
445 struct job *
446 makejob(node, nprocs)
447 union node *node;
448 {
449 int i;
450 struct job *jp;
451
452 for (i = njobs, jp = jobtab ; ; jp++) {
453 if (--i < 0) {
454 INTOFF;
455 if (njobs == 0) {
456 jobtab = ckmalloc(4 * sizeof jobtab[0]);
457 } else {
458 jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
459 bcopy(jobtab, jp, njobs * sizeof jp[0]);
460 ckfree(jobtab);
461 jobtab = jp;
462 }
463 jp = jobtab + njobs;
464 for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0);
465 INTON;
466 break;
467 }
468 if (jp->used == 0)
469 break;
470 }
471 INTOFF;
472 jp->state = 0;
473 jp->used = 1;
474 jp->changed = 0;
475 jp->nprocs = 0;
476 #if JOBS
477 jp->jobctl = jobctl;
478 #endif
479 if (nprocs > 1) {
480 jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
481 } else {
482 jp->ps = &jp->ps0;
483 }
484 INTON;
485 TRACE(("makejob(0x%x, %d) returns %%%d\n", (int)node, nprocs, jp - jobtab + 1));
486 return jp;
487 }
488
489
490 /*
491 * Fork of a subshell. If we are doing job control, give the subshell its
492 * own process group. Jp is a job structure that the job is to be added to.
493 * N is the command that will be evaluated by the child. Both jp and n may
494 * be NULL. The mode parameter can be one of the following:
495 * FORK_FG - Fork off a foreground process.
496 * FORK_BG - Fork off a background process.
497 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
498 * process group even if job control is on.
499 *
500 * When job control is turned off, background processes have their standard
501 * input redirected to /dev/null (except for the second and later processes
502 * in a pipeline).
503 */
504
505 int
506 forkshell(jp, n, mode)
507 union node *n;
508 struct job *jp;
509 {
510 int pid;
511 int pgrp;
512
513 TRACE(("forkshell(%%%d, 0x%x, %d) called\n", jp - jobtab, (int)n, mode));
514 INTOFF;
515 pid = fork();
516 if (pid == -1) {
517 TRACE(("Fork failed, errno=%d\n", errno));
518 INTON;
519 error("Cannot fork");
520 }
521 if (pid == 0) {
522 struct job *p;
523 int wasroot;
524 int i;
525
526 TRACE(("Child shell %d\n", getpid()));
527 wasroot = rootshell;
528 rootshell = 0;
529 for (i = njobs, p = jobtab ; --i >= 0 ; p++)
530 if (p->used)
531 freejob(p);
532 closescript();
533 INTON;
534 clear_traps();
535 #if JOBS
536 jobctl = 0; /* do job control only in root shell */
537 if (wasroot && mode != FORK_NOJOB && jflag) {
538 if (jp == NULL || jp->nprocs == 0)
539 pgrp = getpid();
540 else
541 pgrp = jp->ps[0].pid;
542 setpgrp(0, pgrp);
543 if (mode == FORK_FG) {
544 /*** this causes superfluous TIOCSPGRPS ***/
545 if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0)
546 error("TIOCSPGRP failed, errno=%d\n", errno);
547 }
548 setsignal(SIGTSTP);
549 setsignal(SIGTTOU);
550 } else if (mode == FORK_BG) {
551 ignoresig(SIGINT);
552 ignoresig(SIGQUIT);
553 if (jp == NULL || jp->nprocs == 0) {
554 close(0);
555 if (open("/dev/null", O_RDONLY) != 0)
556 error("Can't open /dev/null");
557 }
558 }
559 #else
560 if (mode == FORK_BG) {
561 ignoresig(SIGINT);
562 ignoresig(SIGQUIT);
563 if (jp == NULL || jp->nprocs == 0) {
564 close(0);
565 if (open("/dev/null", O_RDONLY) != 0)
566 error("Can't open /dev/null");
567 }
568 }
569 #endif
570 if (wasroot && iflag) {
571 setsignal(SIGINT);
572 setsignal(SIGQUIT);
573 setsignal(SIGTERM);
574 }
575 return pid;
576 }
577 if (rootshell && mode != FORK_NOJOB && jflag) {
578 if (jp == NULL || jp->nprocs == 0)
579 pgrp = pid;
580 else
581 pgrp = jp->ps[0].pid;
582 setpgrp(pid, pgrp);
583 }
584 if (mode == FORK_BG)
585 backgndpid = pid; /* set $! */
586 if (jp) {
587 struct procstat *ps = &jp->ps[jp->nprocs++];
588 ps->pid = pid;
589 ps->status = -1;
590 ps->cmd = nullstr;
591 if (iflag && rootshell && n)
592 ps->cmd = commandtext(n);
593 }
594 INTON;
595 TRACE(("In parent shell: child = %d\n", pid));
596 return pid;
597 }
598
599
600
601 /*
602 * Wait for job to finish.
603 *
604 * Under job control we have the problem that while a child process is
605 * running interrupts generated by the user are sent to the child but not
606 * to the shell. This means that an infinite loop started by an inter-
607 * active user may be hard to kill. With job control turned off, an
608 * interactive user may place an interactive program inside a loop. If
609 * the interactive program catches interrupts, the user doesn't want
610 * these interrupts to also abort the loop. The approach we take here
611 * is to have the shell ignore interrupt signals while waiting for a
612 * forground process to terminate, and then send itself an interrupt
613 * signal if the child process was terminated by an interrupt signal.
614 * Unfortunately, some programs want to do a bit of cleanup and then
615 * exit on interrupt; unless these processes terminate themselves by
616 * sending a signal to themselves (instead of calling exit) they will
617 * confuse this approach.
618 */
619
620 int
621 waitforjob(jp)
622 register struct job *jp;
623 {
624 #if JOBS
625 int mypgrp = getpgrp(0);
626 #endif
627 int status;
628 int st;
629
630 INTOFF;
631 TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
632 while (jp->state == 0) {
633 dowait(1, jp);
634 }
635 #if JOBS
636 if (jp->jobctl) {
637 if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0)
638 error("TIOCSPGRP failed, errno=%d\n", errno);
639 }
640 if (jp->state == JOBSTOPPED)
641 curjob = jp - jobtab + 1;
642 #endif
643 status = jp->ps[jp->nprocs - 1].status;
644 /* convert to 8 bits */
645 if ((status & 0xFF) == 0)
646 st = status >> 8 & 0xFF;
647 #if JOBS
648 else if ((status & 0xFF) == 0177)
649 st = (status >> 8 & 0x7F) + 128;
650 #endif
651 else
652 st = (status & 0x7F) + 128;
653 if (! JOBS || jp->state == JOBDONE)
654 freejob(jp);
655 CLEAR_PENDING_INT;
656 if ((status & 0x7F) == SIGINT)
657 kill(getpid(), SIGINT);
658 INTON;
659 return st;
660 }
661
662
663
664 /*
665 * Wait for a process to terminate.
666 */
667
668 STATIC int
669 dowait(block, job)
670 struct job *job;
671 {
672 int pid;
673 int status;
674 struct procstat *sp;
675 struct job *jp;
676 struct job *thisjob;
677 int done;
678 int stopped;
679 int core;
680
681 TRACE(("dowait(%d) called\n", block));
682 do {
683 pid = waitproc(block, &status);
684 TRACE(("wait returns %d, status=%d\n", pid, status));
685 } while (pid == -1 && errno == EINTR);
686 if (pid <= 0)
687 return pid;
688 INTOFF;
689 thisjob = NULL;
690 for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
691 if (jp->used) {
692 done = 1;
693 stopped = 1;
694 for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
695 if (sp->pid == -1)
696 continue;
697 if (sp->pid == pid) {
698 TRACE(("Changin status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status));
699 sp->status = status;
700 thisjob = jp;
701 }
702 if (sp->status == -1)
703 stopped = 0;
704 else if ((sp->status & 0377) == 0177)
705 done = 0;
706 }
707 if (stopped) { /* stopped or done */
708 int state = done? JOBDONE : JOBSTOPPED;
709 if (jp->state != state) {
710 TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
711 jp->state = state;
712 #if JOBS
713 if (done && curjob == jp - jobtab + 1)
714 curjob = 0; /* no current job */
715 #endif
716 }
717 }
718 }
719 }
720 INTON;
721 if (! rootshell || ! iflag || (job && thisjob == job)) {
722 #if JOBS
723 if ((status & 0xFF) == 0177)
724 status >>= 8;
725 #endif
726 core = status & 0x80;
727 status &= 0x7F;
728 if (status != 0 && status != SIGINT && status != SIGPIPE) {
729 if (thisjob != job)
730 outfmt(out2, "%d: ", pid);
731 #if JOBS
732 if (status == SIGTSTP && rootshell && iflag)
733 outfmt(out2, "%%%d ", job - jobtab + 1);
734 #endif
735 if (status <= MAXSIG && sigmesg[status])
736 out2str(sigmesg[status]);
737 else
738 outfmt(out2, "Signal %d", status);
739 if (core)
740 out2str(" - core dumped");
741 out2c('\n');
742 flushout(&errout);
743 } else {
744 TRACE(("Not printing status: status=%d\n", status));
745 }
746 } else {
747 TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job));
748 if (thisjob)
749 thisjob->changed = 1;
750 }
751 return pid;
752 }
753
754
755
756 /*
757 * Do a wait system call. If job control is compiled in, we accept
758 * stopped processes. If block is zero, we return a value of zero
759 * rather than blocking.
760 *
761 * System V doesn't have a non-blocking wait system call. It does
762 * have a SIGCLD signal that is sent to a process when one of it's
763 * children dies. The obvious way to use SIGCLD would be to install
764 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
765 * was received, and have waitproc bump another counter when it got
766 * the status of a process. Waitproc would then know that a wait
767 * system call would not block if the two counters were different.
768 * This approach doesn't work because if a process has children that
769 * have not been waited for, System V will send it a SIGCLD when it
770 * installs a signal handler for SIGCLD. What this means is that when
771 * a child exits, the shell will be sent SIGCLD signals continuously
772 * until is runs out of stack space, unless it does a wait call before
773 * restoring the signal handler. The code below takes advantage of
774 * this (mis)feature by installing a signal handler for SIGCLD and
775 * then checking to see whether it was called. If there are any
776 * children to be waited for, it will be.
777 *
778 * If neither SYSV nor BSD is defined, we don't implement nonblocking
779 * waits at all. In this case, the user will not be informed when
780 * a background process until the next time she runs a real program
781 * (as opposed to running a builtin command or just typing return),
782 * and the jobs command may give out of date information.
783 */
784
785 #ifdef SYSV
786 STATIC int gotsigchild;
787
788 STATIC int onsigchild() {
789 gotsigchild = 1;
790 }
791 #endif
792
793
794 STATIC int
795 waitproc(block, status)
796 int *status;
797 {
798 #ifdef BSD
799 int flags;
800
801 #if JOBS
802 flags = WUNTRACED;
803 #else
804 flags = 0;
805 #endif
806 if (block == 0)
807 flags |= WNOHANG;
808 return wait3((union wait *)status, flags, (struct rusage *)NULL);
809 #else
810 #ifdef SYSV
811 int (*save)();
812
813 if (block == 0) {
814 gotsigchild = 0;
815 save = signal(SIGCLD, onsigchild);
816 signal(SIGCLD, save);
817 if (gotsigchild == 0)
818 return 0;
819 }
820 return wait(status);
821 #else
822 if (block == 0)
823 return 0;
824 return wait(status);
825 #endif
826 #endif
827 }
828
829
830
831 /*
832 * Return a string identifying a command (to be printed by the
833 * jobs command.
834 */
835
836 STATIC char *cmdnextc;
837 STATIC int cmdnleft;
838 STATIC void cmdtxt(), cmdputs();
839
840 STATIC char *
841 commandtext(n)
842 union node *n;
843 {
844 char *name;
845
846 cmdnextc = name = ckmalloc(50);
847 cmdnleft = 50 - 4;
848 cmdtxt(n);
849 *cmdnextc = '\0';
850 return name;
851 }
852
853
854 STATIC void
855 cmdtxt(n)
856 union node *n;
857 {
858 union node *np;
859 struct nodelist *lp;
860 char *p;
861 int i;
862 char s[2];
863
864 switch (n->type) {
865 case NSEMI:
866 cmdtxt(n->nbinary.ch1);
867 cmdputs("; ");
868 cmdtxt(n->nbinary.ch2);
869 break;
870 case NAND:
871 cmdtxt(n->nbinary.ch1);
872 cmdputs(" && ");
873 cmdtxt(n->nbinary.ch2);
874 break;
875 case NOR:
876 cmdtxt(n->nbinary.ch1);
877 cmdputs(" || ");
878 cmdtxt(n->nbinary.ch2);
879 break;
880 case NPIPE:
881 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
882 cmdtxt(lp->n);
883 if (lp->next)
884 cmdputs(" | ");
885 }
886 break;
887 case NSUBSHELL:
888 cmdputs("(");
889 cmdtxt(n->nredir.n);
890 cmdputs(")");
891 break;
892 case NREDIR:
893 case NBACKGND:
894 cmdtxt(n->nredir.n);
895 break;
896 case NIF:
897 cmdputs("if ");
898 cmdtxt(n->nif.test);
899 cmdputs("; then ");
900 cmdtxt(n->nif.ifpart);
901 cmdputs("...");
902 break;
903 case NWHILE:
904 cmdputs("while ");
905 goto until;
906 case NUNTIL:
907 cmdputs("until ");
908 until:
909 cmdtxt(n->nbinary.ch1);
910 cmdputs("; do ");
911 cmdtxt(n->nbinary.ch2);
912 cmdputs("; done");
913 break;
914 case NFOR:
915 cmdputs("for ");
916 cmdputs(n->nfor.var);
917 cmdputs(" in ...");
918 break;
919 case NCASE:
920 cmdputs("case ");
921 cmdputs(n->ncase.expr->narg.text);
922 cmdputs(" in ...");
923 break;
924 case NDEFUN:
925 cmdputs(n->narg.text);
926 cmdputs("() ...");
927 break;
928 case NCMD:
929 for (np = n->ncmd.args ; np ; np = np->narg.next) {
930 cmdtxt(np);
931 if (np->narg.next)
932 cmdputs(" ");
933 }
934 for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
935 cmdputs(" ");
936 cmdtxt(np);
937 }
938 break;
939 case NARG:
940 cmdputs(n->narg.text);
941 break;
942 case NTO:
943 p = ">"; i = 1; goto redir;
944 case NAPPEND:
945 p = ">>"; i = 1; goto redir;
946 case NTOFD:
947 p = ">&"; i = 1; goto redir;
948 case NFROM:
949 p = "<"; i = 0; goto redir;
950 case NFROMFD:
951 p = "<&"; i = 0; goto redir;
952 redir:
953 if (n->nfile.fd != i) {
954 s[0] = n->nfile.fd + '0';
955 s[1] = '\0';
956 cmdputs(s);
957 }
958 cmdputs(p);
959 if (n->type == NTOFD || n->type == NFROMFD) {
960 s[0] = n->ndup.dupfd + '0';
961 s[1] = '\0';
962 cmdputs(s);
963 } else {
964 cmdtxt(n->nfile.fname);
965 }
966 break;
967 case NHERE:
968 case NXHERE:
969 cmdputs("<<...");
970 break;
971 default:
972 cmdputs("???");
973 break;
974 }
975 }
976
977
978
979 STATIC void
980 cmdputs(s)
981 char *s;
982 {
983 register char *p, *q;
984 register char c;
985 int subtype = 0;
986
987 if (cmdnleft <= 0)
988 return;
989 p = s;
990 q = cmdnextc;
991 while ((c = *p++) != '\0') {
992 if (c == CTLESC)
993 *q++ = *p++;
994 else if (c == CTLVAR) {
995 *q++ = '$';
996 if (--cmdnleft > 0)
997 *q++ = '{';
998 subtype = *p++;
999 } else if (c == '=' && subtype != 0) {
1000 *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
1001 subtype = 0;
1002 } else if (c == CTLENDVAR) {
1003 *q++ = '}';
1004 } else if (c == CTLBACKQ | c == CTLBACKQ+CTLQUOTE)
1005 cmdnleft++; /* ignore it */
1006 else
1007 *q++ = c;
1008 if (--cmdnleft <= 0) {
1009 *q++ = '.';
1010 *q++ = '.';
1011 *q++ = '.';
1012 break;
1013 }
1014 }
1015 cmdnextc = q;
1016 }
1017