source: trunk/minix/commands/ash/jobs.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

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