|
|
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.