VirtualBox

source: kBuild/trunk/src/kash/jobs.c@ 1286

Last change on this file since 1286 was 1240, checked in by bird, 17 years ago

shfile_cloexec. signal types.

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id
File size: 30.9 KB
Line 
1/* $NetBSD: jobs.c,v 1.63 2005/06/01 15:41:19 lukem Exp $ */
2
3/*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. 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#if 0
36#ifndef lint
37static char sccsid[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95";
38#else
39__RCSID("$NetBSD: jobs.c,v 1.63 2005/06/01 15:41:19 lukem Exp $");
40#endif /* not lint */
41#endif
42
43#include <fcntl.h>
44#include <errno.h>
45#include <stdlib.h>
46#include <sys/types.h>
47
48#include "shell.h"
49#if JOBS && !defined(_MSC_VER)
50# include <termios.h>
51#endif
52#include "redir.h"
53#include "show.h"
54#include "main.h"
55#include "parser.h"
56#include "nodes.h"
57#include "jobs.h"
58#include "options.h"
59#include "trap.h"
60#include "syntax.h"
61#include "input.h"
62#include "output.h"
63#include "memalloc.h"
64#include "error.h"
65#include "mystring.h"
66#include "shinstance.h"
67
68//static struct job *jobtab; /* array of jobs */
69//static int njobs; /* size of array */
70//static int jobs_invalid; /* set in child */
71//MKINIT pid_t backgndpid = -1; /* pid of last background process */
72#if JOBS
73//int initialpgrp; /* pgrp of shell on invocation */
74//static int curjob = -1; /* current job */
75#endif
76//static int ttyfd = -1;
77
78STATIC void restartjob(shinstance *, struct job *);
79STATIC void freejob(shinstance *, struct job *);
80STATIC struct job *getjob(shinstance *, const char *, int);
81STATIC int dowait(shinstance *, int, struct job *);
82STATIC int waitproc(shinstance *, int, struct job *, int *);
83STATIC void cmdtxt(shinstance *, union node *);
84STATIC void cmdlist(shinstance *, union node *, int);
85STATIC void cmdputs(shinstance *, const char *);
86
87
88/*
89 * Turn job control on and off.
90 *
91 * Note: This code assumes that the third arg to ioctl is a character
92 * pointer, which is true on Berkeley systems but not System V. Since
93 * System V doesn't have job control yet, this isn't a problem now.
94 */
95
96//MKINIT int jobctl;
97
98void
99setjobctl(shinstance *psh, int on)
100{
101 if (on == psh->jobctl || psh->rootshell == 0)
102 return;
103 if (on) {
104 int err;
105 int i;
106 if (psh->ttyfd != -1)
107 shfile_close(&psh->fdtab, psh->ttyfd);
108 if ((psh->ttyfd = shfile_open(&psh->fdtab, "/dev/tty", O_RDWR, 0)) == -1) {
109 for (i = 0; i < 3; i++) {
110 if (shfile_isatty(&psh->fdtab, i)
111 && (psh->ttyfd = shfile_dup(&psh->fdtab, i)) != -1)
112 break;
113 }
114 if (i == 3)
115 goto out;
116 }
117 /* Move to a high fd */
118 for (i = 10; i > 2; i--) {
119 if ((err = shfile_fcntl(&psh->fdtab, psh->ttyfd, F_DUPFD, (1 << i) - 1)) != -1)
120 break;
121 }
122 if (err != -1) {
123 shfile_close(&psh->fdtab, psh->ttyfd);
124 psh->ttyfd = err;
125 }
126 err = shfile_cloexec(&psh->fdtab, psh->ttyfd, 1);
127 if (err == -1) {
128 shfile_close(&psh->fdtab, psh->ttyfd);
129 psh->ttyfd = -1;
130 goto out;
131 }
132 do { /* while we are in the background */
133 if ((psh->initialpgrp = sh_tcgetpgrp(psh, psh->ttyfd)) < 0) {
134out:
135 out2str(psh, "sh: can't access tty; job control turned off\n");
136 mflag(psh) = 0;
137 return;
138 }
139 if (psh->initialpgrp == -1)
140 psh->initialpgrp = sh_getpgrp(psh);
141 else if (psh->initialpgrp != sh_getpgrp(psh)) {
142 sh_killpg(psh, 0, SIGTTIN);
143 continue;
144 }
145 } while (0);
146
147 setsignal(psh, SIGTSTP, 0);
148 setsignal(psh, SIGTTOU, 0);
149 setsignal(psh, SIGTTIN, 0);
150 if (sh_getpgid(psh, 0) != psh->rootpid && sh_setpgid(psh, 0, psh->rootpid) == -1)
151 error(psh, "Cannot set process group (%s) at %d",
152 strerror(errno), __LINE__);
153 if (sh_tcsetpgrp(psh, psh->ttyfd, psh->rootpid) == -1)
154 error(psh, "Cannot set tty process group (%s) at %d",
155 strerror(errno), __LINE__);
156 } else { /* turning job control off */
157 if (sh_getpgid(psh, 0) != psh->initialpgrp && sh_setpgid(psh, 0, psh->initialpgrp) == -1)
158 error(psh, "Cannot set process group (%s) at %d",
159 strerror(errno), __LINE__);
160 if (sh_tcsetpgrp(psh, psh->ttyfd, psh->initialpgrp) == -1)
161 error(psh, "Cannot set tty process group (%s) at %d",
162 strerror(errno), __LINE__);
163 shfile_close(&psh->fdtab, psh->ttyfd);
164 psh->ttyfd = -1;
165 setsignal(psh, SIGTSTP, 0);
166 setsignal(psh, SIGTTOU, 0);
167 setsignal(psh, SIGTTIN, 0);
168 }
169 psh->jobctl = on;
170}
171
172
173#ifdef mkinit
174INCLUDE <stdlib.h>
175
176SHELLPROC {
177 psh->backgndpid = -1;
178#if JOBS
179 psh->jobctl = 0;
180#endif
181}
182
183#endif
184
185
186
187#if JOBS
188int
189fgcmd(shinstance *psh, int argc, char **argv)
190{
191 struct job *jp;
192 int i;
193 int status;
194
195 nextopt(psh, "");
196 jp = getjob(psh, *psh->argptr, 0);
197 if (jp->jobctl == 0)
198 error(psh, "job not created under job control");
199 out1fmt(psh, "%s", jp->ps[0].cmd);
200 for (i = 1; i < jp->nprocs; i++)
201 out1fmt(psh, " | %s", jp->ps[i].cmd );
202 out1c(psh, '\n');
203 output_flushall(psh);
204
205 for (i = 0; i < jp->nprocs; i++)
206 if (sh_tcsetpgrp(psh, psh->ttyfd, jp->ps[i].pid) != -1)
207 break;
208
209 if (i >= jp->nprocs) {
210 error(psh, "Cannot set tty process group (%s) at %d",
211 strerror(errno), __LINE__);
212 }
213 restartjob(psh, jp);
214 INTOFF;
215 status = waitforjob(psh, jp);
216 INTON;
217 return status;
218}
219
220static void
221set_curjob(shinstance *psh, struct job *jp, int mode)
222{
223 struct job *jp1, *jp2;
224 int i, ji;
225
226 ji = (int)(jp - psh->jobtab);
227
228 /* first remove from list */
229 if (ji == psh->curjob)
230 psh->curjob = jp->prev_job;
231 else {
232 for (i = 0; i < psh->njobs; i++) {
233 if (psh->jobtab[i].prev_job != ji)
234 continue;
235 psh->jobtab[i].prev_job = jp->prev_job;
236 break;
237 }
238 }
239
240 /* Then re-insert in correct position */
241 switch (mode) {
242 case 0: /* job being deleted */
243 jp->prev_job = -1;
244 break;
245 case 1: /* newly created job or backgrounded job,
246 put after all stopped jobs. */
247 if (psh->curjob != -1 && psh->jobtab[psh->curjob].state == JOBSTOPPED) {
248 for (jp1 = psh->jobtab + psh->curjob; ; jp1 = jp2) {
249 if (jp1->prev_job == -1)
250 break;
251 jp2 = psh->jobtab + jp1->prev_job;
252 if (jp2->state != JOBSTOPPED)
253 break;
254 }
255 jp->prev_job = jp1->prev_job;
256 jp1->prev_job = ji;
257 break;
258 }
259 /* FALLTHROUGH */
260 case 2: /* newly stopped job - becomes psh->curjob */
261 jp->prev_job = psh->curjob;
262 psh->curjob = ji;
263 break;
264 }
265}
266
267int
268bgcmd(shinstance *psh, int argc, char **argv)
269{
270 struct job *jp;
271 int i;
272
273 nextopt(psh, "");
274 do {
275 jp = getjob(psh, *psh->argptr, 0);
276 if (jp->jobctl == 0)
277 error(psh, "job not created under job control");
278 set_curjob(psh, jp, 1);
279 out1fmt(psh, "[%ld] %s", (long)(jp - psh->jobtab + 1), jp->ps[0].cmd);
280 for (i = 1; i < jp->nprocs; i++)
281 out1fmt(psh, " | %s", jp->ps[i].cmd );
282 out1c(psh, '\n');
283 output_flushall(psh);
284 restartjob(psh, jp);
285 } while (*psh->argptr && *++psh->argptr);
286 return 0;
287}
288
289
290STATIC void
291restartjob(shinstance *psh, struct job *jp)
292{
293 struct procstat *ps;
294 int i;
295
296 if (jp->state == JOBDONE)
297 return;
298 INTOFF;
299 for (i = 0; i < jp->nprocs; i++)
300 if (sh_killpg(psh, jp->ps[i].pid, SIGCONT) != -1)
301 break;
302 if (i >= jp->nprocs)
303 error(psh, "Cannot continue job (%s)", strerror(errno));
304 for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
305 if (WIFSTOPPED(ps->status)) {
306 ps->status = -1;
307 jp->state = JOBRUNNING;
308 }
309 }
310 INTON;
311}
312#endif
313
314static void
315showjob(shinstance *psh, struct output *out, struct job *jp, int mode)
316{
317 int procno;
318 int st;
319 struct procstat *ps;
320 size_t col;
321 char s[64];
322
323#if JOBS
324 if (mode & SHOW_PGID) {
325 /* just output process (group) id of pipeline */
326 outfmt(out, "%ld\n", (long)jp->ps->pid);
327 return;
328 }
329#endif
330
331 procno = jp->nprocs;
332 if (!procno)
333 return;
334
335 if (mode & SHOW_PID)
336 mode |= SHOW_MULTILINE;
337
338 if ((procno > 1 && !(mode & SHOW_MULTILINE))
339 || (mode & SHOW_SIGNALLED)) {
340 /* See if we have more than one status to report */
341 ps = jp->ps;
342 st = ps->status;
343 do {
344 int st1 = ps->status;
345 if (st1 != st)
346 /* yes - need multi-line output */
347 mode |= SHOW_MULTILINE;
348 if (st1 == -1 || !(mode & SHOW_SIGNALLED) || WIFEXITED(st1))
349 continue;
350 if (WIFSTOPPED(st1) || ((st1 = WTERMSIG(st1) & 0x7f)
351 && st1 != SIGINT && st1 != SIGPIPE))
352 mode |= SHOW_ISSIG;
353
354 } while (ps++, --procno);
355 procno = jp->nprocs;
356 }
357
358 if (mode & SHOW_SIGNALLED && !(mode & SHOW_ISSIG)) {
359 if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) {
360 TRACE((psh, "showjob: freeing job %d\n", jp - psh->jobtab + 1));
361 freejob(psh, jp);
362 }
363 return;
364 }
365
366 for (ps = jp->ps; --procno >= 0; ps++) { /* for each process */
367 if (ps == jp->ps)
368 fmtstr(s, 16, "[%ld] %c ",
369 (long)(jp - psh->jobtab + 1),
370#if JOBS
371 jp == psh->jobtab + psh->curjob ? '+' :
372 psh->curjob != -1 && jp == psh->jobtab +
373 psh->jobtab[psh->curjob].prev_job ? '-' :
374#endif
375 ' ');
376 else
377 fmtstr(s, 16, " " );
378 col = strlen(s);
379 if (mode & SHOW_PID) {
380 fmtstr(s + col, 16, "%ld ", (long)ps->pid);
381 col += strlen(s + col);
382 }
383 if (ps->status == -1) {
384 scopy("Running", s + col);
385 } else if (WIFEXITED(ps->status)) {
386 st = WEXITSTATUS(ps->status);
387 if (st)
388 fmtstr(s + col, 16, "Done(%d)", st);
389 else
390 fmtstr(s + col, 16, "Done");
391 } else {
392#if JOBS
393 if (WIFSTOPPED(ps->status))
394 st = WSTOPSIG(ps->status);
395 else /* WIFSIGNALED(ps->status) */
396#endif
397 st = WTERMSIG(ps->status);
398 st &= 0x7f;
399 if (st < NSIG && sys_siglist[st])
400 scopyn(sys_siglist[st], s + col, 32);
401 else
402 fmtstr(s + col, 16, "Signal %d", st);
403 if (WCOREDUMP(ps->status)) {
404 col += strlen(s + col);
405 scopyn(" (core dumped)", s + col, 64 - col);
406 }
407 }
408 col += strlen(s + col);
409 outstr(s, out);
410 do {
411 outc(' ', out);
412 col++;
413 } while (col < 30);
414 outstr(ps->cmd, out);
415 if (mode & SHOW_MULTILINE) {
416 if (procno > 0) {
417 outc(' ', out);
418 outc('|', out);
419 }
420 } else {
421 while (--procno >= 0)
422 outfmt(out, " | %s", (++ps)->cmd );
423 }
424 outc('\n', out);
425 }
426 flushout(out);
427 jp->changed = 0;
428 if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE))
429 freejob(psh, jp);
430}
431
432
433int
434jobscmd(shinstance *psh, int argc, char **argv)
435{
436 int mode, m;
437 int sv = psh->jobs_invalid;
438
439 psh->jobs_invalid = 0;
440 mode = 0;
441 while ((m = nextopt(psh, "lp")))
442 if (m == 'l')
443 mode = SHOW_PID;
444 else
445 mode = SHOW_PGID;
446 if (*psh->argptr)
447 do
448 showjob(psh, psh->out1, getjob(psh, *psh->argptr,0), mode);
449 while (*++psh->argptr);
450 else
451 showjobs(psh, psh->out1, mode);
452 psh->jobs_invalid = sv;
453 return 0;
454}
455
456
457/*
458 * Print a list of jobs. If "change" is nonzero, only print jobs whose
459 * statuses have changed since the last call to showjobs.
460 *
461 * If the shell is interrupted in the process of creating a job, the
462 * result may be a job structure containing zero processes. Such structures
463 * will be freed here.
464 */
465
466void
467showjobs(shinstance *psh, struct output *out, int mode)
468{
469 int jobno;
470 struct job *jp;
471 int silent = 0, gotpid;
472
473 TRACE((psh, "showjobs(%x) called\n", mode));
474
475 /* If not even one one job changed, there is nothing to do */
476 gotpid = dowait(psh, 0, NULL);
477 while (dowait(psh, 0, NULL) > 0)
478 continue;
479#ifdef JOBS
480 /*
481 * Check if we are not in our foreground group, and if not
482 * put us in it.
483 */
484 if (mflag(psh) && gotpid != -1 && sh_tcgetpgrp(psh, psh->ttyfd) != sh_getpid(psh)) {
485 if (sh_tcsetpgrp(psh, psh->ttyfd, sh_getpid(psh)) == -1)
486 error(psh, "Cannot set tty process group (%s) at %d",
487 strerror(errno), __LINE__);
488 TRACE((psh, "repaired tty process group\n"));
489 silent = 1;
490 }
491#endif
492 if (psh->jobs_invalid)
493 return;
494
495 for (jobno = 1, jp = psh->jobtab ; jobno <= psh->njobs ; jobno++, jp++) {
496 if (!jp->used)
497 continue;
498 if (jp->nprocs == 0) {
499 freejob(psh, jp);
500 continue;
501 }
502 if ((mode & SHOW_CHANGED) && !jp->changed)
503 continue;
504 if (silent && jp->changed) {
505 jp->changed = 0;
506 continue;
507 }
508 showjob(psh, out, jp, mode);
509 }
510}
511
512/*
513 * Mark a job structure as unused.
514 */
515
516STATIC void
517freejob(shinstance *psh, struct job *jp)
518{
519 INTOFF;
520 if (jp->ps != &jp->ps0) {
521 ckfree(jp->ps);
522 jp->ps = &jp->ps0;
523 }
524 jp->nprocs = 0;
525 jp->used = 0;
526#if JOBS
527 set_curjob(psh, jp, 0);
528#endif
529 INTON;
530}
531
532
533
534int
535waitcmd(shinstance *psh, int argc, char **argv)
536{
537 struct job *job;
538 int status, retval;
539 struct job *jp;
540
541 nextopt(psh, "");
542
543 if (!*psh->argptr) {
544 /* wait for all jobs */
545 jp = psh->jobtab;
546 if (psh->jobs_invalid)
547 return 0;
548 for (;;) {
549 if (jp >= psh->jobtab + psh->njobs) {
550 /* no running procs */
551 return 0;
552 }
553 if (!jp->used || jp->state != JOBRUNNING) {
554 jp++;
555 continue;
556 }
557 if (dowait(psh, 1, (struct job *)NULL) == -1)
558 return 128 + SIGINT;
559 jp = psh->jobtab;
560 }
561 }
562
563 retval = 127; /* XXXGCC: -Wuninitialized */
564 for (; *psh->argptr; psh->argptr++) {
565 job = getjob(psh, *psh->argptr, 1);
566 if (!job) {
567 retval = 127;
568 continue;
569 }
570 /* loop until process terminated or stopped */
571 while (job->state == JOBRUNNING) {
572 if (dowait(psh, 1, (struct job *)NULL) == -1)
573 return 128 + SIGINT;
574 }
575 status = job->ps[job->nprocs].status;
576 if (WIFEXITED(status))
577 retval = WEXITSTATUS(status);
578#if JOBS
579 else if (WIFSTOPPED(status))
580 retval = WSTOPSIG(status) + 128;
581#endif
582 else {
583 /* XXX: limits number of signals */
584 retval = WTERMSIG(status) + 128;
585 }
586 if (!iflag(psh))
587 freejob(psh, job);
588 }
589 return retval;
590}
591
592
593
594int
595jobidcmd(shinstance *psh, int argc, char **argv)
596{
597 struct job *jp;
598 int i;
599
600 nextopt(psh, "");
601 jp = getjob(psh, *psh->argptr, 0);
602 for (i = 0 ; i < jp->nprocs ; ) {
603 out1fmt(psh, "%ld", (long)jp->ps[i].pid);
604 out1c(psh, ++i < jp->nprocs ? ' ' : '\n');
605 }
606 return 0;
607}
608
609int
610getjobpgrp(shinstance *psh, const char *name)
611{
612 struct job *jp;
613
614 jp = getjob(psh, name, 1);
615 if (jp == 0)
616 return 0;
617 return -jp->ps[0].pid;
618}
619
620/*
621 * Convert a job name to a job structure.
622 */
623
624STATIC struct job *
625getjob(shinstance *psh, const char *name, int noerror)
626{
627 int jobno = -1;
628 struct job *jp;
629 int pid;
630 int i;
631 const char *err_msg = "No such job: %s";
632
633 if (name == NULL) {
634#if JOBS
635 jobno = psh->curjob;
636#endif
637 err_msg = "No current job";
638 } else if (name[0] == '%') {
639 if (is_number(name + 1)) {
640 jobno = number(psh, name + 1) - 1;
641 } else if (!name[2]) {
642 switch (name[1]) {
643#if JOBS
644 case 0:
645 case '+':
646 case '%':
647 jobno = psh->curjob;
648 err_msg = "No current job";
649 break;
650 case '-':
651 jobno = psh->curjob;
652 if (jobno != -1)
653 jobno = psh->jobtab[jobno].prev_job;
654 err_msg = "No previous job";
655 break;
656#endif
657 default:
658 goto check_pattern;
659 }
660 } else {
661 struct job *found;
662 check_pattern:
663 found = NULL;
664 for (jp = psh->jobtab, i = psh->njobs ; --i >= 0 ; jp++) {
665 if (!jp->used || jp->nprocs <= 0)
666 continue;
667 if ((name[1] == '?'
668 && strstr(jp->ps[0].cmd, name + 2))
669 || prefix(name + 1, jp->ps[0].cmd)) {
670 if (found) {
671 err_msg = "%s: ambiguous";
672 found = 0;
673 break;
674 }
675 found = jp;
676 }
677 }
678 if (found)
679 return found;
680 }
681
682 } else if (is_number(name)) {
683 pid = number(psh, name);
684 for (jp = psh->jobtab, i = psh->njobs ; --i >= 0 ; jp++) {
685 if (jp->used && jp->nprocs > 0
686 && jp->ps[jp->nprocs - 1].pid == pid)
687 return jp;
688 }
689 }
690
691 if (!psh->jobs_invalid && jobno >= 0 && jobno < psh->njobs) {
692 jp = psh->jobtab + jobno;
693 if (jp->used)
694 return jp;
695 }
696 if (!noerror)
697 error(psh, err_msg, name);
698 return 0;
699}
700
701
702
703/*
704 * Return a new job structure,
705 */
706
707struct job *
708makejob(shinstance *psh, union node *node, int nprocs)
709{
710 int i;
711 struct job *jp;
712
713 if (psh->jobs_invalid) {
714 for (i = psh->njobs, jp = psh->jobtab ; --i >= 0 ; jp++) {
715 if (jp->used)
716 freejob(psh, jp);
717 }
718 psh->jobs_invalid = 0;
719 }
720
721 for (i = psh->njobs, jp = psh->jobtab ; ; jp++) {
722 if (--i < 0) {
723 INTOFF;
724 if (psh->njobs == 0) {
725 psh->jobtab = ckmalloc(4 * sizeof psh->jobtab[0]);
726 } else {
727 jp = ckmalloc((psh->njobs + 4) * sizeof psh->jobtab[0]);
728 memcpy(jp, psh->jobtab, psh->njobs * sizeof jp[0]);
729 /* Relocate `ps' pointers */
730 for (i = 0; i < psh->njobs; i++)
731 if (jp[i].ps == &psh->jobtab[i].ps0)
732 jp[i].ps = &jp[i].ps0;
733 ckfree(psh->jobtab);
734 psh->jobtab = jp;
735 }
736 jp = psh->jobtab + psh->njobs;
737 for (i = 4 ; --i >= 0 ; psh->jobtab[psh->njobs++].used = 0);
738 INTON;
739 break;
740 }
741 if (jp->used == 0)
742 break;
743 }
744 INTOFF;
745 jp->state = JOBRUNNING;
746 jp->used = 1;
747 jp->changed = 0;
748 jp->nprocs = 0;
749#if JOBS
750 jp->jobctl = psh->jobctl;
751 set_curjob(psh, jp, 1);
752#endif
753 if (nprocs > 1) {
754 jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
755 } else {
756 jp->ps = &jp->ps0;
757 }
758 INTON;
759 TRACE((psh, "makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
760 jp - psh->jobtab + 1));
761 return jp;
762}
763
764
765/*
766 * Fork off a subshell. If we are doing job control, give the subshell its
767 * own process group. Jp is a job structure that the job is to be added to.
768 * N is the command that will be evaluated by the child. Both jp and n may
769 * be NULL. The mode parameter can be one of the following:
770 * FORK_FG - Fork off a foreground process.
771 * FORK_BG - Fork off a background process.
772 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
773 * process group even if job control is on.
774 *
775 * When job control is turned off, background processes have their standard
776 * input redirected to /dev/null (except for the second and later processes
777 * in a pipeline).
778 */
779
780int
781forkshell(shinstance *psh, struct job *jp, union node *n, int mode)
782{
783 int pid;
784
785 TRACE((psh, "forkshell(%%%d, %p, %d) called\n", jp - psh->jobtab, n, mode));
786 switch ((pid = sh_fork(psh))) {
787 case -1:
788 TRACE((psh, "Fork failed, errno=%d\n", errno));
789 INTON;
790 error(psh, "Cannot fork");
791 return -1; /* won't get here */
792 case 0:
793 forkchild(psh, jp, n, mode, 0);
794 return 0;
795 default:
796 return forkparent(psh, jp, n, mode, pid);
797 }
798}
799
800int
801forkparent(shinstance *psh, struct job *jp, union node *n, int mode, pid_t pid)
802{
803 int pgrp;
804
805 if (psh->rootshell && mode != FORK_NOJOB && mflag(psh)) {
806 if (jp == NULL || jp->nprocs == 0)
807 pgrp = pid;
808 else
809 pgrp = jp->ps[0].pid;
810 /* This can fail because we are doing it in the child also */
811 (void)sh_setpgid(psh, pid, pgrp);
812 }
813 if (mode == FORK_BG)
814 psh->backgndpid = pid; /* set $! */
815 if (jp) {
816 struct procstat *ps = &jp->ps[jp->nprocs++];
817 ps->pid = pid;
818 ps->status = -1;
819 ps->cmd[0] = 0;
820 if (/* iflag && rootshell && */ n)
821 commandtext(psh, ps, n);
822 }
823 TRACE((psh, "In parent shell: child = %d\n", pid));
824 return pid;
825}
826
827void
828forkchild(shinstance *psh, struct job *jp, union node *n, int mode, int vforked)
829{
830 int wasroot;
831 int pgrp;
832 const char *devnull = _PATH_DEVNULL;
833 const char *nullerr = "Can't open %s";
834
835 wasroot = psh->rootshell;
836 TRACE((psh, "Child shell %d\n", sh_getpid(psh)));
837 if (!vforked)
838 psh->rootshell = 0;
839
840 closescript(psh, vforked);
841 clear_traps(psh, vforked);
842#if JOBS
843 if (!vforked)
844 psh->jobctl = 0; /* do job control only in root shell */
845 if (wasroot && mode != FORK_NOJOB && mflag(psh)) {
846 if (jp == NULL || jp->nprocs == 0)
847 pgrp = sh_getpid(psh);
848 else
849 pgrp = jp->ps[0].pid;
850 /* This can fail because we are doing it in the parent also */
851 (void)sh_setpgid(psh, 0, pgrp);
852 if (mode == FORK_FG) {
853 if (sh_tcsetpgrp(psh, psh->ttyfd, pgrp) == -1)
854 error(psh, "Cannot set tty process group (%s) at %d",
855 strerror(errno), __LINE__);
856 }
857 setsignal(psh, SIGTSTP, vforked);
858 setsignal(psh, SIGTTOU, vforked);
859 } else if (mode == FORK_BG) {
860 ignoresig(psh, SIGINT, vforked);
861 ignoresig(psh, SIGQUIT, vforked);
862 if ((jp == NULL || jp->nprocs == 0) &&
863 ! fd0_redirected_p(psh)) {
864 shfile_close(&psh->fdtab, 0);
865 if (shfile_open(&psh->fdtab, devnull, O_RDONLY, 0) != 0)
866 error(psh, nullerr, devnull);
867 }
868 }
869#else
870 if (mode == FORK_BG) {
871 ignoresig(psh, SIGINT, vforked);
872 ignoresig(psh, SIGQUIT, vforked);
873 if ((jp == NULL || jp->nprocs == 0) &&
874 ! fd0_redirected_p(psh)) {
875 shfile_close(&psh->fdtab, 0);
876 if (shfile_open(&psh->fdtab, devnull, O_RDONLY, 0) != 0)
877 error(psh, nullerr, devnull);
878 }
879 }
880#endif
881 if (wasroot && iflag(psh)) {
882 setsignal(psh, SIGINT, vforked);
883 setsignal(psh, SIGQUIT, vforked);
884 setsignal(psh, SIGTERM, vforked);
885 }
886
887 if (!vforked)
888 psh->jobs_invalid = 1;
889}
890
891/*
892 * Wait for job to finish.
893 *
894 * Under job control we have the problem that while a child process is
895 * running interrupts generated by the user are sent to the child but not
896 * to the shell. This means that an infinite loop started by an inter-
897 * active user may be hard to kill. With job control turned off, an
898 * interactive user may place an interactive program inside a loop. If
899 * the interactive program catches interrupts, the user doesn't want
900 * these interrupts to also abort the loop. The approach we take here
901 * is to have the shell ignore interrupt signals while waiting for a
902 * forground process to terminate, and then send itself an interrupt
903 * signal if the child process was terminated by an interrupt signal.
904 * Unfortunately, some programs want to do a bit of cleanup and then
905 * exit on interrupt; unless these processes terminate themselves by
906 * sending a signal to themselves (instead of calling exit) they will
907 * confuse this approach.
908 */
909
910int
911waitforjob(shinstance *psh, struct job *jp)
912{
913#if JOBS
914 int mypgrp = sh_getpgrp(psh);
915#endif
916 int status;
917 int st;
918
919 INTOFF;
920 TRACE((psh, "waitforjob(%%%d) called\n", jp - psh->jobtab + 1));
921 while (jp->state == JOBRUNNING) {
922 dowait(psh, 1, jp);
923 }
924#if JOBS
925 if (jp->jobctl) {
926 if (sh_tcsetpgrp(psh, psh->ttyfd, mypgrp) == -1)
927 error(psh, "Cannot set tty process group (%s) at %d",
928 strerror(errno), __LINE__);
929 }
930 if (jp->state == JOBSTOPPED && psh->curjob != jp - psh->jobtab)
931 set_curjob(psh, jp, 2);
932#endif
933 status = jp->ps[jp->nprocs - 1].status;
934 /* convert to 8 bits */
935 if (WIFEXITED(status))
936 st = WEXITSTATUS(status);
937#if JOBS
938 else if (WIFSTOPPED(status))
939 st = WSTOPSIG(status) + 128;
940#endif
941 else
942 st = WTERMSIG(status) + 128;
943 TRACE((psh, "waitforjob: job %d, nproc %d, status %x, st %x\n",
944 jp - psh->jobtab + 1, jp->nprocs, status, st ));
945#if JOBS
946 if (jp->jobctl) {
947 /*
948 * This is truly gross.
949 * If we're doing job control, then we did a TIOCSPGRP which
950 * caused us (the shell) to no longer be in the controlling
951 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
952 * intuit from the subprocess exit status whether a SIGINT
953 * occurred, and if so interrupt ourselves. Yuck. - mycroft
954 */
955 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
956 sh_raise_sigint(psh);/*raise(SIGINT);*/
957 }
958#endif
959 if (! JOBS || jp->state == JOBDONE)
960 freejob(psh, jp);
961 INTON;
962 return st;
963}
964
965
966
967/*
968 * Wait for a process to terminate.
969 */
970
971STATIC int
972dowait(shinstance *psh, int block, struct job *job)
973{
974 int pid;
975 int status;
976 struct procstat *sp;
977 struct job *jp;
978 struct job *thisjob;
979 int done;
980 int stopped;
981
982 TRACE((psh, "dowait(%d) called\n", block));
983 do {
984 pid = waitproc(psh, block, job, &status);
985 TRACE((psh, "wait returns pid %d, status %d\n", pid, status));
986 } while (pid == -1 && errno == EINTR && psh->gotsig[SIGINT - 1] == 0);
987 if (pid <= 0)
988 return pid;
989 INTOFF;
990 thisjob = NULL;
991 for (jp = psh->jobtab ; jp < psh->jobtab + psh->njobs ; jp++) {
992 if (jp->used) {
993 done = 1;
994 stopped = 1;
995 for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
996 if (sp->pid == -1)
997 continue;
998 if (sp->pid == pid) {
999 TRACE((psh, "Job %d: changing status of proc %d from 0x%x to 0x%x\n", jp - psh->jobtab + 1, pid, sp->status, status));
1000 sp->status = status;
1001 thisjob = jp;
1002 }
1003 if (sp->status == -1)
1004 stopped = 0;
1005 else if (WIFSTOPPED(sp->status))
1006 done = 0;
1007 }
1008 if (stopped) { /* stopped or done */
1009 int state = done ? JOBDONE : JOBSTOPPED;
1010 if (jp->state != state) {
1011 TRACE((psh, "Job %d: changing state from %d to %d\n", jp - psh->jobtab + 1, jp->state, state));
1012 jp->state = state;
1013#if JOBS
1014 if (done)
1015 set_curjob(psh, jp, 0);
1016#endif
1017 }
1018 }
1019 }
1020 }
1021
1022 if (thisjob && thisjob->state != JOBRUNNING) {
1023 int mode = 0;
1024 if (!psh->rootshell || !iflag(psh))
1025 mode = SHOW_SIGNALLED;
1026 if (job == thisjob)
1027 mode = SHOW_SIGNALLED | SHOW_NO_FREE;
1028 if (mode)
1029 showjob(psh, psh->out2, thisjob, mode);
1030 else {
1031 TRACE((psh, "Not printing status, rootshell=%d, job=%p\n",
1032 psh->rootshell, job));
1033 thisjob->changed = 1;
1034 }
1035 }
1036
1037 INTON;
1038 return pid;
1039}
1040
1041
1042
1043/*
1044 * Do a wait system call. If job control is compiled in, we accept
1045 * stopped processes. If block is zero, we return a value of zero
1046 * rather than blocking.
1047 */
1048STATIC int
1049waitproc(shinstance *psh, int block, struct job *jp, int *status)
1050{
1051 int flags = 0;
1052
1053#if JOBS
1054 if (jp != NULL && jp->jobctl)
1055 flags |= WUNTRACED;
1056#endif
1057 if (block == 0)
1058 flags |= WNOHANG;
1059 return sh_waitpid(psh, -1, status, flags);
1060}
1061
1062/*
1063 * return 1 if there are stopped jobs, otherwise 0
1064 */
1065//int job_warning = 0;
1066int
1067stoppedjobs(shinstance *psh)
1068{
1069 int jobno;
1070 struct job *jp;
1071
1072 if (psh->job_warning || psh->jobs_invalid)
1073 return (0);
1074 for (jobno = 1, jp = psh->jobtab; jobno <= psh->njobs; jobno++, jp++) {
1075 if (jp->used == 0)
1076 continue;
1077 if (jp->state == JOBSTOPPED) {
1078 out2str(psh, "You have stopped jobs.\n");
1079 psh->job_warning = 2;
1080 return (1);
1081 }
1082 }
1083
1084 return (0);
1085}
1086
1087/*
1088 * Return a string identifying a command (to be printed by the
1089 * jobs command).
1090 */
1091
1092//STATIC char *cmdnextc;
1093//STATIC int cmdnleft;
1094
1095void
1096commandtext(shinstance *psh, struct procstat *ps, union node *n)
1097{
1098 int len;
1099
1100 psh->cmdnextc = ps->cmd;
1101 if (iflag(psh) || mflag(psh) || sizeof(ps->cmd) < 100)
1102 len = sizeof(ps->cmd);
1103 else
1104 len = sizeof(ps->cmd) / 10;
1105 psh->cmdnleft = len;
1106 cmdtxt(psh, n);
1107 if (psh->cmdnleft <= 0) {
1108 char *p = ps->cmd + len - 4;
1109 p[0] = '.';
1110 p[1] = '.';
1111 p[2] = '.';
1112 p[3] = 0;
1113 } else
1114 *psh->cmdnextc = '\0';
1115 TRACE((psh, "commandtext: ps->cmd %x, end %x, left %d\n\t\"%s\"\n",
1116 ps->cmd, psh->cmdnextc, psh->cmdnleft, ps->cmd));
1117}
1118
1119
1120STATIC void
1121cmdtxt(shinstance *psh, union node *n)
1122{
1123 union node *np;
1124 struct nodelist *lp;
1125 const char *p;
1126 int i;
1127 char s[2];
1128
1129 if (n == NULL || psh->cmdnleft <= 0)
1130 return;
1131 switch (n->type) {
1132 case NSEMI:
1133 cmdtxt(psh, n->nbinary.ch1);
1134 cmdputs(psh, "; ");
1135 cmdtxt(psh, n->nbinary.ch2);
1136 break;
1137 case NAND:
1138 cmdtxt(psh, n->nbinary.ch1);
1139 cmdputs(psh, " && ");
1140 cmdtxt(psh, n->nbinary.ch2);
1141 break;
1142 case NOR:
1143 cmdtxt(psh, n->nbinary.ch1);
1144 cmdputs(psh, " || ");
1145 cmdtxt(psh, n->nbinary.ch2);
1146 break;
1147 case NPIPE:
1148 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
1149 cmdtxt(psh, lp->n);
1150 if (lp->next)
1151 cmdputs(psh, " | ");
1152 }
1153 break;
1154 case NSUBSHELL:
1155 cmdputs(psh, "(");
1156 cmdtxt(psh, n->nredir.n);
1157 cmdputs(psh, ")");
1158 break;
1159 case NREDIR:
1160 case NBACKGND:
1161 cmdtxt(psh, n->nredir.n);
1162 break;
1163 case NIF:
1164 cmdputs(psh, "if ");
1165 cmdtxt(psh, n->nif.test);
1166 cmdputs(psh, "; then ");
1167 cmdtxt(psh, n->nif.ifpart);
1168 if (n->nif.elsepart) {
1169 cmdputs(psh, "; else ");
1170 cmdtxt(psh, n->nif.elsepart);
1171 }
1172 cmdputs(psh, "; fi");
1173 break;
1174 case NWHILE:
1175 cmdputs(psh, "while ");
1176 goto until;
1177 case NUNTIL:
1178 cmdputs(psh, "until ");
1179until:
1180 cmdtxt(psh, n->nbinary.ch1);
1181 cmdputs(psh, "; do ");
1182 cmdtxt(psh, n->nbinary.ch2);
1183 cmdputs(psh, "; done");
1184 break;
1185 case NFOR:
1186 cmdputs(psh, "for ");
1187 cmdputs(psh, n->nfor.var);
1188 cmdputs(psh, " in ");
1189 cmdlist(psh, n->nfor.args, 1);
1190 cmdputs(psh, "; do ");
1191 cmdtxt(psh, n->nfor.body);
1192 cmdputs(psh, "; done");
1193 break;
1194 case NCASE:
1195 cmdputs(psh, "case ");
1196 cmdputs(psh, n->ncase.expr->narg.text);
1197 cmdputs(psh, " in ");
1198 for (np = n->ncase.cases; np; np = np->nclist.next) {
1199 cmdtxt(psh, np->nclist.pattern);
1200 cmdputs(psh, ") ");
1201 cmdtxt(psh, np->nclist.body);
1202 cmdputs(psh, ";; ");
1203 }
1204 cmdputs(psh, "esac");
1205 break;
1206 case NDEFUN:
1207 cmdputs(psh, n->narg.text);
1208 cmdputs(psh, "() { ... }");
1209 break;
1210 case NCMD:
1211 cmdlist(psh, n->ncmd.args, 1);
1212 cmdlist(psh, n->ncmd.redirect, 0);
1213 break;
1214 case NARG:
1215 cmdputs(psh, n->narg.text);
1216 break;
1217 case NTO:
1218 p = ">"; i = 1; goto redir;
1219 case NCLOBBER:
1220 p = ">|"; i = 1; goto redir;
1221 case NAPPEND:
1222 p = ">>"; i = 1; goto redir;
1223 case NTOFD:
1224 p = ">&"; i = 1; goto redir;
1225 case NFROM:
1226 p = "<"; i = 0; goto redir;
1227 case NFROMFD:
1228 p = "<&"; i = 0; goto redir;
1229 case NFROMTO:
1230 p = "<>"; i = 0; goto redir;
1231redir:
1232 if (n->nfile.fd != i) {
1233 s[0] = n->nfile.fd + '0';
1234 s[1] = '\0';
1235 cmdputs(psh, s);
1236 }
1237 cmdputs(psh, p);
1238 if (n->type == NTOFD || n->type == NFROMFD) {
1239 s[0] = n->ndup.dupfd + '0';
1240 s[1] = '\0';
1241 cmdputs(psh, s);
1242 } else {
1243 cmdtxt(psh, n->nfile.fname);
1244 }
1245 break;
1246 case NHERE:
1247 case NXHERE:
1248 cmdputs(psh, "<<...");
1249 break;
1250 default:
1251 cmdputs(psh, "???");
1252 break;
1253 }
1254}
1255
1256STATIC void
1257cmdlist(shinstance *psh, union node *np, int sep)
1258{
1259 for (; np; np = np->narg.next) {
1260 if (!sep)
1261 cmdputs(psh, " ");
1262 cmdtxt(psh, np);
1263 if (sep && np->narg.next)
1264 cmdputs(psh, " ");
1265 }
1266}
1267
1268
1269STATIC void
1270cmdputs(shinstance *psh, const char *s)
1271{
1272 const char *p, *str = 0;
1273 char c, cc[2] = " ";
1274 char *nextc;
1275 int nleft;
1276 int subtype = 0;
1277 int quoted = 0;
1278 static char vstype[16][4] = { "", "}", "-", "+", "?", "=",
1279 "#", "##", "%", "%%" };
1280
1281 p = s;
1282 nextc = psh->cmdnextc;
1283 nleft = psh->cmdnleft;
1284 while (nleft > 0 && (c = *p++) != 0) {
1285 switch (c) {
1286 case CTLESC:
1287 c = *p++;
1288 break;
1289 case CTLVAR:
1290 subtype = *p++;
1291 if ((subtype & VSTYPE) == VSLENGTH)
1292 str = "${#";
1293 else
1294 str = "${";
1295 if (!(subtype & VSQUOTE) != !(quoted & 1)) {
1296 quoted ^= 1;
1297 c = '"';
1298 } else
1299 c = *str++;
1300 break;
1301 case CTLENDVAR:
1302 if (quoted & 1) {
1303 c = '"';
1304 str = "}";
1305 } else
1306 c = '}';
1307 quoted >>= 1;
1308 subtype = 0;
1309 break;
1310 case CTLBACKQ:
1311 c = '$';
1312 str = "(...)";
1313 break;
1314 case CTLBACKQ+CTLQUOTE:
1315 c = '"';
1316 str = "$(...)\"";
1317 break;
1318 case CTLARI:
1319 c = '$';
1320 str = "((";
1321 break;
1322 case CTLENDARI:
1323 c = ')';
1324 str = ")";
1325 break;
1326 case CTLQUOTEMARK:
1327 quoted ^= 1;
1328 c = '"';
1329 break;
1330 case '=':
1331 if (subtype == 0)
1332 break;
1333 str = vstype[subtype & VSTYPE];
1334 if (subtype & VSNUL)
1335 c = ':';
1336 else
1337 c = *str++;
1338 if (c != '}')
1339 quoted <<= 1;
1340 break;
1341 case '\'':
1342 case '\\':
1343 case '"':
1344 case '$':
1345 /* These can only happen inside quotes */
1346 cc[0] = c;
1347 str = cc;
1348 c = '\\';
1349 break;
1350 default:
1351 break;
1352 }
1353 do {
1354 *nextc++ = c;
1355 } while (--nleft > 0 && str && (c = *str++));
1356 str = 0;
1357 }
1358 if ((quoted & 1) && nleft) {
1359 *nextc++ = '"';
1360 nleft--;
1361 }
1362 psh->cmdnleft = nleft;
1363 psh->cmdnextc = nextc;
1364}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette