Contents of /trunk/mkinitrd-magellan/klibc/usr/dash/jobs.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 29099 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 29099 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | /*- |
2 | * Copyright (c) 1991, 1993 |
3 | * The Regents of the University of California. All rights reserved. |
4 | * Copyright (c) 1997-2005 |
5 | * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to Berkeley by |
8 | * Kenneth Almquist. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * 3. Neither the name of the University nor the names of its contributors |
19 | * may be used to endorse or promote products derived from this software |
20 | * without specific prior written permission. |
21 | * |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
32 | * SUCH DAMAGE. |
33 | */ |
34 | |
35 | #include <fcntl.h> |
36 | #include <signal.h> |
37 | #include <unistd.h> |
38 | #include <stdlib.h> |
39 | #include <paths.h> |
40 | #include <sys/types.h> |
41 | #include <sys/param.h> |
42 | #ifdef BSD |
43 | #include <sys/wait.h> |
44 | #include <sys/time.h> |
45 | #include <sys/resource.h> |
46 | #endif |
47 | #include <sys/ioctl.h> |
48 | |
49 | #include "shell.h" |
50 | #if JOBS |
51 | #include <termios.h> |
52 | #undef CEOF /* syntax.h redefines this */ |
53 | #endif |
54 | #include "redir.h" |
55 | #include "show.h" |
56 | #include "main.h" |
57 | #include "parser.h" |
58 | #include "nodes.h" |
59 | #include "jobs.h" |
60 | #include "options.h" |
61 | #include "trap.h" |
62 | #include "syntax.h" |
63 | #include "input.h" |
64 | #include "output.h" |
65 | #include "memalloc.h" |
66 | #include "error.h" |
67 | #include "mystring.h" |
68 | #include "system.h" |
69 | |
70 | /* mode flags for set_curjob */ |
71 | #define CUR_DELETE 2 |
72 | #define CUR_RUNNING 1 |
73 | #define CUR_STOPPED 0 |
74 | |
75 | /* mode flags for dowait */ |
76 | #define DOWAIT_NORMAL 0 |
77 | #define DOWAIT_BLOCK 1 |
78 | |
79 | /* array of jobs */ |
80 | static struct job *jobtab; |
81 | /* size of array */ |
82 | static unsigned njobs; |
83 | /* pid of last background process */ |
84 | pid_t backgndpid; |
85 | |
86 | #if JOBS |
87 | /* pgrp of shell on invocation */ |
88 | static int initialpgrp; |
89 | /* control terminal */ |
90 | static int ttyfd = -1; |
91 | #endif |
92 | |
93 | /* current job */ |
94 | static struct job *curjob; |
95 | /* number of presumed living untracked jobs */ |
96 | static int jobless; |
97 | |
98 | STATIC void set_curjob(struct job *, unsigned); |
99 | STATIC int jobno(const struct job *); |
100 | STATIC int sprint_status(char *, int, int); |
101 | STATIC void freejob(struct job *); |
102 | STATIC struct job *getjob(const char *, int); |
103 | STATIC struct job *growjobtab(void); |
104 | STATIC void forkchild(struct job *, union node *, int); |
105 | STATIC void forkparent(struct job *, union node *, int, pid_t); |
106 | STATIC int dowait(int, struct job *); |
107 | #ifdef SYSV |
108 | STATIC int onsigchild(void); |
109 | #endif |
110 | STATIC int waitproc(int, int *); |
111 | STATIC char *commandtext(union node *); |
112 | STATIC void cmdtxt(union node *); |
113 | STATIC void cmdlist(union node *, int); |
114 | STATIC void cmdputs(const char *); |
115 | STATIC void showpipe(struct job *, struct output *); |
116 | STATIC int getstatus(struct job *); |
117 | |
118 | #if JOBS |
119 | static int restartjob(struct job *, int); |
120 | static void xtcsetpgrp(int, pid_t); |
121 | #endif |
122 | |
123 | STATIC void |
124 | set_curjob(struct job *jp, unsigned mode) |
125 | { |
126 | struct job *jp1; |
127 | struct job **jpp, **curp; |
128 | |
129 | /* first remove from list */ |
130 | jpp = curp = &curjob; |
131 | do { |
132 | jp1 = *jpp; |
133 | if (jp1 == jp) |
134 | break; |
135 | jpp = &jp1->prev_job; |
136 | } while (1); |
137 | *jpp = jp1->prev_job; |
138 | |
139 | /* Then re-insert in correct position */ |
140 | jpp = curp; |
141 | switch (mode) { |
142 | default: |
143 | #ifdef DEBUG |
144 | abort(); |
145 | #endif |
146 | case CUR_DELETE: |
147 | /* job being deleted */ |
148 | break; |
149 | case CUR_RUNNING: |
150 | /* newly created job or backgrounded job, |
151 | put after all stopped jobs. */ |
152 | do { |
153 | jp1 = *jpp; |
154 | if (!JOBS || !jp1 || jp1->state != JOBSTOPPED) |
155 | break; |
156 | jpp = &jp1->prev_job; |
157 | } while (1); |
158 | /* FALLTHROUGH */ |
159 | #if JOBS |
160 | case CUR_STOPPED: |
161 | #endif |
162 | /* newly stopped job - becomes curjob */ |
163 | jp->prev_job = *jpp; |
164 | *jpp = jp; |
165 | break; |
166 | } |
167 | } |
168 | |
169 | #if JOBS |
170 | /* |
171 | * Turn job control on and off. |
172 | * |
173 | * Note: This code assumes that the third arg to ioctl is a character |
174 | * pointer, which is true on Berkeley systems but not System V. Since |
175 | * System V doesn't have job control yet, this isn't a problem now. |
176 | * |
177 | * Called with interrupts off. |
178 | */ |
179 | |
180 | int jobctl; |
181 | |
182 | void |
183 | setjobctl(int on) |
184 | { |
185 | int fd; |
186 | int pgrp; |
187 | |
188 | if (on == jobctl || rootshell == 0) |
189 | return; |
190 | if (on) { |
191 | int ofd; |
192 | ofd = fd = open(_PATH_TTY, O_RDWR); |
193 | if (fd < 0) { |
194 | fd += 3; |
195 | while (!isatty(fd) && --fd >= 0) |
196 | ; |
197 | } |
198 | fd = fcntl(fd, F_DUPFD, 10); |
199 | close(ofd); |
200 | if (fd < 0) |
201 | goto out; |
202 | fcntl(fd, F_SETFD, FD_CLOEXEC); |
203 | do { /* while we are in the background */ |
204 | if ((pgrp = tcgetpgrp(fd)) < 0) { |
205 | out: |
206 | sh_warnx("can't access tty; job control turned off"); |
207 | mflag = on = 0; |
208 | goto close; |
209 | } |
210 | if (pgrp == getpgrp()) |
211 | break; |
212 | killpg(0, SIGTTIN); |
213 | } while (1); |
214 | initialpgrp = pgrp; |
215 | |
216 | setsignal(SIGTSTP); |
217 | setsignal(SIGTTOU); |
218 | setsignal(SIGTTIN); |
219 | pgrp = rootpid; |
220 | setpgid(0, pgrp); |
221 | xtcsetpgrp(fd, pgrp); |
222 | } else { |
223 | /* turning job control off */ |
224 | fd = ttyfd; |
225 | pgrp = initialpgrp; |
226 | xtcsetpgrp(fd, pgrp); |
227 | setpgid(0, pgrp); |
228 | setsignal(SIGTSTP); |
229 | setsignal(SIGTTOU); |
230 | setsignal(SIGTTIN); |
231 | close: |
232 | close(fd); |
233 | fd = -1; |
234 | } |
235 | ttyfd = fd; |
236 | jobctl = on; |
237 | } |
238 | #endif |
239 | |
240 | |
241 | int |
242 | killcmd(argc, argv) |
243 | int argc; |
244 | char **argv; |
245 | { |
246 | int signo = -1; |
247 | int list = 0; |
248 | int i; |
249 | pid_t pid; |
250 | struct job *jp; |
251 | |
252 | if (argc <= 1) { |
253 | usage: |
254 | sh_error( |
255 | "Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n" |
256 | "kill -l [exitstatus]" |
257 | ); |
258 | } |
259 | |
260 | if (**++argv == '-') { |
261 | signo = decode_signal(*argv + 1, 1); |
262 | if (signo < 0) { |
263 | int c; |
264 | |
265 | while ((c = nextopt("ls:")) != '\0') |
266 | switch (c) { |
267 | default: |
268 | #ifdef DEBUG |
269 | abort(); |
270 | #endif |
271 | case 'l': |
272 | list = 1; |
273 | break; |
274 | case 's': |
275 | signo = decode_signal(optionarg, 1); |
276 | if (signo < 0) { |
277 | sh_error( |
278 | "invalid signal number or name: %s", |
279 | optionarg |
280 | ); |
281 | } |
282 | break; |
283 | } |
284 | argv = argptr; |
285 | } else |
286 | argv++; |
287 | } |
288 | |
289 | if (!list && signo < 0) |
290 | signo = SIGTERM; |
291 | |
292 | if ((signo < 0 || !*argv) ^ list) { |
293 | goto usage; |
294 | } |
295 | |
296 | if (list) { |
297 | struct output *out; |
298 | |
299 | out = out1; |
300 | if (!*argv) { |
301 | outstr("0\n", out); |
302 | for (i = 1; i < NSIG; i++) { |
303 | outfmt(out, snlfmt, signal_name(i)); |
304 | } |
305 | return 0; |
306 | } |
307 | signo = number(*argv); |
308 | if (signo > 128) |
309 | signo -= 128; |
310 | if (0 < signo && signo < NSIG) |
311 | outfmt(out, snlfmt, signal_name(signo)); |
312 | else |
313 | sh_error("invalid signal number or exit status: %s", |
314 | *argv); |
315 | return 0; |
316 | } |
317 | |
318 | i = 0; |
319 | do { |
320 | if (**argv == '%') { |
321 | jp = getjob(*argv, 0); |
322 | pid = -jp->ps[0].pid; |
323 | } else |
324 | pid = **argv == '-' ? |
325 | -number(*argv + 1) : number(*argv); |
326 | if (kill(pid, signo) != 0) { |
327 | sh_warnx("%s\n", strerror(errno)); |
328 | i = 1; |
329 | } |
330 | } while (*++argv); |
331 | |
332 | return i; |
333 | } |
334 | |
335 | STATIC int |
336 | jobno(const struct job *jp) |
337 | { |
338 | return jp - jobtab + 1; |
339 | } |
340 | |
341 | #if JOBS |
342 | int |
343 | fgcmd(int argc, char **argv) |
344 | { |
345 | struct job *jp; |
346 | struct output *out; |
347 | int mode; |
348 | int retval; |
349 | |
350 | mode = (**argv == 'f') ? FORK_FG : FORK_BG; |
351 | nextopt(nullstr); |
352 | argv = argptr; |
353 | out = out1; |
354 | do { |
355 | jp = getjob(*argv, 1); |
356 | if (mode == FORK_BG) { |
357 | set_curjob(jp, CUR_RUNNING); |
358 | outfmt(out, "[%d] ", jobno(jp)); |
359 | } |
360 | outstr(jp->ps->cmd, out); |
361 | showpipe(jp, out); |
362 | retval = restartjob(jp, mode); |
363 | } while (*argv && *++argv); |
364 | return retval; |
365 | } |
366 | |
367 | int bgcmd(int, char **) __attribute__((__alias__("fgcmd"))); |
368 | |
369 | |
370 | STATIC int |
371 | restartjob(struct job *jp, int mode) |
372 | { |
373 | struct procstat *ps; |
374 | int i; |
375 | int status; |
376 | pid_t pgid; |
377 | |
378 | INTOFF; |
379 | if (jp->state == JOBDONE) |
380 | goto out; |
381 | jp->state = JOBRUNNING; |
382 | pgid = jp->ps->pid; |
383 | if (mode == FORK_FG) |
384 | xtcsetpgrp(ttyfd, pgid); |
385 | killpg(pgid, SIGCONT); |
386 | ps = jp->ps; |
387 | i = jp->nprocs; |
388 | do { |
389 | if (WIFSTOPPED(ps->status)) { |
390 | ps->status = -1; |
391 | } |
392 | } while (ps++, --i); |
393 | out: |
394 | status = (mode == FORK_FG) ? waitforjob(jp) : 0; |
395 | INTON; |
396 | return status; |
397 | } |
398 | #endif |
399 | |
400 | STATIC int |
401 | sprint_status(char *s, int status, int sigonly) |
402 | { |
403 | int col; |
404 | int st; |
405 | |
406 | col = 0; |
407 | st = WEXITSTATUS(status); |
408 | if (!WIFEXITED(status)) { |
409 | #if JOBS |
410 | st = WSTOPSIG(status); |
411 | if (!WIFSTOPPED(status)) |
412 | #endif |
413 | st = WTERMSIG(status); |
414 | if (sigonly) { |
415 | if (st == SIGINT || st == SIGPIPE) |
416 | goto out; |
417 | #if JOBS |
418 | if (WIFSTOPPED(status)) |
419 | goto out; |
420 | #endif |
421 | } |
422 | col = fmtstr(s, 32, strsignal(st)); |
423 | if (WCOREDUMP(status)) { |
424 | col += fmtstr(s + col, 16, " (core dumped)"); |
425 | } |
426 | } else if (!sigonly) { |
427 | if (st) |
428 | col = fmtstr(s, 16, "Done(%d)", st); |
429 | else |
430 | col = fmtstr(s, 16, "Done"); |
431 | } |
432 | |
433 | out: |
434 | return col; |
435 | } |
436 | |
437 | static void |
438 | showjob(struct output *out, struct job *jp, int mode) |
439 | { |
440 | struct procstat *ps; |
441 | struct procstat *psend; |
442 | int col; |
443 | int indent; |
444 | char s[80]; |
445 | |
446 | ps = jp->ps; |
447 | |
448 | if (mode & SHOW_PGID) { |
449 | /* just output process (group) id of pipeline */ |
450 | outfmt(out, "%d\n", ps->pid); |
451 | return; |
452 | } |
453 | |
454 | col = fmtstr(s, 16, "[%d] ", jobno(jp)); |
455 | indent = col; |
456 | |
457 | if (jp == curjob) |
458 | s[col - 2] = '+'; |
459 | else if (curjob && jp == curjob->prev_job) |
460 | s[col - 2] = '-'; |
461 | |
462 | if (mode & SHOW_PID) |
463 | col += fmtstr(s + col, 16, "%d ", ps->pid); |
464 | |
465 | psend = ps + jp->nprocs; |
466 | |
467 | if (jp->state == JOBRUNNING) { |
468 | scopy("Running", s + col); |
469 | col += strlen("Running"); |
470 | } else { |
471 | int status = psend[-1].status; |
472 | #if JOBS |
473 | if (jp->state == JOBSTOPPED) |
474 | status = jp->stopstatus; |
475 | #endif |
476 | col += sprint_status(s + col, status, 0); |
477 | } |
478 | |
479 | goto start; |
480 | |
481 | do { |
482 | /* for each process */ |
483 | col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3; |
484 | |
485 | start: |
486 | outfmt( |
487 | out, "%s%*c%s", |
488 | s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd |
489 | ); |
490 | if (!(mode & SHOW_PID)) { |
491 | showpipe(jp, out); |
492 | break; |
493 | } |
494 | if (++ps == psend) { |
495 | outcslow('\n', out); |
496 | break; |
497 | } |
498 | } while (1); |
499 | |
500 | jp->changed = 0; |
501 | |
502 | if (jp->state == JOBDONE) { |
503 | TRACE(("showjob: freeing job %d\n", jobno(jp))); |
504 | freejob(jp); |
505 | } |
506 | } |
507 | |
508 | |
509 | int |
510 | jobscmd(int argc, char **argv) |
511 | { |
512 | int mode, m; |
513 | struct output *out; |
514 | |
515 | mode = 0; |
516 | while ((m = nextopt("lp"))) |
517 | if (m == 'l') |
518 | mode = SHOW_PID; |
519 | else |
520 | mode = SHOW_PGID; |
521 | |
522 | out = out1; |
523 | argv = argptr; |
524 | if (*argv) |
525 | do |
526 | showjob(out, getjob(*argv,0), mode); |
527 | while (*++argv); |
528 | else |
529 | showjobs(out, mode); |
530 | |
531 | return 0; |
532 | } |
533 | |
534 | |
535 | /* |
536 | * Print a list of jobs. If "change" is nonzero, only print jobs whose |
537 | * statuses have changed since the last call to showjobs. |
538 | */ |
539 | |
540 | void |
541 | showjobs(struct output *out, int mode) |
542 | { |
543 | struct job *jp; |
544 | |
545 | TRACE(("showjobs(%x) called\n", mode)); |
546 | |
547 | /* If not even one one job changed, there is nothing to do */ |
548 | while (dowait(DOWAIT_NORMAL, NULL) > 0) |
549 | continue; |
550 | |
551 | for (jp = curjob; jp; jp = jp->prev_job) { |
552 | if (!(mode & SHOW_CHANGED) || jp->changed) |
553 | showjob(out, jp, mode); |
554 | } |
555 | } |
556 | |
557 | /* |
558 | * Mark a job structure as unused. |
559 | */ |
560 | |
561 | STATIC void |
562 | freejob(struct job *jp) |
563 | { |
564 | struct procstat *ps; |
565 | int i; |
566 | |
567 | INTOFF; |
568 | for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) { |
569 | if (ps->cmd != nullstr) |
570 | ckfree(ps->cmd); |
571 | } |
572 | if (jp->ps != &jp->ps0) |
573 | ckfree(jp->ps); |
574 | jp->used = 0; |
575 | set_curjob(jp, CUR_DELETE); |
576 | INTON; |
577 | } |
578 | |
579 | |
580 | |
581 | int |
582 | waitcmd(int argc, char **argv) |
583 | { |
584 | struct job *job; |
585 | int retval; |
586 | struct job *jp; |
587 | |
588 | EXSIGON(); |
589 | |
590 | nextopt(nullstr); |
591 | retval = 0; |
592 | |
593 | argv = argptr; |
594 | if (!*argv) { |
595 | /* wait for all jobs */ |
596 | for (;;) { |
597 | jp = curjob; |
598 | while (1) { |
599 | if (!jp) { |
600 | /* no running procs */ |
601 | goto out; |
602 | } |
603 | if (jp->state == JOBRUNNING) |
604 | break; |
605 | jp->waited = 1; |
606 | jp = jp->prev_job; |
607 | } |
608 | dowait(DOWAIT_BLOCK, 0); |
609 | } |
610 | } |
611 | |
612 | retval = 127; |
613 | do { |
614 | if (**argv != '%') { |
615 | pid_t pid = number(*argv); |
616 | job = curjob; |
617 | goto start; |
618 | do { |
619 | if (job->ps[job->nprocs - 1].pid == pid) |
620 | break; |
621 | job = job->prev_job; |
622 | start: |
623 | if (!job) |
624 | goto repeat; |
625 | } while (1); |
626 | } else |
627 | job = getjob(*argv, 0); |
628 | /* loop until process terminated or stopped */ |
629 | while (job->state == JOBRUNNING) |
630 | dowait(DOWAIT_BLOCK, 0); |
631 | job->waited = 1; |
632 | retval = getstatus(job); |
633 | repeat: |
634 | ; |
635 | } while (*++argv); |
636 | |
637 | out: |
638 | return retval; |
639 | } |
640 | |
641 | |
642 | |
643 | /* |
644 | * Convert a job name to a job structure. |
645 | */ |
646 | |
647 | STATIC struct job * |
648 | getjob(const char *name, int getctl) |
649 | { |
650 | struct job *jp; |
651 | struct job *found; |
652 | const char *err_msg = "No such job: %s"; |
653 | unsigned num; |
654 | int c; |
655 | const char *p; |
656 | char *(*match)(const char *, const char *); |
657 | |
658 | jp = curjob; |
659 | p = name; |
660 | if (!p) |
661 | goto currentjob; |
662 | |
663 | if (*p != '%') |
664 | goto err; |
665 | |
666 | c = *++p; |
667 | if (!c) |
668 | goto currentjob; |
669 | |
670 | if (!p[1]) { |
671 | if (c == '+' || c == '%') { |
672 | currentjob: |
673 | err_msg = "No current job"; |
674 | goto check; |
675 | } else if (c == '-') { |
676 | if (jp) |
677 | jp = jp->prev_job; |
678 | err_msg = "No previous job"; |
679 | check: |
680 | if (!jp) |
681 | goto err; |
682 | goto gotit; |
683 | } |
684 | } |
685 | |
686 | if (is_number(p)) { |
687 | num = atoi(p); |
688 | if (num < njobs) { |
689 | jp = jobtab + num - 1; |
690 | if (jp->used) |
691 | goto gotit; |
692 | goto err; |
693 | } |
694 | } |
695 | |
696 | match = prefix; |
697 | if (*p == '?') { |
698 | match = strstr; |
699 | p++; |
700 | } |
701 | |
702 | found = 0; |
703 | while (1) { |
704 | if (!jp) |
705 | goto err; |
706 | if (match(jp->ps[0].cmd, p)) { |
707 | if (found) |
708 | goto err; |
709 | found = jp; |
710 | err_msg = "%s: ambiguous"; |
711 | } |
712 | jp = jp->prev_job; |
713 | } |
714 | |
715 | gotit: |
716 | #if JOBS |
717 | err_msg = "job %s not created under job control"; |
718 | if (getctl && jp->jobctl == 0) |
719 | goto err; |
720 | #endif |
721 | return jp; |
722 | err: |
723 | sh_error(err_msg, name); |
724 | } |
725 | |
726 | |
727 | |
728 | /* |
729 | * Return a new job structure. |
730 | * Called with interrupts off. |
731 | */ |
732 | |
733 | struct job * |
734 | makejob(union node *node, int nprocs) |
735 | { |
736 | int i; |
737 | struct job *jp; |
738 | |
739 | for (i = njobs, jp = jobtab ; ; jp++) { |
740 | if (--i < 0) { |
741 | jp = growjobtab(); |
742 | break; |
743 | } |
744 | if (jp->used == 0) |
745 | break; |
746 | if (jp->state != JOBDONE || !jp->waited) |
747 | continue; |
748 | if (jobctl) |
749 | continue; |
750 | freejob(jp); |
751 | break; |
752 | } |
753 | memset(jp, 0, sizeof(*jp)); |
754 | #if JOBS |
755 | if (jobctl) |
756 | jp->jobctl = 1; |
757 | #endif |
758 | jp->prev_job = curjob; |
759 | curjob = jp; |
760 | jp->used = 1; |
761 | jp->ps = &jp->ps0; |
762 | if (nprocs > 1) { |
763 | jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); |
764 | } |
765 | TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, |
766 | jobno(jp))); |
767 | return jp; |
768 | } |
769 | |
770 | STATIC struct job * |
771 | growjobtab(void) |
772 | { |
773 | size_t len; |
774 | ptrdiff_t offset; |
775 | struct job *jp, *jq; |
776 | |
777 | len = njobs * sizeof(*jp); |
778 | jq = jobtab; |
779 | jp = ckrealloc(jq, len + 4 * sizeof(*jp)); |
780 | |
781 | offset = (char *)jp - (char *)jq; |
782 | if (offset) { |
783 | /* Relocate pointers */ |
784 | size_t l = len; |
785 | |
786 | jq = (struct job *)((char *)jq + l); |
787 | while (l) { |
788 | l -= sizeof(*jp); |
789 | jq--; |
790 | #define joff(p) ((struct job *)((char *)(p) + l)) |
791 | #define jmove(p) (p) = (void *)((char *)(p) + offset) |
792 | if (likely(joff(jp)->ps == &jq->ps0)) |
793 | jmove(joff(jp)->ps); |
794 | if (joff(jp)->prev_job) |
795 | jmove(joff(jp)->prev_job); |
796 | } |
797 | if (curjob) |
798 | jmove(curjob); |
799 | #undef joff |
800 | #undef jmove |
801 | } |
802 | |
803 | njobs += 4; |
804 | jobtab = jp; |
805 | jp = (struct job *)((char *)jp + len); |
806 | jq = jp + 3; |
807 | do { |
808 | jq->used = 0; |
809 | } while (--jq >= jp); |
810 | return jp; |
811 | } |
812 | |
813 | |
814 | /* |
815 | * Fork off a subshell. If we are doing job control, give the subshell its |
816 | * own process group. Jp is a job structure that the job is to be added to. |
817 | * N is the command that will be evaluated by the child. Both jp and n may |
818 | * be NULL. The mode parameter can be one of the following: |
819 | * FORK_FG - Fork off a foreground process. |
820 | * FORK_BG - Fork off a background process. |
821 | * FORK_NOJOB - Like FORK_FG, but don't give the process its own |
822 | * process group even if job control is on. |
823 | * |
824 | * When job control is turned off, background processes have their standard |
825 | * input redirected to /dev/null (except for the second and later processes |
826 | * in a pipeline). |
827 | * |
828 | * Called with interrupts off. |
829 | */ |
830 | |
831 | STATIC inline void |
832 | forkchild(struct job *jp, union node *n, int mode) |
833 | { |
834 | int oldlvl; |
835 | |
836 | TRACE(("Child shell %d\n", getpid())); |
837 | oldlvl = shlvl; |
838 | shlvl++; |
839 | |
840 | closescript(); |
841 | clear_traps(); |
842 | #if JOBS |
843 | /* do job control only in root shell */ |
844 | jobctl = 0; |
845 | if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) { |
846 | pid_t pgrp; |
847 | |
848 | if (jp->nprocs == 0) |
849 | pgrp = getpid(); |
850 | else |
851 | pgrp = jp->ps[0].pid; |
852 | /* This can fail because we are doing it in the parent also */ |
853 | (void)setpgid(0, pgrp); |
854 | if (mode == FORK_FG) |
855 | xtcsetpgrp(ttyfd, pgrp); |
856 | setsignal(SIGTSTP); |
857 | setsignal(SIGTTOU); |
858 | } else |
859 | #endif |
860 | if (mode == FORK_BG) { |
861 | ignoresig(SIGINT); |
862 | ignoresig(SIGQUIT); |
863 | if (jp->nprocs == 0) { |
864 | close(0); |
865 | if (open(_PATH_DEVNULL, O_RDONLY) != 0) |
866 | sh_error("Can't open %s", _PATH_DEVNULL); |
867 | } |
868 | } |
869 | if (!oldlvl && iflag) { |
870 | setsignal(SIGINT); |
871 | setsignal(SIGQUIT); |
872 | setsignal(SIGTERM); |
873 | } |
874 | for (jp = curjob; jp; jp = jp->prev_job) |
875 | freejob(jp); |
876 | jobless = 0; |
877 | } |
878 | |
879 | STATIC inline void |
880 | forkparent(struct job *jp, union node *n, int mode, pid_t pid) |
881 | { |
882 | TRACE(("In parent shell: child = %d\n", pid)); |
883 | if (!jp) { |
884 | while (jobless && dowait(DOWAIT_NORMAL, 0) > 0); |
885 | jobless++; |
886 | return; |
887 | } |
888 | #if JOBS |
889 | if (mode != FORK_NOJOB && jp->jobctl) { |
890 | int pgrp; |
891 | |
892 | if (jp->nprocs == 0) |
893 | pgrp = pid; |
894 | else |
895 | pgrp = jp->ps[0].pid; |
896 | /* This can fail because we are doing it in the child also */ |
897 | (void)setpgid(pid, pgrp); |
898 | } |
899 | #endif |
900 | if (mode == FORK_BG) { |
901 | backgndpid = pid; /* set $! */ |
902 | set_curjob(jp, CUR_RUNNING); |
903 | } |
904 | if (jp) { |
905 | struct procstat *ps = &jp->ps[jp->nprocs++]; |
906 | ps->pid = pid; |
907 | ps->status = -1; |
908 | ps->cmd = nullstr; |
909 | if (jobctl && n) |
910 | ps->cmd = commandtext(n); |
911 | } |
912 | } |
913 | |
914 | int |
915 | forkshell(struct job *jp, union node *n, int mode) |
916 | { |
917 | int pid; |
918 | |
919 | TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode)); |
920 | pid = fork(); |
921 | if (pid < 0) { |
922 | TRACE(("Fork failed, errno=%d", errno)); |
923 | if (jp) |
924 | freejob(jp); |
925 | sh_error("Cannot fork"); |
926 | } |
927 | if (pid == 0) |
928 | forkchild(jp, n, mode); |
929 | else |
930 | forkparent(jp, n, mode, pid); |
931 | return pid; |
932 | } |
933 | |
934 | /* |
935 | * Wait for job to finish. |
936 | * |
937 | * Under job control we have the problem that while a child process is |
938 | * running interrupts generated by the user are sent to the child but not |
939 | * to the shell. This means that an infinite loop started by an inter- |
940 | * active user may be hard to kill. With job control turned off, an |
941 | * interactive user may place an interactive program inside a loop. If |
942 | * the interactive program catches interrupts, the user doesn't want |
943 | * these interrupts to also abort the loop. The approach we take here |
944 | * is to have the shell ignore interrupt signals while waiting for a |
945 | * forground process to terminate, and then send itself an interrupt |
946 | * signal if the child process was terminated by an interrupt signal. |
947 | * Unfortunately, some programs want to do a bit of cleanup and then |
948 | * exit on interrupt; unless these processes terminate themselves by |
949 | * sending a signal to themselves (instead of calling exit) they will |
950 | * confuse this approach. |
951 | * |
952 | * Called with interrupts off. |
953 | */ |
954 | |
955 | int |
956 | waitforjob(struct job *jp) |
957 | { |
958 | int st; |
959 | |
960 | TRACE(("waitforjob(%%%d) called\n", jobno(jp))); |
961 | while (jp->state == JOBRUNNING) { |
962 | dowait(DOWAIT_BLOCK, jp); |
963 | } |
964 | st = getstatus(jp); |
965 | #if JOBS |
966 | if (jp->jobctl) { |
967 | xtcsetpgrp(ttyfd, rootpid); |
968 | /* |
969 | * This is truly gross. |
970 | * If we're doing job control, then we did a TIOCSPGRP which |
971 | * caused us (the shell) to no longer be in the controlling |
972 | * session -- so we wouldn't have seen any ^C/SIGINT. So, we |
973 | * intuit from the subprocess exit status whether a SIGINT |
974 | * occurred, and if so interrupt ourselves. Yuck. - mycroft |
975 | */ |
976 | if (jp->sigint) |
977 | raise(SIGINT); |
978 | } |
979 | #endif |
980 | if (! JOBS || jp->state == JOBDONE) |
981 | freejob(jp); |
982 | return st; |
983 | } |
984 | |
985 | |
986 | |
987 | /* |
988 | * Wait for a process to terminate. |
989 | */ |
990 | |
991 | STATIC int |
992 | dowait(int block, struct job *job) |
993 | { |
994 | int pid; |
995 | int status; |
996 | struct job *jp; |
997 | struct job *thisjob; |
998 | int state; |
999 | |
1000 | TRACE(("dowait(%d) called\n", block)); |
1001 | pid = waitproc(block, &status); |
1002 | TRACE(("wait returns pid %d, status=%d\n", pid, status)); |
1003 | if (pid <= 0) |
1004 | return pid; |
1005 | INTOFF; |
1006 | thisjob = NULL; |
1007 | for (jp = curjob; jp; jp = jp->prev_job) { |
1008 | struct procstat *sp; |
1009 | struct procstat *spend; |
1010 | if (jp->state == JOBDONE) |
1011 | continue; |
1012 | state = JOBDONE; |
1013 | spend = jp->ps + jp->nprocs; |
1014 | sp = jp->ps; |
1015 | do { |
1016 | if (sp->pid == pid) { |
1017 | TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jobno(jp), pid, sp->status, status)); |
1018 | sp->status = status; |
1019 | thisjob = jp; |
1020 | } |
1021 | if (sp->status == -1) |
1022 | state = JOBRUNNING; |
1023 | #if JOBS |
1024 | if (state == JOBRUNNING) |
1025 | continue; |
1026 | if (WIFSTOPPED(sp->status)) { |
1027 | jp->stopstatus = sp->status; |
1028 | state = JOBSTOPPED; |
1029 | } |
1030 | #endif |
1031 | } while (++sp < spend); |
1032 | if (thisjob) |
1033 | goto gotjob; |
1034 | } |
1035 | if (!JOBS || !WIFSTOPPED(status)) |
1036 | jobless--; |
1037 | goto out; |
1038 | |
1039 | gotjob: |
1040 | if (state != JOBRUNNING) { |
1041 | thisjob->changed = 1; |
1042 | |
1043 | if (thisjob->state != state) { |
1044 | TRACE(("Job %d: changing state from %d to %d\n", jobno(thisjob), thisjob->state, state)); |
1045 | thisjob->state = state; |
1046 | #if JOBS |
1047 | if (state == JOBSTOPPED) { |
1048 | set_curjob(thisjob, CUR_STOPPED); |
1049 | } |
1050 | #endif |
1051 | } |
1052 | } |
1053 | |
1054 | out: |
1055 | INTON; |
1056 | |
1057 | if (thisjob && thisjob == job) { |
1058 | char s[48 + 1]; |
1059 | int len; |
1060 | |
1061 | len = sprint_status(s, status, 1); |
1062 | if (len) { |
1063 | s[len] = '\n'; |
1064 | s[len + 1] = 0; |
1065 | outstr(s, out2); |
1066 | } |
1067 | } |
1068 | return pid; |
1069 | } |
1070 | |
1071 | |
1072 | |
1073 | /* |
1074 | * Do a wait system call. If job control is compiled in, we accept |
1075 | * stopped processes. If block is zero, we return a value of zero |
1076 | * rather than blocking. |
1077 | * |
1078 | * System V doesn't have a non-blocking wait system call. It does |
1079 | * have a SIGCLD signal that is sent to a process when one of it's |
1080 | * children dies. The obvious way to use SIGCLD would be to install |
1081 | * a handler for SIGCLD which simply bumped a counter when a SIGCLD |
1082 | * was received, and have waitproc bump another counter when it got |
1083 | * the status of a process. Waitproc would then know that a wait |
1084 | * system call would not block if the two counters were different. |
1085 | * This approach doesn't work because if a process has children that |
1086 | * have not been waited for, System V will send it a SIGCLD when it |
1087 | * installs a signal handler for SIGCLD. What this means is that when |
1088 | * a child exits, the shell will be sent SIGCLD signals continuously |
1089 | * until is runs out of stack space, unless it does a wait call before |
1090 | * restoring the signal handler. The code below takes advantage of |
1091 | * this (mis)feature by installing a signal handler for SIGCLD and |
1092 | * then checking to see whether it was called. If there are any |
1093 | * children to be waited for, it will be. |
1094 | * |
1095 | * If neither SYSV nor BSD is defined, we don't implement nonblocking |
1096 | * waits at all. In this case, the user will not be informed when |
1097 | * a background process until the next time she runs a real program |
1098 | * (as opposed to running a builtin command or just typing return), |
1099 | * and the jobs command may give out of date information. |
1100 | */ |
1101 | |
1102 | #ifdef SYSV |
1103 | STATIC int gotsigchild; |
1104 | |
1105 | STATIC int onsigchild() { |
1106 | gotsigchild = 1; |
1107 | } |
1108 | #endif |
1109 | |
1110 | |
1111 | STATIC int |
1112 | waitproc(int block, int *status) |
1113 | { |
1114 | #ifdef BSD |
1115 | int flags = 0; |
1116 | |
1117 | #if JOBS |
1118 | if (jobctl) |
1119 | flags |= WUNTRACED; |
1120 | #endif |
1121 | if (block == 0) |
1122 | flags |= WNOHANG; |
1123 | return wait3(status, flags, (struct rusage *)NULL); |
1124 | #else |
1125 | #ifdef SYSV |
1126 | int (*save)(); |
1127 | |
1128 | if (block == 0) { |
1129 | gotsigchild = 0; |
1130 | save = signal(SIGCLD, onsigchild); |
1131 | signal(SIGCLD, save); |
1132 | if (gotsigchild == 0) |
1133 | return 0; |
1134 | } |
1135 | return wait(status); |
1136 | #else |
1137 | if (block == 0) |
1138 | return 0; |
1139 | return wait(status); |
1140 | #endif |
1141 | #endif |
1142 | } |
1143 | |
1144 | /* |
1145 | * return 1 if there are stopped jobs, otherwise 0 |
1146 | */ |
1147 | int job_warning; |
1148 | int |
1149 | stoppedjobs(void) |
1150 | { |
1151 | struct job *jp; |
1152 | int retval; |
1153 | |
1154 | retval = 0; |
1155 | if (job_warning) |
1156 | goto out; |
1157 | jp = curjob; |
1158 | if (jp && jp->state == JOBSTOPPED) { |
1159 | out2str("You have stopped jobs.\n"); |
1160 | job_warning = 2; |
1161 | retval++; |
1162 | } |
1163 | |
1164 | out: |
1165 | return retval; |
1166 | } |
1167 | |
1168 | /* |
1169 | * Return a string identifying a command (to be printed by the |
1170 | * jobs command). |
1171 | */ |
1172 | |
1173 | STATIC char *cmdnextc; |
1174 | |
1175 | STATIC char * |
1176 | commandtext(union node *n) |
1177 | { |
1178 | char *name; |
1179 | |
1180 | STARTSTACKSTR(cmdnextc); |
1181 | cmdtxt(n); |
1182 | name = stackblock(); |
1183 | TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n", |
1184 | name, cmdnextc, ps->cmd)); |
1185 | return savestr(name); |
1186 | } |
1187 | |
1188 | |
1189 | STATIC void |
1190 | cmdtxt(union node *n) |
1191 | { |
1192 | union node *np; |
1193 | struct nodelist *lp; |
1194 | const char *p; |
1195 | char s[2]; |
1196 | |
1197 | if (!n) |
1198 | return; |
1199 | switch (n->type) { |
1200 | default: |
1201 | #if DEBUG |
1202 | abort(); |
1203 | #endif |
1204 | case NPIPE: |
1205 | lp = n->npipe.cmdlist; |
1206 | for (;;) { |
1207 | cmdtxt(lp->n); |
1208 | lp = lp->next; |
1209 | if (!lp) |
1210 | break; |
1211 | cmdputs(" | "); |
1212 | } |
1213 | break; |
1214 | case NSEMI: |
1215 | p = "; "; |
1216 | goto binop; |
1217 | case NAND: |
1218 | p = " && "; |
1219 | goto binop; |
1220 | case NOR: |
1221 | p = " || "; |
1222 | binop: |
1223 | cmdtxt(n->nbinary.ch1); |
1224 | cmdputs(p); |
1225 | n = n->nbinary.ch2; |
1226 | goto donode; |
1227 | case NREDIR: |
1228 | case NBACKGND: |
1229 | n = n->nredir.n; |
1230 | goto donode; |
1231 | case NNOT: |
1232 | cmdputs("!"); |
1233 | n = n->nnot.com; |
1234 | donode: |
1235 | cmdtxt(n); |
1236 | break; |
1237 | case NIF: |
1238 | cmdputs("if "); |
1239 | cmdtxt(n->nif.test); |
1240 | cmdputs("; then "); |
1241 | n = n->nif.ifpart; |
1242 | if (n->nif.elsepart) { |
1243 | cmdtxt(n); |
1244 | cmdputs("; else "); |
1245 | n = n->nif.elsepart; |
1246 | } |
1247 | p = "; fi"; |
1248 | goto dotail; |
1249 | case NSUBSHELL: |
1250 | cmdputs("("); |
1251 | n = n->nredir.n; |
1252 | p = ")"; |
1253 | goto dotail; |
1254 | case NWHILE: |
1255 | p = "while "; |
1256 | goto until; |
1257 | case NUNTIL: |
1258 | p = "until "; |
1259 | until: |
1260 | cmdputs(p); |
1261 | cmdtxt(n->nbinary.ch1); |
1262 | n = n->nbinary.ch2; |
1263 | p = "; done"; |
1264 | dodo: |
1265 | cmdputs("; do "); |
1266 | dotail: |
1267 | cmdtxt(n); |
1268 | goto dotail2; |
1269 | case NFOR: |
1270 | cmdputs("for "); |
1271 | cmdputs(n->nfor.var); |
1272 | cmdputs(" in "); |
1273 | cmdlist(n->nfor.args, 1); |
1274 | n = n->nfor.body; |
1275 | p = "; done"; |
1276 | goto dodo; |
1277 | case NDEFUN: |
1278 | cmdputs(n->narg.text); |
1279 | p = "() { ... }"; |
1280 | goto dotail2; |
1281 | case NCMD: |
1282 | cmdlist(n->ncmd.args, 1); |
1283 | cmdlist(n->ncmd.redirect, 0); |
1284 | break; |
1285 | case NARG: |
1286 | p = n->narg.text; |
1287 | dotail2: |
1288 | cmdputs(p); |
1289 | break; |
1290 | case NHERE: |
1291 | case NXHERE: |
1292 | p = "<<..."; |
1293 | goto dotail2; |
1294 | case NCASE: |
1295 | cmdputs("case "); |
1296 | cmdputs(n->ncase.expr->narg.text); |
1297 | cmdputs(" in "); |
1298 | for (np = n->ncase.cases; np; np = np->nclist.next) { |
1299 | cmdtxt(np->nclist.pattern); |
1300 | cmdputs(") "); |
1301 | cmdtxt(np->nclist.body); |
1302 | cmdputs(";; "); |
1303 | } |
1304 | p = "esac"; |
1305 | goto dotail2; |
1306 | case NTO: |
1307 | p = ">"; |
1308 | goto redir; |
1309 | case NCLOBBER: |
1310 | p = ">|"; |
1311 | goto redir; |
1312 | case NAPPEND: |
1313 | p = ">>"; |
1314 | goto redir; |
1315 | case NTOFD: |
1316 | p = ">&"; |
1317 | goto redir; |
1318 | case NFROM: |
1319 | p = "<"; |
1320 | goto redir; |
1321 | case NFROMFD: |
1322 | p = "<&"; |
1323 | goto redir; |
1324 | case NFROMTO: |
1325 | p = "<>"; |
1326 | redir: |
1327 | s[0] = n->nfile.fd + '0'; |
1328 | s[1] = '\0'; |
1329 | cmdputs(s); |
1330 | cmdputs(p); |
1331 | if (n->type == NTOFD || n->type == NFROMFD) { |
1332 | s[0] = n->ndup.dupfd + '0'; |
1333 | p = s; |
1334 | goto dotail2; |
1335 | } else { |
1336 | n = n->nfile.fname; |
1337 | goto donode; |
1338 | } |
1339 | } |
1340 | } |
1341 | |
1342 | STATIC void |
1343 | cmdlist(union node *np, int sep) |
1344 | { |
1345 | for (; np; np = np->narg.next) { |
1346 | if (!sep) |
1347 | cmdputs(spcstr); |
1348 | cmdtxt(np); |
1349 | if (sep && np->narg.next) |
1350 | cmdputs(spcstr); |
1351 | } |
1352 | } |
1353 | |
1354 | |
1355 | STATIC void |
1356 | cmdputs(const char *s) |
1357 | { |
1358 | const char *p, *str; |
1359 | char cc[2] = " "; |
1360 | char *nextc; |
1361 | signed char c; |
1362 | int subtype = 0; |
1363 | int quoted = 0; |
1364 | static const char vstype[VSTYPE + 1][4] = { |
1365 | "", "}", "-", "+", "?", "=", |
1366 | "%", "%%", "#", "##", |
1367 | }; |
1368 | |
1369 | nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc); |
1370 | p = s; |
1371 | while ((c = *p++) != 0) { |
1372 | str = 0; |
1373 | switch (c) { |
1374 | case CTLESC: |
1375 | c = *p++; |
1376 | break; |
1377 | case CTLVAR: |
1378 | subtype = *p++; |
1379 | if ((subtype & VSTYPE) == VSLENGTH) |
1380 | str = "${#"; |
1381 | else |
1382 | str = "${"; |
1383 | if (!(subtype & VSQUOTE) != !(quoted & 1)) { |
1384 | quoted ^= 1; |
1385 | c = '"'; |
1386 | } else |
1387 | goto dostr; |
1388 | break; |
1389 | case CTLENDVAR: |
1390 | str = "\"}" + !(quoted & 1); |
1391 | quoted >>= 1; |
1392 | subtype = 0; |
1393 | goto dostr; |
1394 | case CTLBACKQ: |
1395 | str = "$(...)"; |
1396 | goto dostr; |
1397 | case CTLBACKQ+CTLQUOTE: |
1398 | str = "\"$(...)\""; |
1399 | goto dostr; |
1400 | case CTLARI: |
1401 | str = "$(("; |
1402 | goto dostr; |
1403 | case CTLENDARI: |
1404 | str = "))"; |
1405 | goto dostr; |
1406 | case CTLQUOTEMARK: |
1407 | quoted ^= 1; |
1408 | c = '"'; |
1409 | break; |
1410 | case '=': |
1411 | if (subtype == 0) |
1412 | break; |
1413 | if ((subtype & VSTYPE) != VSNORMAL) |
1414 | quoted <<= 1; |
1415 | str = vstype[subtype & VSTYPE]; |
1416 | if (subtype & VSNUL) |
1417 | c = ':'; |
1418 | else |
1419 | goto checkstr; |
1420 | break; |
1421 | case '\'': |
1422 | case '\\': |
1423 | case '"': |
1424 | case '$': |
1425 | /* These can only happen inside quotes */ |
1426 | cc[0] = c; |
1427 | str = cc; |
1428 | c = '\\'; |
1429 | break; |
1430 | default: |
1431 | break; |
1432 | } |
1433 | USTPUTC(c, nextc); |
1434 | checkstr: |
1435 | if (!str) |
1436 | continue; |
1437 | dostr: |
1438 | while ((c = *str++)) { |
1439 | USTPUTC(c, nextc); |
1440 | } |
1441 | } |
1442 | if (quoted & 1) { |
1443 | USTPUTC('"', nextc); |
1444 | } |
1445 | *nextc = 0; |
1446 | cmdnextc = nextc; |
1447 | } |
1448 | |
1449 | |
1450 | STATIC void |
1451 | showpipe(struct job *jp, struct output *out) |
1452 | { |
1453 | struct procstat *sp; |
1454 | struct procstat *spend; |
1455 | |
1456 | spend = jp->ps + jp->nprocs; |
1457 | for (sp = jp->ps + 1; sp < spend; sp++) |
1458 | outfmt(out, " | %s", sp->cmd); |
1459 | outcslow('\n', out); |
1460 | flushall(); |
1461 | } |
1462 | |
1463 | |
1464 | #if JOBS |
1465 | STATIC void |
1466 | xtcsetpgrp(int fd, pid_t pgrp) |
1467 | { |
1468 | if (tcsetpgrp(fd, pgrp)) |
1469 | sh_error("Cannot set tty process group (%s)", strerror(errno)); |
1470 | } |
1471 | #endif |
1472 | |
1473 | |
1474 | STATIC int |
1475 | getstatus(struct job *job) { |
1476 | int status; |
1477 | int retval; |
1478 | |
1479 | status = job->ps[job->nprocs - 1].status; |
1480 | retval = WEXITSTATUS(status); |
1481 | if (!WIFEXITED(status)) { |
1482 | #if JOBS |
1483 | retval = WSTOPSIG(status); |
1484 | if (!WIFSTOPPED(status)) |
1485 | #endif |
1486 | { |
1487 | /* XXX: limits number of signals */ |
1488 | retval = WTERMSIG(status); |
1489 | #if JOBS |
1490 | if (retval == SIGINT) |
1491 | job->sigint = 1; |
1492 | #endif |
1493 | } |
1494 | retval += 128; |
1495 | } |
1496 | TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n", |
1497 | jobno(job), job->nprocs, status, retval)); |
1498 | return retval; |
1499 | } |