1*1302Sbill static char *sccsid = "@(#)proc.c 4.1 10/09/80"; 2*1302Sbill 3*1302Sbill #include "sh.h" 4*1302Sbill #include "sh.dir.h" 5*1302Sbill #include "sh.proc.h" 6*1302Sbill #include <wait.h> 7*1302Sbill #include <sys/ioctl.h> 8*1302Sbill 9*1302Sbill /* 10*1302Sbill * C Shell - functions that manage processes, handling hanging, termination 11*1302Sbill */ 12*1302Sbill 13*1302Sbill #define BIGINDEX 9 /* largest desirable job index */ 14*1302Sbill 15*1302Sbill /* 16*1302Sbill * pchild - called at interrupt level by the SIGCHLD signal 17*1302Sbill * indicating that at least one child has terminated or stopped 18*1302Sbill * thus at least one wait system call will definitely return a 19*1302Sbill * childs status. Top level routines (like pwait) must be sure 20*1302Sbill * to mask interrupts when playing with the proclist data structures! 21*1302Sbill */ 22*1302Sbill pchild() 23*1302Sbill { 24*1302Sbill register struct process *pp; 25*1302Sbill register struct process *fp; 26*1302Sbill register int pid; 27*1302Sbill union wait w; 28*1302Sbill int jobflags; 29*1302Sbill #ifdef VMUNIX 30*1302Sbill struct vtimes vt; 31*1302Sbill #endif 32*1302Sbill 33*1302Sbill if (!timesdone) 34*1302Sbill timesdone++, times(&shtimes); 35*1302Sbill loop: 36*1302Sbill pid = wait3(&w.w_status, (setintr ? WNOHANG|WUNTRACED:WNOHANG), 37*1302Sbill #ifndef VMUNIX 38*1302Sbill 0); 39*1302Sbill #else 40*1302Sbill &vt); 41*1302Sbill #endif 42*1302Sbill if (pid <= 0) { 43*1302Sbill if (errno == EINTR) { 44*1302Sbill errno = 0; 45*1302Sbill goto loop; 46*1302Sbill } 47*1302Sbill pnoprocesses = pid == -1; 48*1302Sbill return; 49*1302Sbill } 50*1302Sbill for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) 51*1302Sbill if (pid == pp->p_pid) 52*1302Sbill goto found; 53*1302Sbill goto loop; 54*1302Sbill found: 55*1302Sbill if (pid == atoi(value("child"))) 56*1302Sbill unsetv("child"); 57*1302Sbill pp->p_flags &= ~(PRUNNING|PSTOPPED|PREPORTED); 58*1302Sbill if (WIFSTOPPED(w)) { 59*1302Sbill pp->p_flags |= PSTOPPED; 60*1302Sbill pp->p_reason = w.w_stopsig; 61*1302Sbill } else { 62*1302Sbill if (pp->p_flags & (PTIME|PPTIME) || adrof("time")) { 63*1302Sbill time_t oldcutimes, oldcstimes; 64*1302Sbill oldcutimes = shtimes.tms_cutime; 65*1302Sbill oldcstimes = shtimes.tms_cstime; 66*1302Sbill time(&pp->p_etime); 67*1302Sbill times(&shtimes); 68*1302Sbill pp->p_utime = shtimes.tms_cutime - oldcutimes; 69*1302Sbill pp->p_stime = shtimes.tms_cstime - oldcstimes; 70*1302Sbill } else 71*1302Sbill times(&shtimes); 72*1302Sbill #ifdef VMUNIX 73*1302Sbill pp->p_vtimes = vt; 74*1302Sbill #endif 75*1302Sbill if (WIFSIGNALED(w)) { 76*1302Sbill if (w.w_termsig == SIGINT) 77*1302Sbill pp->p_flags |= PINTERRUPTED; 78*1302Sbill else 79*1302Sbill pp->p_flags |= PSIGNALED; 80*1302Sbill if (w.w_coredump) 81*1302Sbill pp->p_flags |= PDUMPED; 82*1302Sbill pp->p_reason = w.w_termsig; 83*1302Sbill } else { 84*1302Sbill pp->p_reason = w.w_retcode; 85*1302Sbill #ifdef IIASA 86*1302Sbill if (pp->p_reason >= 3) 87*1302Sbill #else 88*1302Sbill if (pp->p_reason != 0) 89*1302Sbill #endif 90*1302Sbill pp->p_flags |= PAEXITED; 91*1302Sbill else 92*1302Sbill pp->p_flags |= PNEXITED; 93*1302Sbill } 94*1302Sbill } 95*1302Sbill jobflags = 0; 96*1302Sbill fp = pp; 97*1302Sbill do { 98*1302Sbill if ((fp->p_flags & (PPTIME|PRUNNING|PSTOPPED)) == 0 && 99*1302Sbill !child && adrof("time") && 100*1302Sbill (fp->p_utime + fp->p_stime) / HZ >= 101*1302Sbill atoi(value("time"))) 102*1302Sbill fp->p_flags |= PTIME; 103*1302Sbill jobflags |= fp->p_flags; 104*1302Sbill } while ((fp = fp->p_friends) != pp); 105*1302Sbill pp->p_flags &= ~PFOREGND; 106*1302Sbill if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { 107*1302Sbill pp->p_flags &= ~PPTIME; 108*1302Sbill pp->p_flags |= PTIME; 109*1302Sbill } 110*1302Sbill if ((jobflags & (PRUNNING|PREPORTED)) == 0) { 111*1302Sbill fp = pp; 112*1302Sbill do { 113*1302Sbill if (fp->p_flags&PSTOPPED) 114*1302Sbill fp->p_flags |= PREPORTED; 115*1302Sbill } while((fp = fp->p_friends) != pp); 116*1302Sbill while(fp->p_pid != fp->p_jobid) 117*1302Sbill fp = fp->p_friends; 118*1302Sbill if (jobflags&PFOREGND) { 119*1302Sbill if (jobflags&PSTOPPED) { 120*1302Sbill if (pcurrent && pcurrent != fp) 121*1302Sbill pprevious = pcurrent; 122*1302Sbill pcurrent = fp; 123*1302Sbill } else 124*1302Sbill pclrcurr(fp); 125*1302Sbill if (jobflags & (PSIGNALED|PSTOPPED|PPTIME) || 126*1302Sbill #ifdef IIASA 127*1302Sbill jobflags & PAEXITED || 128*1302Sbill #endif 129*1302Sbill !eq(dcwd->di_name, fp->p_cwd->di_name)) { 130*1302Sbill if (jobflags & PSTOPPED) 131*1302Sbill printf("\n"); 132*1302Sbill pprint(fp, AREASON|SHELLDIR); 133*1302Sbill } else if ((jobflags & (PTIME|PSTOPPED)) == PTIME) 134*1302Sbill ptprint(fp); 135*1302Sbill } else { 136*1302Sbill if (jobflags&PNOTIFY || adrof("notify")) { 137*1302Sbill printf("\215\n"); 138*1302Sbill pprint(pp, NUMBER|NAME|REASON); 139*1302Sbill if ((jobflags&PSTOPPED) == 0) 140*1302Sbill pflush(pp); 141*1302Sbill } else { 142*1302Sbill if ((jobflags&PSTOPPED) == 0) 143*1302Sbill pclrcurr(fp); 144*1302Sbill fp->p_flags |= PNEEDNOTE; 145*1302Sbill neednote++; 146*1302Sbill } 147*1302Sbill } 148*1302Sbill } 149*1302Sbill goto loop; 150*1302Sbill } 151*1302Sbill 152*1302Sbill pnote() 153*1302Sbill { 154*1302Sbill register struct process *pp; 155*1302Sbill int flags; 156*1302Sbill 157*1302Sbill neednote = 0; 158*1302Sbill for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) { 159*1302Sbill if (pp->p_flags & PNEEDNOTE) { 160*1302Sbill sighold(SIGCHLD); 161*1302Sbill pp->p_flags &= ~PNEEDNOTE; 162*1302Sbill flags = pprint(pp, NUMBER|NAME|REASON); 163*1302Sbill if ((flags&(PRUNNING|PSTOPPED)) == 0) 164*1302Sbill pflush(pp); 165*1302Sbill sigrelse(SIGCHLD); 166*1302Sbill } 167*1302Sbill } 168*1302Sbill } 169*1302Sbill 170*1302Sbill /* 171*1302Sbill * pwait - wait for current job to terminate, maintaining integrity 172*1302Sbill * of current and previous job indicators. 173*1302Sbill */ 174*1302Sbill pwait() 175*1302Sbill { 176*1302Sbill register struct process *fp, *pp; 177*1302Sbill 178*1302Sbill /* 179*1302Sbill * Here's where dead procs get flushed. 180*1302Sbill */ 181*1302Sbill sighold(SIGCHLD); 182*1302Sbill for (pp = (fp = &proclist)->p_next; pp != PNULL; pp = (fp = pp)->p_next) 183*1302Sbill if (pp->p_pid == 0) { 184*1302Sbill fp->p_next = pp->p_next; 185*1302Sbill xfree(pp->p_command); 186*1302Sbill if (pp->p_cwd && --pp->p_cwd->di_count == 0) 187*1302Sbill if (pp->p_cwd->di_next == 0) 188*1302Sbill dfree(pp->p_cwd); 189*1302Sbill xfree((char *)pp); 190*1302Sbill pp = fp; 191*1302Sbill } 192*1302Sbill sigrelse(SIGCHLD); 193*1302Sbill if (setintr) 194*1302Sbill sigignore(SIGINT); 195*1302Sbill pjwait(pcurrjob); 196*1302Sbill } 197*1302Sbill 198*1302Sbill /* 199*1302Sbill * pjwait - wait for a job to finish or become stopped 200*1302Sbill * It is assumed to be in the foreground state (PFOREGND) 201*1302Sbill */ 202*1302Sbill pjwait(pp) 203*1302Sbill register struct process *pp; 204*1302Sbill { 205*1302Sbill register struct process *fp; 206*1302Sbill int jobflags, reason; 207*1302Sbill 208*1302Sbill fp = pp; 209*1302Sbill do { 210*1302Sbill if ((fp->p_flags&(PFOREGND|PRUNNING)) == PRUNNING) 211*1302Sbill printf("BUG: waiting for background job!\n"); 212*1302Sbill } while ((fp = fp->p_friends) != pp); 213*1302Sbill /* 214*1302Sbill * Now keep pausing as long as we are not interrupted (SIGINT), 215*1302Sbill * and the target process, or any of its friends, are running 216*1302Sbill */ 217*1302Sbill fp = pp; 218*1302Sbill for (;;) { 219*1302Sbill sighold(SIGCHLD); 220*1302Sbill jobflags = 0; 221*1302Sbill do 222*1302Sbill jobflags |= fp->p_flags; 223*1302Sbill while((fp = (fp->p_friends)) != pp); 224*1302Sbill if ((jobflags & PRUNNING) == 0) 225*1302Sbill break; 226*1302Sbill sigpause(SIGCHLD); 227*1302Sbill } 228*1302Sbill sigrelse(SIGCHLD); 229*1302Sbill if (tpgrp > 0) 230*1302Sbill ioctl(FSHTTY, TIOCSPGRP, &tpgrp); /* get tty back */ 231*1302Sbill if (jobflags & PSTOPPED) 232*1302Sbill return; 233*1302Sbill if ((jobflags&PINTERRUPTED) && setintr && 234*1302Sbill (!gointr || !eq(gointr, "-"))) { 235*1302Sbill pflush(pp); 236*1302Sbill pintr(); 237*1302Sbill /*NOTREACHED*/ 238*1302Sbill } 239*1302Sbill reason = 0; 240*1302Sbill fp = pp; 241*1302Sbill do { 242*1302Sbill if (fp->p_reason) 243*1302Sbill reason = fp->p_flags & (PSIGNALED|PINTERRUPTED) ? 244*1302Sbill fp->p_reason | QUOTE : fp->p_reason; 245*1302Sbill } while ((fp = fp->p_friends) != pp); 246*1302Sbill set("status", putn(reason)); 247*1302Sbill if (reason && exiterr) 248*1302Sbill exitstat(); 249*1302Sbill pflush(pp); 250*1302Sbill } 251*1302Sbill 252*1302Sbill /* 253*1302Sbill * dowait - wait for all processes to finish 254*1302Sbill */ 255*1302Sbill dowait() 256*1302Sbill { 257*1302Sbill register struct process *pp; 258*1302Sbill 259*1302Sbill pjobs++; 260*1302Sbill if (setintr) 261*1302Sbill sigrelse(SIGINT); 262*1302Sbill loop: 263*1302Sbill sighold(SIGCHLD); 264*1302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 265*1302Sbill if (pp->p_pid && pp->p_pid == pp->p_jobid && 266*1302Sbill pp->p_flags&PRUNNING) { 267*1302Sbill sigpause(SIGCHLD); 268*1302Sbill goto loop; 269*1302Sbill } 270*1302Sbill sigrelse(SIGCHLD); 271*1302Sbill pjobs = 0; 272*1302Sbill } 273*1302Sbill 274*1302Sbill /* 275*1302Sbill * pflushall - flush all jobs from list (e.g. at fork()) 276*1302Sbill */ 277*1302Sbill pflushall() 278*1302Sbill { 279*1302Sbill register struct process *pp; 280*1302Sbill 281*1302Sbill for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) 282*1302Sbill if (pp->p_pid) 283*1302Sbill pflush(pp); 284*1302Sbill } 285*1302Sbill 286*1302Sbill /* 287*1302Sbill * pflush - flag all process structures in the same job as the 288*1302Sbill * the argument process for deletion. The actual free of the 289*1302Sbill * space is not done here since pflush is called at interrupt level. 290*1302Sbill */ 291*1302Sbill pflush(pp) 292*1302Sbill register struct process *pp; 293*1302Sbill { 294*1302Sbill register struct process *np; 295*1302Sbill register int index; 296*1302Sbill 297*1302Sbill if (pp->p_pid == 0) { 298*1302Sbill printf("BUG: process flushed twice"); 299*1302Sbill return; 300*1302Sbill } 301*1302Sbill while (pp->p_pid != pp->p_jobid) 302*1302Sbill pp = pp->p_friends; 303*1302Sbill pclrcurr(pp); 304*1302Sbill if (pp == pcurrjob) 305*1302Sbill pcurrjob = 0; 306*1302Sbill index = pp->p_index; 307*1302Sbill np = pp; 308*1302Sbill do { 309*1302Sbill np->p_index = np->p_pid = 0; 310*1302Sbill np->p_flags &= ~PNEEDNOTE; 311*1302Sbill } while ((np = np->p_friends) != pp); 312*1302Sbill if (index == pmaxindex) { 313*1302Sbill for (np = proclist.p_next, index = 0; np; np = np->p_next) 314*1302Sbill if (np->p_index > index) 315*1302Sbill index = np->p_index; 316*1302Sbill pmaxindex = index; 317*1302Sbill } 318*1302Sbill } 319*1302Sbill 320*1302Sbill /* 321*1302Sbill * pclrcurr - make sure the given job is not the current or previous job; 322*1302Sbill * pp MUST be the job leader 323*1302Sbill */ 324*1302Sbill pclrcurr(pp) 325*1302Sbill register struct process *pp; 326*1302Sbill { 327*1302Sbill 328*1302Sbill if (pp == pcurrent) 329*1302Sbill if (pprevious != PNULL) { 330*1302Sbill pcurrent = pprevious; 331*1302Sbill pprevious = pgetcurr(pp); 332*1302Sbill } else { 333*1302Sbill pcurrent = pgetcurr(pp); 334*1302Sbill pprevious = pgetcurr(pp); 335*1302Sbill } 336*1302Sbill else if (pp == pprevious) 337*1302Sbill pprevious = pgetcurr(pp); 338*1302Sbill } 339*1302Sbill 340*1302Sbill /* +4 here is 1 for '\0', 1 ea for << >& >> */ 341*1302Sbill char command[PMAXLEN+4]; 342*1302Sbill int cmdlen; 343*1302Sbill char *cmdp; 344*1302Sbill /* 345*1302Sbill * palloc - allocate a process structure and fill it up. 346*1302Sbill * an important assumption is made that the process is running. 347*1302Sbill */ 348*1302Sbill palloc(pid, t) 349*1302Sbill int pid; 350*1302Sbill register struct command *t; 351*1302Sbill { 352*1302Sbill register struct process *pp; 353*1302Sbill int i; 354*1302Sbill 355*1302Sbill pp = (struct process *)calloc(1, sizeof(struct process)); 356*1302Sbill pp->p_pid = pid; 357*1302Sbill pp->p_flags = t->t_dflg & FAND ? PRUNNING : PRUNNING|PFOREGND; 358*1302Sbill if (t->t_dflg & FTIME) 359*1302Sbill pp->p_flags |= PPTIME; 360*1302Sbill cmdp = command; 361*1302Sbill cmdlen = 0; 362*1302Sbill padd(t); 363*1302Sbill *cmdp++ = 0; 364*1302Sbill if (t->t_dflg & FPOU) { 365*1302Sbill pp->p_flags |= PPOU; 366*1302Sbill if (t->t_dflg & FDIAG) 367*1302Sbill pp->p_flags |= PDIAG; 368*1302Sbill } 369*1302Sbill pp->p_command = savestr(command); 370*1302Sbill if (pcurrjob) { 371*1302Sbill struct process *fp; 372*1302Sbill /* careful here with interrupt level */ 373*1302Sbill pp->p_cwd = 0; 374*1302Sbill pp->p_index = pcurrjob->p_index; 375*1302Sbill pp->p_friends = pcurrjob; 376*1302Sbill pp->p_jobid = pcurrjob->p_pid; 377*1302Sbill for (fp = pcurrjob; fp->p_friends != pcurrjob; fp = fp->p_friends) 378*1302Sbill ; 379*1302Sbill fp->p_friends = pp; 380*1302Sbill } else { 381*1302Sbill pcurrjob = pp; 382*1302Sbill pp->p_jobid = pid; 383*1302Sbill pp->p_friends = pp; 384*1302Sbill pp->p_cwd = dcwd; 385*1302Sbill dcwd->di_count++; 386*1302Sbill if (pmaxindex < BIGINDEX) 387*1302Sbill pp->p_index = ++pmaxindex; 388*1302Sbill else { 389*1302Sbill struct process *np; 390*1302Sbill 391*1302Sbill for (i = 1; ; i++) { 392*1302Sbill for (np = proclist.p_next; np; np = np->p_next) 393*1302Sbill if (np->p_index == i) 394*1302Sbill goto tryagain; 395*1302Sbill pmaxindex = pp->p_index = i; 396*1302Sbill break; 397*1302Sbill tryagain:; 398*1302Sbill } 399*1302Sbill } 400*1302Sbill if (pcurrent == PNULL) 401*1302Sbill pcurrent = pp; 402*1302Sbill else if (pprevious == PNULL) 403*1302Sbill pprevious = pp; 404*1302Sbill } 405*1302Sbill pp->p_next = proclist.p_next; 406*1302Sbill proclist.p_next = pp; 407*1302Sbill time(&pp->p_btime); 408*1302Sbill } 409*1302Sbill 410*1302Sbill padd(t) 411*1302Sbill register struct command *t; 412*1302Sbill { 413*1302Sbill char **argp; 414*1302Sbill 415*1302Sbill if (t == 0) 416*1302Sbill return; 417*1302Sbill switch (t->t_dtyp) { 418*1302Sbill 419*1302Sbill case TPAR: 420*1302Sbill pads("( "); 421*1302Sbill padd(t->t_dspr); 422*1302Sbill pads(" )"); 423*1302Sbill break; 424*1302Sbill 425*1302Sbill case TCOM: 426*1302Sbill for (argp = t->t_dcom; *argp; argp++) { 427*1302Sbill pads(*argp); 428*1302Sbill if (argp[1]) 429*1302Sbill pads(" "); 430*1302Sbill } 431*1302Sbill break; 432*1302Sbill 433*1302Sbill case TFIL: 434*1302Sbill padd(t->t_dcar); 435*1302Sbill pads(" | "); 436*1302Sbill padd(t->t_dcdr); 437*1302Sbill return; 438*1302Sbill 439*1302Sbill case TLST: 440*1302Sbill padd(t->t_dcar); 441*1302Sbill pads("; "); 442*1302Sbill padd(t->t_dcdr); 443*1302Sbill return; 444*1302Sbill } 445*1302Sbill if ((t->t_dflg & FPIN) == 0 && t->t_dlef) { 446*1302Sbill pads((t->t_dflg & FHERE) ? " << " : " < "); 447*1302Sbill pads(t->t_dlef); 448*1302Sbill } 449*1302Sbill if ((t->t_dflg & FPOU) == 0 && t->t_drit) { 450*1302Sbill pads((t->t_dflg & FCAT) ? " >>" : " >"); 451*1302Sbill if (t->t_dflg & FDIAG) 452*1302Sbill pads("&"); 453*1302Sbill pads(" "); 454*1302Sbill pads(t->t_drit); 455*1302Sbill } 456*1302Sbill } 457*1302Sbill 458*1302Sbill pads(cp) 459*1302Sbill char *cp; 460*1302Sbill { 461*1302Sbill register int i = strlen(cp); 462*1302Sbill 463*1302Sbill if (cmdlen >= PMAXLEN) 464*1302Sbill return; 465*1302Sbill if (cmdlen + i >= PMAXLEN) { 466*1302Sbill strcpy(cmdp, " ..."); 467*1302Sbill cmdlen = PMAXLEN; 468*1302Sbill cmdp += 4; 469*1302Sbill return; 470*1302Sbill } 471*1302Sbill strcpy(cmdp, cp); 472*1302Sbill cmdp += i; 473*1302Sbill cmdlen += i; 474*1302Sbill } 475*1302Sbill 476*1302Sbill /* 477*1302Sbill * psavejob - temporarily save the current job on a one level stack 478*1302Sbill * so another job can be created. Used for { } in exp6 479*1302Sbill * and `` in globbing. 480*1302Sbill */ 481*1302Sbill psavejob() 482*1302Sbill { 483*1302Sbill 484*1302Sbill pholdjob = pcurrjob; 485*1302Sbill pcurrjob = PNULL; 486*1302Sbill } 487*1302Sbill 488*1302Sbill /* 489*1302Sbill * prestjob - opposite of psavejob. This may be missed if we are interrupted 490*1302Sbill * somewhere, but pendjob cleans up anyway. 491*1302Sbill */ 492*1302Sbill prestjob() 493*1302Sbill { 494*1302Sbill 495*1302Sbill pcurrjob = pholdjob; 496*1302Sbill pholdjob = PNULL; 497*1302Sbill } 498*1302Sbill 499*1302Sbill /* 500*1302Sbill * pendjob - indicate that a job (set of commands) has been completed 501*1302Sbill * or is about to begin. 502*1302Sbill */ 503*1302Sbill pendjob() 504*1302Sbill { 505*1302Sbill register struct process *pp, *tp; 506*1302Sbill 507*1302Sbill if (pcurrjob && (pcurrjob->p_flags&(PFOREGND|PSTOPPED)) == 0) { 508*1302Sbill pp = pcurrjob; 509*1302Sbill while (pp->p_pid != pp->p_jobid) 510*1302Sbill pp = pp->p_friends; 511*1302Sbill printf("[%d]", pp->p_index); 512*1302Sbill tp = pp; 513*1302Sbill do { 514*1302Sbill printf(" %d", pp->p_pid); 515*1302Sbill pp = pp->p_friends; 516*1302Sbill } while (pp != tp); 517*1302Sbill printf("\n"); 518*1302Sbill } 519*1302Sbill pholdjob = pcurrjob = 0; 520*1302Sbill } 521*1302Sbill 522*1302Sbill /* 523*1302Sbill * pprint - print a job 524*1302Sbill */ 525*1302Sbill pprint(pp, flag) 526*1302Sbill register struct process *pp; 527*1302Sbill { 528*1302Sbill register status, reason; 529*1302Sbill struct process *tp; 530*1302Sbill extern char *linp, linbuf[]; 531*1302Sbill int jobflags, pstatus; 532*1302Sbill char *format; 533*1302Sbill 534*1302Sbill while (pp->p_pid != pp->p_jobid) 535*1302Sbill pp = pp->p_friends; 536*1302Sbill if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { 537*1302Sbill pp->p_flags &= ~PPTIME; 538*1302Sbill pp->p_flags |= PTIME; 539*1302Sbill } 540*1302Sbill tp = pp; 541*1302Sbill status = reason = -1; 542*1302Sbill jobflags = 0; 543*1302Sbill do { 544*1302Sbill jobflags |= pp->p_flags; 545*1302Sbill pstatus = pp->p_flags & PALLSTATES; 546*1302Sbill if (tp != pp && linp != linbuf && !(flag&FANCY) && 547*1302Sbill (pstatus == status && pp->p_reason == reason || 548*1302Sbill !(flag&REASON))) 549*1302Sbill printf(" "); 550*1302Sbill else { 551*1302Sbill if (tp != pp && linp != linbuf) 552*1302Sbill printf("\n"); 553*1302Sbill if(flag&NUMBER) 554*1302Sbill if (pp == tp) 555*1302Sbill printf("[%d]%s %c ", pp->p_index, 556*1302Sbill pp->p_index < 10 ? " " : "", 557*1302Sbill pp==pcurrent ? '+' : 558*1302Sbill (pp == pprevious ? '-' : ' ')); 559*1302Sbill else 560*1302Sbill printf(" "); 561*1302Sbill if (flag&FANCY) 562*1302Sbill printf("%5d ", pp->p_pid); 563*1302Sbill if (flag&(REASON|AREASON)) { 564*1302Sbill if (flag&NAME) 565*1302Sbill format = "%-21s"; 566*1302Sbill else 567*1302Sbill format = "%s"; 568*1302Sbill if (pstatus == status) 569*1302Sbill if (pp->p_reason == reason) { 570*1302Sbill printf(format, ""); 571*1302Sbill goto prcomd; 572*1302Sbill } else 573*1302Sbill reason = pp->p_reason; 574*1302Sbill else { 575*1302Sbill status = pstatus; 576*1302Sbill reason = pp->p_reason; 577*1302Sbill } 578*1302Sbill switch (status) { 579*1302Sbill 580*1302Sbill case PRUNNING: 581*1302Sbill printf(format, "Running "); 582*1302Sbill break; 583*1302Sbill 584*1302Sbill case PINTERRUPTED: 585*1302Sbill case PSTOPPED: 586*1302Sbill case PSIGNALED: 587*1302Sbill if (flag&REASON || reason != SIGINT || 588*1302Sbill reason != SIGPIPE) 589*1302Sbill printf(format, mesg[pp->p_reason].pname); 590*1302Sbill break; 591*1302Sbill 592*1302Sbill case PNEXITED: 593*1302Sbill case PAEXITED: 594*1302Sbill if (flag & REASON) 595*1302Sbill if (pp->p_reason) 596*1302Sbill printf("Exit %-16d", pp->p_reason); 597*1302Sbill else 598*1302Sbill printf(format, "Done"); 599*1302Sbill break; 600*1302Sbill 601*1302Sbill default: 602*1302Sbill printf("BUG: status=%-9o", status); 603*1302Sbill } 604*1302Sbill } 605*1302Sbill } 606*1302Sbill prcomd: 607*1302Sbill if (flag&NAME) { 608*1302Sbill printf("%s", pp->p_command); 609*1302Sbill if (pp->p_flags & PPOU) 610*1302Sbill printf(" |"); 611*1302Sbill if (pp->p_flags & PDIAG) 612*1302Sbill printf("&"); 613*1302Sbill } 614*1302Sbill if (flag&(REASON|AREASON) && pp->p_flags&PDUMPED) 615*1302Sbill printf(" (core dumped)"); 616*1302Sbill if (tp == pp->p_friends) { 617*1302Sbill if (flag&ERSAND) 618*1302Sbill printf(" &"); 619*1302Sbill if (flag&JOBDIR && 620*1302Sbill !eq(tp->p_cwd->di_name, dcwd->di_name)) { 621*1302Sbill printf(" (wd: "); 622*1302Sbill dtildepr(value("home"), tp->p_cwd->di_name); 623*1302Sbill printf(")"); 624*1302Sbill } 625*1302Sbill } 626*1302Sbill if (pp->p_flags&PPTIME && !(status&(PSTOPPED|PRUNNING))) { 627*1302Sbill if (linp != linbuf) 628*1302Sbill printf("\n\t"); 629*1302Sbill #ifndef VMUNIX 630*1302Sbill ptimes(pp->p_utime, pp->p_stime, pp->p_etime-pp->p_btime); 631*1302Sbill #else 632*1302Sbill pvtimes(&zvms, &pp->p_vtimes, pp->p_etime - pp->p_btime); 633*1302Sbill #endif 634*1302Sbill } 635*1302Sbill if (tp == pp->p_friends) { 636*1302Sbill if (linp != linbuf) 637*1302Sbill printf("\n"); 638*1302Sbill if (flag&SHELLDIR && !eq(tp->p_cwd->di_name, dcwd->di_name)) { 639*1302Sbill printf("(wd now: "); 640*1302Sbill dtildepr(value("home"), dcwd->di_name); 641*1302Sbill printf(")\n"); 642*1302Sbill } 643*1302Sbill } 644*1302Sbill } while ((pp = pp->p_friends) != tp); 645*1302Sbill if (jobflags&PTIME && (jobflags&(PSTOPPED|PRUNNING)) == 0) { 646*1302Sbill if (jobflags & NUMBER) 647*1302Sbill printf(" "); 648*1302Sbill ptprint(tp); 649*1302Sbill } 650*1302Sbill return (jobflags); 651*1302Sbill } 652*1302Sbill 653*1302Sbill ptprint(tp) 654*1302Sbill register struct process *tp; 655*1302Sbill { 656*1302Sbill time_t tetime = 0; 657*1302Sbill #ifdef VMUNIX 658*1302Sbill struct vtimes vmt; 659*1302Sbill #else 660*1302Sbill time_t tutime = 0, tstime = 0; 661*1302Sbill #endif 662*1302Sbill register struct process *pp = tp; 663*1302Sbill 664*1302Sbill vmt = zvms; 665*1302Sbill do { 666*1302Sbill #ifdef VMUNIX 667*1302Sbill vmsadd(&vmt, &pp->p_vtimes); 668*1302Sbill #else 669*1302Sbill tutime += pp->p_utime; 670*1302Sbill tstime += pp->p_stime; 671*1302Sbill #endif 672*1302Sbill if (pp->p_etime - pp->p_btime > tetime) 673*1302Sbill tetime = pp->p_etime - pp->p_btime; 674*1302Sbill } while ((pp = pp->p_friends) != tp); 675*1302Sbill #ifdef VMUNIX 676*1302Sbill pvtimes(&zvms, &vmt, tetime); 677*1302Sbill #else 678*1302Sbill ptimes(tutime, tstime, tetime); 679*1302Sbill #endif 680*1302Sbill } 681*1302Sbill 682*1302Sbill /* 683*1302Sbill * dojobs - print all jobs 684*1302Sbill */ 685*1302Sbill dojobs(v) 686*1302Sbill char **v; 687*1302Sbill { 688*1302Sbill register struct process *pp; 689*1302Sbill register int flag = NUMBER|NAME|REASON; 690*1302Sbill int i; 691*1302Sbill 692*1302Sbill if (chkstop) 693*1302Sbill chkstop = 2; 694*1302Sbill if (*++v) { 695*1302Sbill if (v[1] || !eq(*v, "-l")) 696*1302Sbill error("Usage: jobs [ -l ]"); 697*1302Sbill flag |= FANCY|JOBDIR; 698*1302Sbill } 699*1302Sbill for (i = 1; i <= pmaxindex; i++) 700*1302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 701*1302Sbill if (pp->p_index == i && pp->p_pid == pp->p_jobid) { 702*1302Sbill pp->p_flags &= ~PNEEDNOTE; 703*1302Sbill if (!(pprint(pp, flag) & (PRUNNING|PSTOPPED))) 704*1302Sbill pflush(pp); 705*1302Sbill break; 706*1302Sbill } 707*1302Sbill } 708*1302Sbill 709*1302Sbill /* 710*1302Sbill * dofg - builtin - put the job into the foreground 711*1302Sbill */ 712*1302Sbill dofg(v) 713*1302Sbill char **v; 714*1302Sbill { 715*1302Sbill register struct process *pp; 716*1302Sbill 717*1302Sbill okpcntl(); 718*1302Sbill ++v; 719*1302Sbill do { 720*1302Sbill pp = pfind(*v); 721*1302Sbill pstart(pp, 1); 722*1302Sbill if (setintr) 723*1302Sbill sigignore(SIGINT); 724*1302Sbill pjwait(pp); 725*1302Sbill } while (*v && *++v); 726*1302Sbill } 727*1302Sbill 728*1302Sbill /* 729*1302Sbill * %... - builtin - put the job into the foreground 730*1302Sbill */ 731*1302Sbill dofg1(v) 732*1302Sbill char **v; 733*1302Sbill { 734*1302Sbill register struct process *pp; 735*1302Sbill 736*1302Sbill okpcntl(); 737*1302Sbill pp = pfind(v[0]); 738*1302Sbill pstart(pp, 1); 739*1302Sbill if (setintr) 740*1302Sbill sigignore(SIGINT); 741*1302Sbill pjwait(pp); 742*1302Sbill } 743*1302Sbill 744*1302Sbill /* 745*1302Sbill * dobg - builtin - put the job into the background 746*1302Sbill */ 747*1302Sbill dobg(v) 748*1302Sbill char **v; 749*1302Sbill { 750*1302Sbill register struct process *pp; 751*1302Sbill 752*1302Sbill okpcntl(); 753*1302Sbill ++v; 754*1302Sbill do { 755*1302Sbill pp = pfind(*v); 756*1302Sbill pstart(pp, 0); 757*1302Sbill } while (*v && *++v); 758*1302Sbill } 759*1302Sbill 760*1302Sbill /* 761*1302Sbill * %... & - builtin - put the job into the background 762*1302Sbill */ 763*1302Sbill dobg1(v) 764*1302Sbill char **v; 765*1302Sbill { 766*1302Sbill register struct process *pp; 767*1302Sbill 768*1302Sbill pp = pfind(v[0]); 769*1302Sbill pstart(pp, 0); 770*1302Sbill } 771*1302Sbill 772*1302Sbill /* 773*1302Sbill * dostop - builtin - stop the job 774*1302Sbill */ 775*1302Sbill dostop(v) 776*1302Sbill char **v; 777*1302Sbill { 778*1302Sbill 779*1302Sbill pkill(++v, SIGSTOP); 780*1302Sbill } 781*1302Sbill 782*1302Sbill /* 783*1302Sbill * dokill - builtin - superset of kill (1) 784*1302Sbill */ 785*1302Sbill dokill(v) 786*1302Sbill char **v; 787*1302Sbill { 788*1302Sbill register int signum; 789*1302Sbill register char *name; 790*1302Sbill 791*1302Sbill v++; 792*1302Sbill if (v[0] && v[0][0] == '-') { 793*1302Sbill if (v[0][1] == 'l') { 794*1302Sbill for (signum = 1; signum <= NSIG; signum++) { 795*1302Sbill if (name = mesg[signum].iname) 796*1302Sbill printf("%s ", name); 797*1302Sbill if (signum == 16) 798*1302Sbill printf("\n"); 799*1302Sbill } 800*1302Sbill printf("\n"); 801*1302Sbill return; 802*1302Sbill } 803*1302Sbill if (digit(v[0][1])) { 804*1302Sbill signum = atoi(v[0]+1); 805*1302Sbill if (signum < 1 || signum > NSIG) 806*1302Sbill bferr("Bad signal number"); 807*1302Sbill } else { 808*1302Sbill name = &v[0][1]; 809*1302Sbill for (signum = 1; signum <= NSIG; signum++) 810*1302Sbill if (mesg[signum].iname && 811*1302Sbill eq(name, mesg[signum].iname)) 812*1302Sbill goto gotsig; 813*1302Sbill setname(name); 814*1302Sbill bferr("Unknown signal; kill -l lists signals"); 815*1302Sbill } 816*1302Sbill gotsig: 817*1302Sbill v++; 818*1302Sbill } else 819*1302Sbill signum = SIGTERM; 820*1302Sbill pkill(v, signum); 821*1302Sbill } 822*1302Sbill 823*1302Sbill pkill(v, signum) 824*1302Sbill char **v; 825*1302Sbill int signum; 826*1302Sbill { 827*1302Sbill register struct process *pp, *np; 828*1302Sbill register int jobflags = 0; 829*1302Sbill int pid; 830*1302Sbill extern char *sys_errlist[]; 831*1302Sbill int err = 0; 832*1302Sbill 833*1302Sbill if (setintr) 834*1302Sbill sighold(SIGINT); 835*1302Sbill sighold(SIGCHLD); 836*1302Sbill while (*v) { 837*1302Sbill if (**v == '%') { 838*1302Sbill np = pp = pfind(*v); 839*1302Sbill do 840*1302Sbill jobflags |= np->p_flags; 841*1302Sbill while ((np = np->p_friends) != pp); 842*1302Sbill switch (signum) { 843*1302Sbill 844*1302Sbill case SIGSTOP: 845*1302Sbill case SIGTSTP: 846*1302Sbill case SIGTTIN: 847*1302Sbill case SIGTTOU: 848*1302Sbill if ((jobflags & PRUNNING) == 0) { 849*1302Sbill printf("%s: Already stopped\n", *v); 850*1302Sbill err++; 851*1302Sbill goto cont; 852*1302Sbill } 853*1302Sbill } 854*1302Sbill killpg(pp->p_jobid, signum); 855*1302Sbill if (signum == SIGTERM || signum == SIGHUP) 856*1302Sbill killpg(pp->p_jobid, SIGCONT); 857*1302Sbill } else if (!digit(**v)) 858*1302Sbill bferr("Arguments should be jobs or process id's"); 859*1302Sbill else { 860*1302Sbill pid = atoi(*v); 861*1302Sbill if (kill(pid, signum) < 0) { 862*1302Sbill printf("%d: ", pid); 863*1302Sbill printf("%s\n", sys_errlist[errno]); 864*1302Sbill err++; 865*1302Sbill goto cont; 866*1302Sbill } 867*1302Sbill if (signum == SIGTERM || signum == SIGHUP) 868*1302Sbill kill(pid, SIGCONT); 869*1302Sbill } 870*1302Sbill cont: 871*1302Sbill v++; 872*1302Sbill } 873*1302Sbill sigrelse(SIGCHLD); 874*1302Sbill if (setintr) 875*1302Sbill sigrelse(SIGINT); 876*1302Sbill if (err) 877*1302Sbill error(NOSTR); 878*1302Sbill } 879*1302Sbill 880*1302Sbill /* 881*1302Sbill * pstart - start the job in foreground/background 882*1302Sbill */ 883*1302Sbill pstart(pp, foregnd) 884*1302Sbill register struct process *pp; 885*1302Sbill int foregnd; 886*1302Sbill { 887*1302Sbill register struct process *np; 888*1302Sbill int jobflags = 0; 889*1302Sbill 890*1302Sbill sighold(SIGCHLD); 891*1302Sbill np = pp; 892*1302Sbill do { 893*1302Sbill jobflags |= np->p_flags; 894*1302Sbill if (np->p_flags&(PRUNNING|PSTOPPED)) { 895*1302Sbill np->p_flags |= PRUNNING; 896*1302Sbill np->p_flags &= ~PSTOPPED; 897*1302Sbill if (foregnd) 898*1302Sbill np->p_flags |= PFOREGND; 899*1302Sbill else 900*1302Sbill np->p_flags &= ~PFOREGND; 901*1302Sbill } 902*1302Sbill } while((np = np->p_friends) != pp); 903*1302Sbill pprint(pp, foregnd ? NAME|JOBDIR : NUMBER|NAME|AMPERSAND); 904*1302Sbill if (foregnd) 905*1302Sbill ioctl(FSHTTY, TIOCSPGRP, &pp->p_jobid); 906*1302Sbill if (jobflags&PSTOPPED) 907*1302Sbill killpg(pp->p_jobid, SIGCONT); 908*1302Sbill sigrelse(SIGCHLD); 909*1302Sbill } 910*1302Sbill 911*1302Sbill panystop(neednl) 912*1302Sbill { 913*1302Sbill register struct process *pp; 914*1302Sbill 915*1302Sbill chkstop = 2; 916*1302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 917*1302Sbill if (pp->p_flags & PSTOPPED) 918*1302Sbill error("\nThere are stopped jobs" + 1 - neednl); 919*1302Sbill } 920*1302Sbill 921*1302Sbill struct process * 922*1302Sbill pfind(cp) 923*1302Sbill char *cp; 924*1302Sbill { 925*1302Sbill register struct process *pp, *np; 926*1302Sbill 927*1302Sbill if (cp == 0 || cp[1] == 0 || eq(cp, "%%") || eq(cp, "%+")) { 928*1302Sbill if (pcurrent == PNULL) 929*1302Sbill bferr("No current job"); 930*1302Sbill return (pcurrent); 931*1302Sbill } 932*1302Sbill if (eq(cp, "%-") || eq(cp, "%#")) { 933*1302Sbill if (pprevious == PNULL) 934*1302Sbill bferr("No previous job"); 935*1302Sbill return (pprevious); 936*1302Sbill } 937*1302Sbill if (digit(cp[1])) { 938*1302Sbill int index = atoi(cp+1); 939*1302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 940*1302Sbill if (pp->p_index == index && pp->p_pid == pp->p_jobid) 941*1302Sbill return (pp); 942*1302Sbill bferr("No such job"); 943*1302Sbill } 944*1302Sbill np = PNULL; 945*1302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 946*1302Sbill if (pp->p_pid == pp->p_jobid) { 947*1302Sbill if (cp[1] == '?') { 948*1302Sbill register char *dp; 949*1302Sbill for (dp = pp->p_command; *dp; dp++) { 950*1302Sbill if (*dp != cp[2]) 951*1302Sbill continue; 952*1302Sbill if (prefix(cp+2, dp)) 953*1302Sbill goto match; 954*1302Sbill } 955*1302Sbill } else if (prefix(cp+1, pp->p_command)) { 956*1302Sbill match: 957*1302Sbill if (np) 958*1302Sbill bferr("Ambiguous"); 959*1302Sbill np = pp; 960*1302Sbill } 961*1302Sbill } 962*1302Sbill if (np) 963*1302Sbill return (np); 964*1302Sbill if (cp[1] == '?') 965*1302Sbill bferr("No job matches pattern"); 966*1302Sbill else 967*1302Sbill bferr("No such job"); 968*1302Sbill } 969*1302Sbill 970*1302Sbill /* 971*1302Sbill * pgetcurr - find a job that is not pp and ``most recent'' 972*1302Sbill */ 973*1302Sbill struct process * 974*1302Sbill pgetcurr(pp) 975*1302Sbill register struct process *pp; 976*1302Sbill { 977*1302Sbill register struct process *np; 978*1302Sbill 979*1302Sbill for (np = proclist.p_next; np; np = np->p_next) 980*1302Sbill if (np != pcurrent && np != pp && np->p_pid && 981*1302Sbill np->p_pid == np->p_jobid) { 982*1302Sbill return (np); 983*1302Sbill } 984*1302Sbill return (PNULL); 985*1302Sbill } 986*1302Sbill 987*1302Sbill /* 988*1302Sbill * donotify - flag the job so as to report termination asynchronously 989*1302Sbill */ 990*1302Sbill donotify(v) 991*1302Sbill char **v; 992*1302Sbill { 993*1302Sbill register struct process *pp; 994*1302Sbill 995*1302Sbill pp = pfind(*++v); 996*1302Sbill pp->p_flags |= PNOTIFY; 997*1302Sbill } 998*1302Sbill 999*1302Sbill /* 1000*1302Sbill * Do the fork and whatever should be done in the child side that 1001*1302Sbill * should not be done if we are not forking at all (like for simple builtin's) 1002*1302Sbill * Also do everything that needs any signals fiddled with in the parent side 1003*1302Sbill * 1004*1302Sbill * Wanttty tells whether process and/or tty pgrps are to be manipulated: 1005*1302Sbill * -1: leave tty alone; inherit pgrp from parent 1006*1302Sbill * 0: already have tty; manipulate process pgrps only 1007*1302Sbill * 1: want to claim tty; manipulate process and tty pgrps 1008*1302Sbill * It is usually just the value of tpgrp. 1009*1302Sbill */ 1010*1302Sbill pfork(t, wanttty) 1011*1302Sbill struct command *t; /* command we are forking for */ 1012*1302Sbill int wanttty; 1013*1302Sbill { 1014*1302Sbill register int pid; 1015*1302Sbill bool ignint = 0; 1016*1302Sbill int pgrp; 1017*1302Sbill 1018*1302Sbill /* 1019*1302Sbill * A child will be uninterruptible only under very special 1020*1302Sbill * conditions. Remember that the semantics of '&' is 1021*1302Sbill * implemented by disconnecting the process from the tty so 1022*1302Sbill * signals do not need to ignored just for '&'. 1023*1302Sbill * Thus signals are set to default action for children unless: 1024*1302Sbill * we have had an "onintr -" (then specifically ignored) 1025*1302Sbill * we are not playing with signals (inherit action) 1026*1302Sbill */ 1027*1302Sbill if (setintr) 1028*1302Sbill ignint = (tpgrp == -1 && (t->t_dflg&FINT)) 1029*1302Sbill || (gointr && eq(gointr, "-")); 1030*1302Sbill /* 1031*1302Sbill * Hold SIGCHLD until we have the process installed in our table. 1032*1302Sbill */ 1033*1302Sbill sighold(SIGCHLD); 1034*1302Sbill while ((pid = fork()) < 0) 1035*1302Sbill if (setintr == 0) 1036*1302Sbill sleep(FORKSLEEP); 1037*1302Sbill else { 1038*1302Sbill sigrelse(SIGINT); 1039*1302Sbill sigrelse(SIGCHLD); 1040*1302Sbill error("No more processes"); 1041*1302Sbill } 1042*1302Sbill if (pid == 0) { 1043*1302Sbill settimes(); 1044*1302Sbill pgrp = pcurrjob ? pcurrjob->p_jobid : getpid(); 1045*1302Sbill pflushall(); 1046*1302Sbill pcurrjob = PNULL; 1047*1302Sbill timesdone = 0; 1048*1302Sbill child++; 1049*1302Sbill if (setintr) { 1050*1302Sbill setintr = 0; /* until I think otherwise */ 1051*1302Sbill sigrelse(SIGCHLD); 1052*1302Sbill /* 1053*1302Sbill * Children just get blown away on SIGINT, SIGQUIT 1054*1302Sbill * unless "onintr -" seen. 1055*1302Sbill */ 1056*1302Sbill signal(SIGINT, ignint ? SIG_IGN : SIG_DFL); 1057*1302Sbill signal(SIGQUIT, ignint ? SIG_IGN : SIG_DFL); 1058*1302Sbill if (wanttty >= 0) { 1059*1302Sbill /* make stoppable */ 1060*1302Sbill signal(SIGTSTP, SIG_DFL); 1061*1302Sbill signal(SIGTTIN, SIG_DFL); 1062*1302Sbill signal(SIGTTOU, SIG_DFL); 1063*1302Sbill } 1064*1302Sbill signal(SIGTERM, parterm); 1065*1302Sbill } else if (tpgrp == -1 && (t->t_dflg&FINT)) { 1066*1302Sbill signal(SIGINT, SIG_IGN); 1067*1302Sbill signal(SIGQUIT, SIG_IGN); 1068*1302Sbill } 1069*1302Sbill if (wanttty > 0) 1070*1302Sbill ioctl(FSHTTY, TIOCSPGRP, &pgrp); 1071*1302Sbill if (wanttty >= 0 && tpgrp >= 0) 1072*1302Sbill setpgrp(0, pgrp); 1073*1302Sbill if (tpgrp > 0) 1074*1302Sbill tpgrp = 0; /* gave tty away */ 1075*1302Sbill /* 1076*1302Sbill * Nohup and nice apply only to TCOM's but it would be 1077*1302Sbill * nice (?!?) if you could say "nohup (foo;bar)" 1078*1302Sbill * Then the parser would have to know about nice/nohup/time 1079*1302Sbill */ 1080*1302Sbill if (t->t_dflg & FNOHUP) 1081*1302Sbill signal(SIGHUP, SIG_IGN); 1082*1302Sbill if (t->t_dflg & FNICE) { 1083*1302Sbill /* sigh... 1084*1302Sbill nice(20); 1085*1302Sbill nice(-10); 1086*1302Sbill */ 1087*1302Sbill nice(t->t_nice); 1088*1302Sbill } 1089*1302Sbill 1090*1302Sbill } else { 1091*1302Sbill palloc(pid, t); 1092*1302Sbill sigrelse(SIGCHLD); 1093*1302Sbill } 1094*1302Sbill 1095*1302Sbill return (pid); 1096*1302Sbill } 1097*1302Sbill 1098*1302Sbill okpcntl() 1099*1302Sbill { 1100*1302Sbill 1101*1302Sbill if (tpgrp == -1) 1102*1302Sbill error("No job control in this shell"); 1103*1302Sbill if (tpgrp == 0) 1104*1302Sbill error("No job control in subshells"); 1105*1302Sbill } 1106