VirtualBox

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

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

more fixes - it all compiles now (linking fails of course).

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