|
|
1.1 root 1: /*
2: * Process and job control
3: */
4:
5: static char *RCSid = "$Header: /newbits/usr/bin/korn/RCS/jobs.c,v 1.2 91/08/01 12:40:37 bin Exp Locker: bin $";
6:
7: /*
8: * based on version by Ron Natalie, BRL
9: *
10: * TODO:
11: * change Proc table to Job table, with array of pids.
12: * make %+ be jobs, %- be jobs->next.
13: * do not JFREE members of pipeline.
14: * consider procs[] related critical sections.
15: * signal(SIGCHLD, j_sigchld) should be
16: * sigaction(SIGCHLD, sigchld, NULL),
17: * with sigchld.sa_flags = SA_RESTART.
18: * There is a simple work-around if there is no SA_RESTART.
19: */
20:
21: #include <stddef.h>
22: #include <stdlib.h>
23: #include <string.h>
24: #include <stdio.h>
25: #include <errno.h>
26: #include <unistd.h>
27: #include <signal.h>
28: #include <setjmp.h>
29: #include <sys/types.h>
30: #include <sys/times.h>
31: #include <wait.h>
32: #if JOBS
33: #if _BSD
34: #include <sys/ioctl.h>
35: #else
36: #include "termios.h"
37: #endif
38: #endif
39: #include "sh.h"
40: #include "tree.h"
41:
42: #ifndef WIFCORED
43: #define WIFCORED(x) (!!((x)&0x80)) /* non-standard */
44: #endif
45:
46: /* as of P1003.1 Draft 12.3:
47: * pid_t getpgrp(void); // Get process group id
48: * pid_t setsid(void); // Create session and Set process group id
49: * int setpgid(pid_t pid, pid_t pgid); // Set process group id for job control
50: */
51:
52: #if JOBS
53: #if _BSD /* _BSD 4.* */
54: #define setpgid(p, pg) setpgrp(p, pg)
55: #define getpgid(p) getpgrp(p)
56: #define tcsetpgrp(fd,p) ioctl(fd, TIOCSPGRP, &(p))
57: #else /* POSIX-compatible */
58: #define getpgid(p) getpgrp() /* 1003.1 stupidity */
59: #define killpg(p, s) kill(-(p), s)
60: #endif
61: #endif
62:
63: #ifndef SIGCHLD
64: #define SIGCHLD SIGCLD
65: #endif
66:
67: typedef struct Proc Proc;
68: struct Proc {
69: Proc *next; /* `procs' list link */
70: int job; /* job number: %n */
71: short Volatile state; /* proc state */
72: short Volatile notify; /* proc state has changed */
73: Proc *prev; /* prev member of pipeline */
74: pid_t proc; /* process id */
75: pid_t pgrp; /* process group if flag[FMONITOR] */
76: short flags; /* execute flags */
77: int status; /* wait status */
78: clock_t utime, stime; /* user/system time when JDONE */
79: char com [48]; /* command */
80: };
81:
82: /* proc states */
83: #define JFREE 0 /* unused proc */
84: #define JRUN 1 /* foreground */
85: #define JEXIT 2 /* exit termination */
86: #define JSIGNAL 3 /* signal termination */
87: #define JSTOP 4 /* stopped */
88:
89: static Proc *procs = NULL; /* job/process table */
90:
91: clock_t j_utime, j_stime; /* user and system time for last job a-waited */
92: #if JOBS
93: static int sm_default, sm_sigchld; /* signal masks */
94: static int our_pgrp; /* shell's pgrp */
95: #endif
96: static Proc *j_lastj; /* last proc created by exchild */
97: static int j_lastjob = 0; /* last created job */
98: static int j_current = 0; /* current job */
99: static int j_previous = 0; /* previous job */
100:
101: static int j_newjob ARGS((void));
102: static void j_print ARGS((Proc *j));
103: static Proc *j_search ARGS((int job));
104: static int j_waitj ARGS((Proc *j, int intr));
105: static void j_sigchld ARGS((int sig));
106:
107: /* initialize job control */
108: void
109: j_init()
110: {
111: #if JOBS
112: #ifdef NTTYDISC
113: int ldisc = NTTYDISC; /* BSD brain damage */
114:
115: if (ttyfd >= 0)
116: ioctl(ttyfd, TIOCSETD, &ldisc);
117: #endif
118: our_pgrp = getpgid(0);
119: sm_default = 0;
120: sm_sigchld = sigmask(SIGCHLD);
121: #endif
122: }
123:
124: /* job cleanup before shell exit */
125: void
126: j_exit()
127: {
128: register Proc *j;
129: int killed = 0;
130:
131: #if JOBS
132: /* kill stopped jobs */
133: for (j = procs; j != NULL; j = j->next)
134: if (j->state == JSTOP) {
135: killed ++;
136: killpg(j->pgrp, SIGHUP);
137: killpg(j->pgrp, SIGCONT);
138: }
139: if (killed)
140: sleep(1);
141: #endif
142: j_notify();
143:
144: #if JOBS
145: if (flag[FMONITOR]) {
146: flag[FMONITOR] = 0;
147: j_change();
148: }
149: #endif
150: }
151:
152: #if JOBS
153: /* turn job control on or off according to flag[FMONITOR] */
154: void
155: j_change()
156: {
157: static handler_t old_tstp, old_ttin, old_ttou;
158:
159: if (flag[FMONITOR]) {
160: if (ttyfd < 0) {
161: flag[FMONITOR] = 0;
162: shellf("job control requires tty\n");
163: return;
164: }
165: #if !COHERENT
166: (void) signal(SIGCHLD, j_sigchld);
167: sigtraps[SIGCHLD].sig_dfl = 1; /* restore on fork */
168: old_tstp = signal(SIGTSTP, SIG_IGN);
169: sigtraps[SIGTSTP].sig_dfl = 1;
170: old_ttin = signal(SIGTTIN, SIG_IGN);
171: sigtraps[SIGTTIN].sig_dfl = 1;
172: old_ttou = signal(SIGTTOU, SIG_IGN);
173: sigtraps[SIGTTOU].sig_dfl = 1;
174: #endif
175: sigsetmask(sm_default);
176: tcsetpgrp(ttyfd, our_pgrp);
177: } else {
178: (void) signal(SIGCHLD, SIG_DFL);
179: (void) signal(SIGTSTP, old_tstp);
180: sigtraps[SIGTSTP].sig_dfl = 0;
181: (void) signal(SIGTTIN, old_ttin);
182: sigtraps[SIGTTIN].sig_dfl = 0;
183: (void) signal(SIGTTOU, old_ttou);
184: sigtraps[SIGTTOU].sig_dfl = 0;
185: }
186: }
187: #endif
188:
189: /* execute tree in child subprocess */
190: int
191: exchild(t, flags)
192: struct op *t;
193: int flags;
194: {
195: register int i;
196: register Proc *j;
197: int rv = 0;
198:
199: flags &= ~XFORK;
200: if ((flags&XEXEC))
201: return execute(t, flags);
202:
203: /* get free Proc entry */
204: for (j = procs; j != NULL; j = j->next)
205: if (j->state == JFREE)
206: goto Found;
207: j = alloc(sizeof(Proc), APERM);
208: j->next = procs;
209: j->state = JFREE;
210: procs = j;
211: Found:
212:
213: j->prev = ((flags&XPIPEI)) ? j_lastj : NULL;
214: j->proc = j->pgrp = 0;
215: j->flags = flags;
216: j->job = (flags&XPIPEI) ? j_lastjob : j_newjob();
217: snptreef(j->com, sizeof(j->com), "%T", t); /* save proc's command */
218: j->com[sizeof(j->com)-1] = '\0';
219: j->state = JRUN;
220:
221: /* stdio buffer must be flushed and invalidated */
222: for (i = 0; i < NUFILE; i++)
223: flushshf(i);
224:
225: /* create child process */
226: if ((i = fork()) < 0) {
227: /* todo: try a few times with exp-incr delay */
228: j->state = JFREE;
229: errorf("cannot fork - try again\n");
230: }
231: j->proc = (i != 0) ? i : getpid();
232:
233: #if JOBS
234: /* job control set up */
235: if (flag[FMONITOR] && !(flags&XXCOM)) {
236: j->pgrp = !(flags&XPIPEI) ? j->proc : j_lastj->pgrp;
237: /* do in both parent and child to avoid fork race condition */
238: if (!(flags&XBGND))
239: tcsetpgrp(ttyfd, j->pgrp); /* could be trouble */
240: setpgid(j->proc, j->pgrp);
241: }
242: #endif
243: j_lastj = j;
244:
245: if (i == 0) { /* child */
246: e.oenv = NULL;
247: if (flag[FTALKING])
248: restoresigs(); /* set back to default */
249: if ((flags&XBGND) && !flag[FMONITOR]) {
250: signal(SIGINT, SIG_IGN);
251: signal(SIGQUIT, SIG_IGN);
252: if (flag[FTALKING])
253: signal(SIGTERM, SIG_DFL);
254: #if COHERENT
255: if (!(flags & XPIPEI)) {
256: i = open("/dev/null", 0);
257: (void) dup2(i, 0);
258: close(i);
259: }
260: #endif
261: }
262: for (j = procs; j != NULL; j = j->next)
263: j->state = JFREE;
264: ttyfd = -1;
265: flag[FMONITOR] = flag[FTALKING] = 0;
266: cleartraps();
267: execute(t, flags|XEXEC); /* no return */
268: /* NOTREACHED */
269: }
270:
271: /* shell (parent) stuff */
272: if ((flags&XBGND)) { /* async statement */
273: async = j->proc;
274: j_previous = j_current;
275: j_current = j->job;
276: if (flag[FTALKING])
277: j_print(j);
278: } else { /* sync statement */
279: if (!(flags&XPIPE))
280: rv = j_waitj(j, 0);
281: }
282:
283: return rv;
284: }
285:
286: /* wait for last job: pipeline or $() sub-process */
287: int
288: waitlast()
289: {
290: return j_waitj(j_lastj, 0);
291: }
292:
293: /* wait for job to complete or change state */
294: static int
295: j_waitj(aj, intr)
296: Proc *aj;
297: int intr; /* interruptable */
298: {
299: register Proc *j;
300: int rv = 1;
301: int ttysig = 0;
302:
303: #if JOBS
304: if (flag[FMONITOR])
305: sigsetmask(sm_sigchld);
306: #endif
307: /* wait for all members of pipeline */
308: for (j = aj; j != NULL; j = j->prev) {
309: /* wait for job to finish, stop, or ^C of built-in wait */
310: while (j->state == JRUN) {
311: #if JOBS
312: if (flag[FMONITOR])
313: sigpause(sm_default);
314: else
315: #endif
316: #if COHERENT
317: j_sigchld(-1);
318: #else
319: j_sigchld(SIGCHLD);
320: #endif
321: if (sigtraps[SIGINT].set && intr)
322: goto Break;
323: }
324: if (j->state == JEXIT) { /* exit termination */
325: if (!(j->flags&XPIPEO))
326: rv = WEXITSTATUS(j->status);
327: j->notify = 0;
328: } else
329: if (j->state == JSIGNAL) { /* signalled to death */
330: if (WTERMSIG(j->status) == SIGINT ||
331: WTERMSIG(j->status) == SIGPIPE && (j->flags&XPIPEO))
332: j->notify = 0;
333: if (WTERMSIG(j->status) == SIGINT ||
334: WTERMSIG(j->status) == SIGQUIT)
335: ttysig = 1;
336: } else
337: #if JOBS
338: if (j->state == JSTOP)
339: if (WSTOPSIG(j->status) == SIGTSTP)
340: ttysig = 1;
341: #else
342: ;
343: #endif
344: }
345:
346: /* compute total child time for time statement */
347: for (j = aj; j != NULL; j = j->prev)
348: j_utime += j->utime, j_stime += j->stime;
349:
350: /* find new current job */
351: #if JOBS
352: if (aj->state == JSTOP) {
353: j_previous = j_current;
354: j_current = aj->job;
355: } else {
356: #else
357: if (1) {
358: #endif
359: int hijob = 0;
360:
361: /* todo: this needs to be done in j_notify */
362: /* todo: figure out what to do with j_previous */
363: j_current = 0;
364: for (j = procs; j != NULL; j = j->next)
365: if ((j->state == JRUN || j->state == JSTOP)
366: && j->job > hijob) {
367: hijob = j->job;
368: j_current = j->job;
369: }
370: }
371:
372: Break:
373: #if JOBS
374: if (flag[FMONITOR]) {
375: /* reset shell job control state */
376: sigsetmask(sm_default);
377: tcsetpgrp(ttyfd, our_pgrp);
378: }
379: #endif
380: if (ttysig)
381: fputc('\n', shlout);
382: j_notify();
383:
384: return rv;
385: }
386:
387: /* SIGCHLD handler to reap children */
388: static void
389: j_sigchld(sig)
390: int sig;
391: {
392: int errno_ = errno;
393: struct tms t0, t1;
394:
395: (void) times(&t0);
396: do {
397: register Proc *j;
398: int pid, status;
399: #if JOBS
400: if (flag[FMONITOR])
401: pid = waitpid(-1, &status, (WNOHANG|WUNTRACED));
402: else
403: #endif
404: pid = wait(&status);
405: if (pid <= 0) /* return if would block (0) ... */
406: break; /* ... or no children or interrupted (-1) */
407: (void) times(&t1);
408:
409: for (j = procs; j != NULL; j = j->next)
410: if (j->state != JFREE && pid == j->proc)
411: goto Found;
412: continue;
413:
414: Found:
415: j->notify = 1;
416: j->status = status;
417: #if JOBS
418: if (WIFSTOPPED(status))
419: j->state = JSTOP;
420: else
421: #endif
422: if (WIFEXITED(status))
423: j->state = JEXIT;
424: else
425: if (WIFSIGNALED(status))
426: j->state = JSIGNAL;
427:
428: /* compute child's time */
429: /* todo: what does a stopped job do? */
430: j->utime = t1.tms_cutime - t0.tms_cutime;
431: j->stime = t1.tms_cstime - t0.tms_cstime;
432: t0 = t1;
433: #if JOBS
434: } while (flag[FMONITOR]);
435: #else
436: } while (0); /* only once if wait()ing */
437: #endif
438:
439: errno = errno_;
440: }
441:
442: /* wait for child, interruptable */
443: int
444: waitfor(job)
445: int job;
446: {
447: register Proc *j;
448:
449: if (job == 0 && j_current == 0)
450: errorf("no current job\n");
451: j = j_search((job == 0) ? j_current : job);
452: if (j == NULL)
453: errorf("no such job: %d\n", job);
454: if (flag[FTALKING])
455: j_print(j);
456: if (e.interactive) { /* flush stdout, shlout */
457: fflush(shf[1]);
458: fflush(shf[2]);
459: }
460: return j_waitj(j, 1);
461: }
462:
463: /* kill (built-in) a job */
464: void
465: j_kill(job, sig)
466: int job;
467: int sig;
468: {
469: register Proc *j;
470:
471: j = j_search(job);
472: if (j == NULL)
473: errorf("cannot find job\n");
474: if (j->pgrp == 0) { /* !flag[FMONITOR] */
475: if (kill(j->proc, sig) < 0) /* todo: all member of pipeline */
476: errorf("kill: %s\n", strerror(errno));
477: #if JOBS
478: } else {
479: if (sig == SIGTERM || sig == SIGHUP)
480: (void) killpg(j->pgrp, SIGCONT);
481: if (killpg(j->pgrp, sig) < 0)
482: errorf("killpg: %s\n", strerror(errno));
483: #endif
484: }
485: }
486:
487: #if JOBS
488:
489: /* fg and bg built-ins */
490: int
491: j_resume(job, bg)
492: int job;
493: int bg;
494: {
495: register Proc *j;
496:
497: j = j_search((job == 0) ? j_current : job);
498: if (j == NULL)
499: errorf("cannot find job\n", job);
500: if (j->pgrp == 0)
501: errorf("job not job-controlled\n");
502:
503: j->state = JRUN;
504: j_print(j);
505: flushshf(2);
506:
507: if (!bg)
508: tcsetpgrp(ttyfd, j->pgrp); /* attach shell to job */
509: if (killpg(j->pgrp, SIGCONT) < 0)
510: errorf("cannot continue job %%%d\n", job);
511: if (!bg)
512: return j_waitj(j, 0);
513: return 0;
514: }
515:
516: #endif
517:
518: /* list jobs for jobs built-in */
519: void
520: j_jobs()
521: {
522: register Proc *j;
523:
524: for (j = procs; j != NULL; j = j->next)
525: if (j->state != JFREE)
526: j_print(j);
527: }
528:
529: /* list jobs for top-level notification */
530: void
531: j_notify()
532: {
533: register Proc *j;
534:
535: for (j = procs; j != NULL; j = j->next) {
536: if (j->state == JEXIT && !flag[FTALKING])
537: j->notify = 0;
538: if (j->state != JFREE && j->notify)
539: j_print(j);
540: if (j->state == JEXIT || j->state == JSIGNAL)
541: j->state = JFREE;
542: j->notify = 0;
543: }
544: }
545:
546: static void
547: j_print(j)
548: register Proc *j;
549: {
550: char buf [64], *s = buf;
551:
552: switch (j->state) {
553: case JRUN:
554: s = "Running";
555: break;
556:
557: #if JOBS
558: case JSTOP:
559: strcpy(buf, "Stopped ");
560: s = strchr(sigtraps[WSTOPSIG(j->status)].mess, '(');
561: if (s != NULL)
562: strcat(buf, s);
563: s = buf;
564: break;
565: #endif
566:
567: case JEXIT: {
568: int rv;
569: rv = WEXITSTATUS(j->status);
570: sprintf(buf, "Done (%d)", rv);
571: if (rv == 0)
572: *strchr(buf, '(') = 0;
573: j->state = JFREE;
574: } break;
575:
576: case JSIGNAL: {
577: int sig = WTERMSIG(j->status);
578: #if COHERENT
579: char *n = NULL;
580: int i;
581: for (i = 0; i < SIGNALS; ++i)
582: if (sigtraps[i].signal == sig) {
583: n = sigtraps[i].mess;
584: break;
585: }
586: #else
587: char *n = sigtraps[sig].mess;
588: #endif
589: if (n != NULL)
590: sprintf(buf, "%s", n);
591: else
592: sprintf(buf, "Signal %d", sig);
593: if (WIFCORED(j->status))
594: strcat(buf, " - core dumped");
595: j->state = JFREE;
596: } break;
597:
598: default:
599: s = "Hideous job state";
600: j->state = JFREE;
601: break;
602: }
603: shellf("%%%-2d%c %5d %-20s %s%s\n", j->job,
604: (j_current==j->job) ? '+' : (j_previous==j->job) ? '-' : ' ',
605: j->proc, s, j->com, (j->flags&XPIPEO) ? "|" : "");
606: }
607:
608: /* convert % sequence to job number */
609: int
610: j_lookup(cp)
611: char *cp;
612: {
613: register Proc *j;
614: int len, job = 0;
615:
616: if (*cp == '%') /* leading % is optional */
617: cp++;
618: switch (*cp) {
619: case '\0':
620: case '+':
621: job = j_current;
622: break;
623:
624: case '-':
625: job = j_previous;
626: break;
627:
628: case '0': case '1': case '2': case '3': case '4':
629: case '5': case '6': case '7': case '8': case '9':
630: job = atoi(cp);
631: break;
632:
633: case '?': /* %?string */
634: for (j = procs; j != NULL; j = j->next)
635: if (j->state != JFREE && strstr(j->com, cp+1) != NULL)
636: job = j->job;
637: break;
638:
639: default: /* %string */
640: len = strlen(cp);
641: for (j = procs; j != NULL; j = j->next)
642: if (j->state != JFREE && strncmp(cp, j->com, len) == 0)
643: job = j->job;
644: break;
645: }
646: if (job == 0)
647: errorf("%s: no such job\n", cp);
648: return job;
649: }
650:
651: /* are any stopped jobs ? */
652: #if JOBS
653: int
654: j_stopped()
655: {
656: register Proc *j;
657:
658: for (j = procs; j != NULL; j = j->next)
659: if (j->state == JSTOP)
660: return 1;
661: return 0;
662: }
663: #endif
664:
665: /* create new job number */
666: static int
667: j_newjob()
668: {
669: register Proc *j;
670: register int max = 0;
671:
672: j_lastjob ++;
673: for (j = procs; j != NULL; j = j->next)
674: if (j->state != JFREE && j->job)
675: if (j->job > max)
676: max = j->job;
677: if (j_lastjob > max)
678: j_lastjob = max + 1;
679: return j_lastjob;
680: }
681:
682: /* search for job by job number */
683: static Proc *
684: j_search(job)
685: int job;
686: {
687: register Proc *j;
688:
689: for (j = procs; j != NULL; j = j->next)
690: if (j->state != JFREE && job == j->job && !(j->flags&XPIPEO))
691: return j;
692: return NULL;
693: }
694:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.