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