1*3213Swnj static char *sccsid = "@(#)proc.c 4.3 03/11/81"; 21302Sbill 31302Sbill #include "sh.h" 41302Sbill #include "sh.dir.h" 51302Sbill #include "sh.proc.h" 61302Sbill #include <wait.h> 71302Sbill #include <sys/ioctl.h> 81302Sbill 91302Sbill /* 101302Sbill * C Shell - functions that manage processes, handling hanging, termination 111302Sbill */ 121302Sbill 131302Sbill #define BIGINDEX 9 /* largest desirable job index */ 141302Sbill 151302Sbill /* 161302Sbill * pchild - called at interrupt level by the SIGCHLD signal 171302Sbill * indicating that at least one child has terminated or stopped 181302Sbill * thus at least one wait system call will definitely return a 191302Sbill * childs status. Top level routines (like pwait) must be sure 201302Sbill * to mask interrupts when playing with the proclist data structures! 211302Sbill */ 221302Sbill pchild() 231302Sbill { 241302Sbill register struct process *pp; 251302Sbill register struct process *fp; 261302Sbill register int pid; 271302Sbill union wait w; 281302Sbill int jobflags; 291302Sbill #ifdef VMUNIX 301302Sbill struct vtimes vt; 311302Sbill #endif 321302Sbill 331302Sbill if (!timesdone) 341302Sbill timesdone++, times(&shtimes); 351302Sbill loop: 361302Sbill pid = wait3(&w.w_status, (setintr ? WNOHANG|WUNTRACED:WNOHANG), 371302Sbill #ifndef VMUNIX 381302Sbill 0); 391302Sbill #else 401302Sbill &vt); 411302Sbill #endif 421302Sbill if (pid <= 0) { 431302Sbill if (errno == EINTR) { 441302Sbill errno = 0; 451302Sbill goto loop; 461302Sbill } 471302Sbill pnoprocesses = pid == -1; 481302Sbill return; 491302Sbill } 501302Sbill for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) 511302Sbill if (pid == pp->p_pid) 521302Sbill goto found; 531302Sbill goto loop; 541302Sbill found: 551302Sbill if (pid == atoi(value("child"))) 561302Sbill unsetv("child"); 571302Sbill pp->p_flags &= ~(PRUNNING|PSTOPPED|PREPORTED); 581302Sbill if (WIFSTOPPED(w)) { 591302Sbill pp->p_flags |= PSTOPPED; 601302Sbill pp->p_reason = w.w_stopsig; 611302Sbill } else { 621302Sbill if (pp->p_flags & (PTIME|PPTIME) || adrof("time")) { 631302Sbill time_t oldcutimes, oldcstimes; 641302Sbill oldcutimes = shtimes.tms_cutime; 651302Sbill oldcstimes = shtimes.tms_cstime; 661302Sbill time(&pp->p_etime); 671302Sbill times(&shtimes); 681302Sbill pp->p_utime = shtimes.tms_cutime - oldcutimes; 691302Sbill pp->p_stime = shtimes.tms_cstime - oldcstimes; 701302Sbill } else 711302Sbill times(&shtimes); 721302Sbill #ifdef VMUNIX 731302Sbill pp->p_vtimes = vt; 741302Sbill #endif 751302Sbill if (WIFSIGNALED(w)) { 761302Sbill if (w.w_termsig == SIGINT) 771302Sbill pp->p_flags |= PINTERRUPTED; 781302Sbill else 791302Sbill pp->p_flags |= PSIGNALED; 801302Sbill if (w.w_coredump) 811302Sbill pp->p_flags |= PDUMPED; 821302Sbill pp->p_reason = w.w_termsig; 831302Sbill } else { 841302Sbill pp->p_reason = w.w_retcode; 851302Sbill #ifdef IIASA 861302Sbill if (pp->p_reason >= 3) 871302Sbill #else 881302Sbill if (pp->p_reason != 0) 891302Sbill #endif 901302Sbill pp->p_flags |= PAEXITED; 911302Sbill else 921302Sbill pp->p_flags |= PNEXITED; 931302Sbill } 941302Sbill } 951302Sbill jobflags = 0; 961302Sbill fp = pp; 971302Sbill do { 981302Sbill if ((fp->p_flags & (PPTIME|PRUNNING|PSTOPPED)) == 0 && 991302Sbill !child && adrof("time") && 1001302Sbill (fp->p_utime + fp->p_stime) / HZ >= 1011302Sbill atoi(value("time"))) 1021302Sbill fp->p_flags |= PTIME; 1031302Sbill jobflags |= fp->p_flags; 1041302Sbill } while ((fp = fp->p_friends) != pp); 1051302Sbill pp->p_flags &= ~PFOREGND; 1061302Sbill if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { 1071302Sbill pp->p_flags &= ~PPTIME; 1081302Sbill pp->p_flags |= PTIME; 1091302Sbill } 1101302Sbill if ((jobflags & (PRUNNING|PREPORTED)) == 0) { 1111302Sbill fp = pp; 1121302Sbill do { 1131302Sbill if (fp->p_flags&PSTOPPED) 1141302Sbill fp->p_flags |= PREPORTED; 1151302Sbill } while((fp = fp->p_friends) != pp); 1161302Sbill while(fp->p_pid != fp->p_jobid) 1171302Sbill fp = fp->p_friends; 1182357Swnj if (jobflags&PSTOPPED) { 1192357Swnj if (pcurrent && pcurrent != fp) 1202357Swnj pprevious = pcurrent; 1212357Swnj pcurrent = fp; 1222357Swnj } else 1232357Swnj pclrcurr(fp); 1241302Sbill if (jobflags&PFOREGND) { 1251302Sbill if (jobflags & (PSIGNALED|PSTOPPED|PPTIME) || 1261302Sbill #ifdef IIASA 1271302Sbill jobflags & PAEXITED || 1281302Sbill #endif 1291302Sbill !eq(dcwd->di_name, fp->p_cwd->di_name)) { 130*3213Swnj ; /* print in pjwait */ 1311302Sbill } else if ((jobflags & (PTIME|PSTOPPED)) == PTIME) 1321302Sbill ptprint(fp); 1331302Sbill } else { 1341302Sbill if (jobflags&PNOTIFY || adrof("notify")) { 1351302Sbill printf("\215\n"); 1361302Sbill pprint(pp, NUMBER|NAME|REASON); 1371302Sbill if ((jobflags&PSTOPPED) == 0) 1381302Sbill pflush(pp); 1391302Sbill } else { 1401302Sbill fp->p_flags |= PNEEDNOTE; 1411302Sbill neednote++; 1421302Sbill } 1431302Sbill } 1441302Sbill } 1451302Sbill goto loop; 1461302Sbill } 1471302Sbill 1481302Sbill pnote() 1491302Sbill { 1501302Sbill register struct process *pp; 1511302Sbill int flags; 1521302Sbill 1531302Sbill neednote = 0; 1541302Sbill for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) { 1551302Sbill if (pp->p_flags & PNEEDNOTE) { 1561302Sbill sighold(SIGCHLD); 1571302Sbill pp->p_flags &= ~PNEEDNOTE; 1581302Sbill flags = pprint(pp, NUMBER|NAME|REASON); 1591302Sbill if ((flags&(PRUNNING|PSTOPPED)) == 0) 1601302Sbill pflush(pp); 1611302Sbill sigrelse(SIGCHLD); 1621302Sbill } 1631302Sbill } 1641302Sbill } 1651302Sbill 1661302Sbill /* 1671302Sbill * pwait - wait for current job to terminate, maintaining integrity 1681302Sbill * of current and previous job indicators. 1691302Sbill */ 1701302Sbill pwait() 1711302Sbill { 1721302Sbill register struct process *fp, *pp; 1731302Sbill 1741302Sbill /* 1751302Sbill * Here's where dead procs get flushed. 1761302Sbill */ 1771302Sbill sighold(SIGCHLD); 1781302Sbill for (pp = (fp = &proclist)->p_next; pp != PNULL; pp = (fp = pp)->p_next) 1791302Sbill if (pp->p_pid == 0) { 1801302Sbill fp->p_next = pp->p_next; 1811302Sbill xfree(pp->p_command); 1821302Sbill if (pp->p_cwd && --pp->p_cwd->di_count == 0) 1831302Sbill if (pp->p_cwd->di_next == 0) 1841302Sbill dfree(pp->p_cwd); 1851302Sbill xfree((char *)pp); 1861302Sbill pp = fp; 1871302Sbill } 1881302Sbill sigrelse(SIGCHLD); 1891302Sbill if (setintr) 1901302Sbill sigignore(SIGINT); 1911302Sbill pjwait(pcurrjob); 1921302Sbill } 1931302Sbill 1941302Sbill /* 1951302Sbill * pjwait - wait for a job to finish or become stopped 1961302Sbill * It is assumed to be in the foreground state (PFOREGND) 1971302Sbill */ 1981302Sbill pjwait(pp) 1991302Sbill register struct process *pp; 2001302Sbill { 2011302Sbill register struct process *fp; 2021302Sbill int jobflags, reason; 2031302Sbill 2041302Sbill fp = pp; 2051302Sbill do { 2061302Sbill if ((fp->p_flags&(PFOREGND|PRUNNING)) == PRUNNING) 2071302Sbill printf("BUG: waiting for background job!\n"); 2081302Sbill } while ((fp = fp->p_friends) != pp); 2091302Sbill /* 2101302Sbill * Now keep pausing as long as we are not interrupted (SIGINT), 2111302Sbill * and the target process, or any of its friends, are running 2121302Sbill */ 2131302Sbill fp = pp; 2141302Sbill for (;;) { 2151302Sbill sighold(SIGCHLD); 2161302Sbill jobflags = 0; 2171302Sbill do 2181302Sbill jobflags |= fp->p_flags; 2191302Sbill while((fp = (fp->p_friends)) != pp); 2201302Sbill if ((jobflags & PRUNNING) == 0) 2211302Sbill break; 2221302Sbill sigpause(SIGCHLD); 2231302Sbill } 2241302Sbill sigrelse(SIGCHLD); 2251302Sbill if (tpgrp > 0) 2261302Sbill ioctl(FSHTTY, TIOCSPGRP, &tpgrp); /* get tty back */ 227*3213Swnj if ((jobflags&(PINTERRUPTED|PSTOPPED)) && setintr && 2281302Sbill (!gointr || !eq(gointr, "-"))) { 229*3213Swnj if ((jobflags & PSTOPPED) == 0) 230*3213Swnj pflush(pp); 231*3213Swnj else { 232*3213Swnj printf("\n"); 233*3213Swnj pprint(pp, AREASON|SHELLDIR); 234*3213Swnj } 235*3213Swnj pintr1(0); 2361302Sbill /*NOTREACHED*/ 2371302Sbill } 2381302Sbill reason = 0; 2391302Sbill fp = pp; 2401302Sbill do { 2411302Sbill if (fp->p_reason) 2421302Sbill reason = fp->p_flags & (PSIGNALED|PINTERRUPTED) ? 2431302Sbill fp->p_reason | QUOTE : fp->p_reason; 2441302Sbill } while ((fp = fp->p_friends) != pp); 2451302Sbill set("status", putn(reason)); 2461302Sbill if (reason && exiterr) 2471302Sbill exitstat(); 2481302Sbill pflush(pp); 2491302Sbill } 2501302Sbill 2511302Sbill /* 2521302Sbill * dowait - wait for all processes to finish 2531302Sbill */ 2541302Sbill dowait() 2551302Sbill { 2561302Sbill register struct process *pp; 2571302Sbill 2581302Sbill pjobs++; 2591302Sbill if (setintr) 2601302Sbill sigrelse(SIGINT); 2611302Sbill loop: 2621302Sbill sighold(SIGCHLD); 2631302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 2641302Sbill if (pp->p_pid && pp->p_pid == pp->p_jobid && 2651302Sbill pp->p_flags&PRUNNING) { 2661302Sbill sigpause(SIGCHLD); 2671302Sbill goto loop; 2681302Sbill } 2691302Sbill sigrelse(SIGCHLD); 2701302Sbill pjobs = 0; 2711302Sbill } 2721302Sbill 2731302Sbill /* 2741302Sbill * pflushall - flush all jobs from list (e.g. at fork()) 2751302Sbill */ 2761302Sbill pflushall() 2771302Sbill { 2781302Sbill register struct process *pp; 2791302Sbill 2801302Sbill for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) 2811302Sbill if (pp->p_pid) 2821302Sbill pflush(pp); 2831302Sbill } 2841302Sbill 2851302Sbill /* 2861302Sbill * pflush - flag all process structures in the same job as the 2871302Sbill * the argument process for deletion. The actual free of the 2881302Sbill * space is not done here since pflush is called at interrupt level. 2891302Sbill */ 2901302Sbill pflush(pp) 2911302Sbill register struct process *pp; 2921302Sbill { 2931302Sbill register struct process *np; 2941302Sbill register int index; 2951302Sbill 2961302Sbill if (pp->p_pid == 0) { 2971302Sbill printf("BUG: process flushed twice"); 2981302Sbill return; 2991302Sbill } 3001302Sbill while (pp->p_pid != pp->p_jobid) 3011302Sbill pp = pp->p_friends; 3021302Sbill pclrcurr(pp); 3031302Sbill if (pp == pcurrjob) 3041302Sbill pcurrjob = 0; 3051302Sbill index = pp->p_index; 3061302Sbill np = pp; 3071302Sbill do { 3081302Sbill np->p_index = np->p_pid = 0; 3091302Sbill np->p_flags &= ~PNEEDNOTE; 3101302Sbill } while ((np = np->p_friends) != pp); 3111302Sbill if (index == pmaxindex) { 3121302Sbill for (np = proclist.p_next, index = 0; np; np = np->p_next) 3131302Sbill if (np->p_index > index) 3141302Sbill index = np->p_index; 3151302Sbill pmaxindex = index; 3161302Sbill } 3171302Sbill } 3181302Sbill 3191302Sbill /* 3201302Sbill * pclrcurr - make sure the given job is not the current or previous job; 3211302Sbill * pp MUST be the job leader 3221302Sbill */ 3231302Sbill pclrcurr(pp) 3241302Sbill register struct process *pp; 3251302Sbill { 3261302Sbill 3271302Sbill if (pp == pcurrent) 3281302Sbill if (pprevious != PNULL) { 3291302Sbill pcurrent = pprevious; 3301302Sbill pprevious = pgetcurr(pp); 3311302Sbill } else { 3321302Sbill pcurrent = pgetcurr(pp); 3331302Sbill pprevious = pgetcurr(pp); 3341302Sbill } 3351302Sbill else if (pp == pprevious) 3361302Sbill pprevious = pgetcurr(pp); 3371302Sbill } 3381302Sbill 3391302Sbill /* +4 here is 1 for '\0', 1 ea for << >& >> */ 3401302Sbill char command[PMAXLEN+4]; 3411302Sbill int cmdlen; 3421302Sbill char *cmdp; 3431302Sbill /* 3441302Sbill * palloc - allocate a process structure and fill it up. 3451302Sbill * an important assumption is made that the process is running. 3461302Sbill */ 3471302Sbill palloc(pid, t) 3481302Sbill int pid; 3491302Sbill register struct command *t; 3501302Sbill { 3511302Sbill register struct process *pp; 3521302Sbill int i; 3531302Sbill 3541302Sbill pp = (struct process *)calloc(1, sizeof(struct process)); 3551302Sbill pp->p_pid = pid; 3561302Sbill pp->p_flags = t->t_dflg & FAND ? PRUNNING : PRUNNING|PFOREGND; 3571302Sbill if (t->t_dflg & FTIME) 3581302Sbill pp->p_flags |= PPTIME; 3591302Sbill cmdp = command; 3601302Sbill cmdlen = 0; 3611302Sbill padd(t); 3621302Sbill *cmdp++ = 0; 3631302Sbill if (t->t_dflg & FPOU) { 3641302Sbill pp->p_flags |= PPOU; 3651302Sbill if (t->t_dflg & FDIAG) 3661302Sbill pp->p_flags |= PDIAG; 3671302Sbill } 3681302Sbill pp->p_command = savestr(command); 3691302Sbill if (pcurrjob) { 3701302Sbill struct process *fp; 3711302Sbill /* careful here with interrupt level */ 3721302Sbill pp->p_cwd = 0; 3731302Sbill pp->p_index = pcurrjob->p_index; 3741302Sbill pp->p_friends = pcurrjob; 3751302Sbill pp->p_jobid = pcurrjob->p_pid; 3761302Sbill for (fp = pcurrjob; fp->p_friends != pcurrjob; fp = fp->p_friends) 3771302Sbill ; 3781302Sbill fp->p_friends = pp; 3791302Sbill } else { 3801302Sbill pcurrjob = pp; 3811302Sbill pp->p_jobid = pid; 3821302Sbill pp->p_friends = pp; 3831302Sbill pp->p_cwd = dcwd; 3841302Sbill dcwd->di_count++; 3851302Sbill if (pmaxindex < BIGINDEX) 3861302Sbill pp->p_index = ++pmaxindex; 3871302Sbill else { 3881302Sbill struct process *np; 3891302Sbill 3901302Sbill for (i = 1; ; i++) { 3911302Sbill for (np = proclist.p_next; np; np = np->p_next) 3921302Sbill if (np->p_index == i) 3931302Sbill goto tryagain; 3942357Swnj pp->p_index = i; 3952357Swnj if (i > pmaxindex) 3962357Swnj pmaxindex = i; 3971302Sbill break; 3981302Sbill tryagain:; 3991302Sbill } 4001302Sbill } 4011302Sbill if (pcurrent == PNULL) 4021302Sbill pcurrent = pp; 4031302Sbill else if (pprevious == PNULL) 4041302Sbill pprevious = pp; 4051302Sbill } 4061302Sbill pp->p_next = proclist.p_next; 4071302Sbill proclist.p_next = pp; 4081302Sbill time(&pp->p_btime); 4091302Sbill } 4101302Sbill 4111302Sbill padd(t) 4121302Sbill register struct command *t; 4131302Sbill { 4141302Sbill char **argp; 4151302Sbill 4161302Sbill if (t == 0) 4171302Sbill return; 4181302Sbill switch (t->t_dtyp) { 4191302Sbill 4201302Sbill case TPAR: 4211302Sbill pads("( "); 4221302Sbill padd(t->t_dspr); 4231302Sbill pads(" )"); 4241302Sbill break; 4251302Sbill 4261302Sbill case TCOM: 4271302Sbill for (argp = t->t_dcom; *argp; argp++) { 4281302Sbill pads(*argp); 4291302Sbill if (argp[1]) 4301302Sbill pads(" "); 4311302Sbill } 4321302Sbill break; 4331302Sbill 4341302Sbill case TFIL: 4351302Sbill padd(t->t_dcar); 4361302Sbill pads(" | "); 4371302Sbill padd(t->t_dcdr); 4381302Sbill return; 4391302Sbill 4401302Sbill case TLST: 4411302Sbill padd(t->t_dcar); 4421302Sbill pads("; "); 4431302Sbill padd(t->t_dcdr); 4441302Sbill return; 4451302Sbill } 4461302Sbill if ((t->t_dflg & FPIN) == 0 && t->t_dlef) { 4471302Sbill pads((t->t_dflg & FHERE) ? " << " : " < "); 4481302Sbill pads(t->t_dlef); 4491302Sbill } 4501302Sbill if ((t->t_dflg & FPOU) == 0 && t->t_drit) { 4511302Sbill pads((t->t_dflg & FCAT) ? " >>" : " >"); 4521302Sbill if (t->t_dflg & FDIAG) 4531302Sbill pads("&"); 4541302Sbill pads(" "); 4551302Sbill pads(t->t_drit); 4561302Sbill } 4571302Sbill } 4581302Sbill 4591302Sbill pads(cp) 4601302Sbill char *cp; 4611302Sbill { 4621302Sbill register int i = strlen(cp); 4631302Sbill 4641302Sbill if (cmdlen >= PMAXLEN) 4651302Sbill return; 4661302Sbill if (cmdlen + i >= PMAXLEN) { 4671302Sbill strcpy(cmdp, " ..."); 4681302Sbill cmdlen = PMAXLEN; 4691302Sbill cmdp += 4; 4701302Sbill return; 4711302Sbill } 4721302Sbill strcpy(cmdp, cp); 4731302Sbill cmdp += i; 4741302Sbill cmdlen += i; 4751302Sbill } 4761302Sbill 4771302Sbill /* 4781302Sbill * psavejob - temporarily save the current job on a one level stack 4791302Sbill * so another job can be created. Used for { } in exp6 4801302Sbill * and `` in globbing. 4811302Sbill */ 4821302Sbill psavejob() 4831302Sbill { 4841302Sbill 4851302Sbill pholdjob = pcurrjob; 4861302Sbill pcurrjob = PNULL; 4871302Sbill } 4881302Sbill 4891302Sbill /* 4901302Sbill * prestjob - opposite of psavejob. This may be missed if we are interrupted 4911302Sbill * somewhere, but pendjob cleans up anyway. 4921302Sbill */ 4931302Sbill prestjob() 4941302Sbill { 4951302Sbill 4961302Sbill pcurrjob = pholdjob; 4971302Sbill pholdjob = PNULL; 4981302Sbill } 4991302Sbill 5001302Sbill /* 5011302Sbill * pendjob - indicate that a job (set of commands) has been completed 5021302Sbill * or is about to begin. 5031302Sbill */ 5041302Sbill pendjob() 5051302Sbill { 5061302Sbill register struct process *pp, *tp; 5071302Sbill 5081302Sbill if (pcurrjob && (pcurrjob->p_flags&(PFOREGND|PSTOPPED)) == 0) { 5091302Sbill pp = pcurrjob; 5101302Sbill while (pp->p_pid != pp->p_jobid) 5111302Sbill pp = pp->p_friends; 5121302Sbill printf("[%d]", pp->p_index); 5131302Sbill tp = pp; 5141302Sbill do { 5151302Sbill printf(" %d", pp->p_pid); 5161302Sbill pp = pp->p_friends; 5171302Sbill } while (pp != tp); 5181302Sbill printf("\n"); 5191302Sbill } 5201302Sbill pholdjob = pcurrjob = 0; 5211302Sbill } 5221302Sbill 5231302Sbill /* 5241302Sbill * pprint - print a job 5251302Sbill */ 5261302Sbill pprint(pp, flag) 5271302Sbill register struct process *pp; 5281302Sbill { 5291302Sbill register status, reason; 5301302Sbill struct process *tp; 5311302Sbill extern char *linp, linbuf[]; 5321302Sbill int jobflags, pstatus; 5331302Sbill char *format; 5341302Sbill 5351302Sbill while (pp->p_pid != pp->p_jobid) 5361302Sbill pp = pp->p_friends; 5371302Sbill if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { 5381302Sbill pp->p_flags &= ~PPTIME; 5391302Sbill pp->p_flags |= PTIME; 5401302Sbill } 5411302Sbill tp = pp; 5421302Sbill status = reason = -1; 5431302Sbill jobflags = 0; 5441302Sbill do { 5451302Sbill jobflags |= pp->p_flags; 5461302Sbill pstatus = pp->p_flags & PALLSTATES; 5471302Sbill if (tp != pp && linp != linbuf && !(flag&FANCY) && 5481302Sbill (pstatus == status && pp->p_reason == reason || 5491302Sbill !(flag&REASON))) 5501302Sbill printf(" "); 5511302Sbill else { 5521302Sbill if (tp != pp && linp != linbuf) 5531302Sbill printf("\n"); 5541302Sbill if(flag&NUMBER) 5551302Sbill if (pp == tp) 5561302Sbill printf("[%d]%s %c ", pp->p_index, 5571302Sbill pp->p_index < 10 ? " " : "", 5581302Sbill pp==pcurrent ? '+' : 5591302Sbill (pp == pprevious ? '-' : ' ')); 5601302Sbill else 5611302Sbill printf(" "); 5621302Sbill if (flag&FANCY) 5631302Sbill printf("%5d ", pp->p_pid); 5641302Sbill if (flag&(REASON|AREASON)) { 5651302Sbill if (flag&NAME) 5661302Sbill format = "%-21s"; 5671302Sbill else 5681302Sbill format = "%s"; 5691302Sbill if (pstatus == status) 5701302Sbill if (pp->p_reason == reason) { 5711302Sbill printf(format, ""); 5721302Sbill goto prcomd; 5731302Sbill } else 5741302Sbill reason = pp->p_reason; 5751302Sbill else { 5761302Sbill status = pstatus; 5771302Sbill reason = pp->p_reason; 5781302Sbill } 5791302Sbill switch (status) { 5801302Sbill 5811302Sbill case PRUNNING: 5821302Sbill printf(format, "Running "); 5831302Sbill break; 5841302Sbill 5851302Sbill case PINTERRUPTED: 5861302Sbill case PSTOPPED: 5871302Sbill case PSIGNALED: 5881302Sbill if (flag&REASON || reason != SIGINT || 5891302Sbill reason != SIGPIPE) 5901302Sbill printf(format, mesg[pp->p_reason].pname); 5911302Sbill break; 5921302Sbill 5931302Sbill case PNEXITED: 5941302Sbill case PAEXITED: 5951302Sbill if (flag & REASON) 5961302Sbill if (pp->p_reason) 5971302Sbill printf("Exit %-16d", pp->p_reason); 5981302Sbill else 5991302Sbill printf(format, "Done"); 6001302Sbill break; 6011302Sbill 6021302Sbill default: 6031302Sbill printf("BUG: status=%-9o", status); 6041302Sbill } 6051302Sbill } 6061302Sbill } 6071302Sbill prcomd: 6081302Sbill if (flag&NAME) { 6091302Sbill printf("%s", pp->p_command); 6101302Sbill if (pp->p_flags & PPOU) 6111302Sbill printf(" |"); 6121302Sbill if (pp->p_flags & PDIAG) 6131302Sbill printf("&"); 6141302Sbill } 6151302Sbill if (flag&(REASON|AREASON) && pp->p_flags&PDUMPED) 6161302Sbill printf(" (core dumped)"); 6171302Sbill if (tp == pp->p_friends) { 6181302Sbill if (flag&ERSAND) 6191302Sbill printf(" &"); 6201302Sbill if (flag&JOBDIR && 6211302Sbill !eq(tp->p_cwd->di_name, dcwd->di_name)) { 6221302Sbill printf(" (wd: "); 6231302Sbill dtildepr(value("home"), tp->p_cwd->di_name); 6241302Sbill printf(")"); 6251302Sbill } 6261302Sbill } 6271302Sbill if (pp->p_flags&PPTIME && !(status&(PSTOPPED|PRUNNING))) { 6281302Sbill if (linp != linbuf) 6291302Sbill printf("\n\t"); 6301302Sbill #ifndef VMUNIX 6311302Sbill ptimes(pp->p_utime, pp->p_stime, pp->p_etime-pp->p_btime); 6321302Sbill #else 6331302Sbill pvtimes(&zvms, &pp->p_vtimes, pp->p_etime - pp->p_btime); 6341302Sbill #endif 6351302Sbill } 6361302Sbill if (tp == pp->p_friends) { 6371302Sbill if (linp != linbuf) 6381302Sbill printf("\n"); 6391302Sbill if (flag&SHELLDIR && !eq(tp->p_cwd->di_name, dcwd->di_name)) { 6401302Sbill printf("(wd now: "); 6411302Sbill dtildepr(value("home"), dcwd->di_name); 6421302Sbill printf(")\n"); 6431302Sbill } 6441302Sbill } 6451302Sbill } while ((pp = pp->p_friends) != tp); 6461302Sbill if (jobflags&PTIME && (jobflags&(PSTOPPED|PRUNNING)) == 0) { 6471302Sbill if (jobflags & NUMBER) 6481302Sbill printf(" "); 6491302Sbill ptprint(tp); 6501302Sbill } 6511302Sbill return (jobflags); 6521302Sbill } 6531302Sbill 6541302Sbill ptprint(tp) 6551302Sbill register struct process *tp; 6561302Sbill { 6571302Sbill time_t tetime = 0; 6581302Sbill #ifdef VMUNIX 6591302Sbill struct vtimes vmt; 6601302Sbill #else 6611302Sbill time_t tutime = 0, tstime = 0; 6621302Sbill #endif 6631302Sbill register struct process *pp = tp; 6641302Sbill 6651302Sbill vmt = zvms; 6661302Sbill do { 6671302Sbill #ifdef VMUNIX 6681302Sbill vmsadd(&vmt, &pp->p_vtimes); 6691302Sbill #else 6701302Sbill tutime += pp->p_utime; 6711302Sbill tstime += pp->p_stime; 6721302Sbill #endif 6731302Sbill if (pp->p_etime - pp->p_btime > tetime) 6741302Sbill tetime = pp->p_etime - pp->p_btime; 6751302Sbill } while ((pp = pp->p_friends) != tp); 6761302Sbill #ifdef VMUNIX 6771302Sbill pvtimes(&zvms, &vmt, tetime); 6781302Sbill #else 6791302Sbill ptimes(tutime, tstime, tetime); 6801302Sbill #endif 6811302Sbill } 6821302Sbill 6831302Sbill /* 6841302Sbill * dojobs - print all jobs 6851302Sbill */ 6861302Sbill dojobs(v) 6871302Sbill char **v; 6881302Sbill { 6891302Sbill register struct process *pp; 6901302Sbill register int flag = NUMBER|NAME|REASON; 6911302Sbill int i; 6921302Sbill 6931302Sbill if (chkstop) 6941302Sbill chkstop = 2; 6951302Sbill if (*++v) { 6961302Sbill if (v[1] || !eq(*v, "-l")) 6971302Sbill error("Usage: jobs [ -l ]"); 6981302Sbill flag |= FANCY|JOBDIR; 6991302Sbill } 7001302Sbill for (i = 1; i <= pmaxindex; i++) 7011302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 7021302Sbill if (pp->p_index == i && pp->p_pid == pp->p_jobid) { 7031302Sbill pp->p_flags &= ~PNEEDNOTE; 7041302Sbill if (!(pprint(pp, flag) & (PRUNNING|PSTOPPED))) 7051302Sbill pflush(pp); 7061302Sbill break; 7071302Sbill } 7081302Sbill } 7091302Sbill 7101302Sbill /* 7111302Sbill * dofg - builtin - put the job into the foreground 7121302Sbill */ 7131302Sbill dofg(v) 7141302Sbill char **v; 7151302Sbill { 7161302Sbill register struct process *pp; 7171302Sbill 7181302Sbill okpcntl(); 7191302Sbill ++v; 7201302Sbill do { 7211302Sbill pp = pfind(*v); 7221302Sbill pstart(pp, 1); 7231302Sbill if (setintr) 7241302Sbill sigignore(SIGINT); 7251302Sbill pjwait(pp); 7261302Sbill } while (*v && *++v); 7271302Sbill } 7281302Sbill 7291302Sbill /* 7301302Sbill * %... - builtin - put the job into the foreground 7311302Sbill */ 7321302Sbill dofg1(v) 7331302Sbill char **v; 7341302Sbill { 7351302Sbill register struct process *pp; 7361302Sbill 7371302Sbill okpcntl(); 7381302Sbill pp = pfind(v[0]); 7391302Sbill pstart(pp, 1); 7401302Sbill if (setintr) 7411302Sbill sigignore(SIGINT); 7421302Sbill pjwait(pp); 7431302Sbill } 7441302Sbill 7451302Sbill /* 7461302Sbill * dobg - builtin - put the job into the background 7471302Sbill */ 7481302Sbill dobg(v) 7491302Sbill char **v; 7501302Sbill { 7511302Sbill register struct process *pp; 7521302Sbill 7531302Sbill okpcntl(); 7541302Sbill ++v; 7551302Sbill do { 7561302Sbill pp = pfind(*v); 7571302Sbill pstart(pp, 0); 7581302Sbill } while (*v && *++v); 7591302Sbill } 7601302Sbill 7611302Sbill /* 7621302Sbill * %... & - builtin - put the job into the background 7631302Sbill */ 7641302Sbill dobg1(v) 7651302Sbill char **v; 7661302Sbill { 7671302Sbill register struct process *pp; 7681302Sbill 7691302Sbill pp = pfind(v[0]); 7701302Sbill pstart(pp, 0); 7711302Sbill } 7721302Sbill 7731302Sbill /* 7741302Sbill * dostop - builtin - stop the job 7751302Sbill */ 7761302Sbill dostop(v) 7771302Sbill char **v; 7781302Sbill { 7791302Sbill 7801302Sbill pkill(++v, SIGSTOP); 7811302Sbill } 7821302Sbill 7831302Sbill /* 7841302Sbill * dokill - builtin - superset of kill (1) 7851302Sbill */ 7861302Sbill dokill(v) 7871302Sbill char **v; 7881302Sbill { 7891302Sbill register int signum; 7901302Sbill register char *name; 7911302Sbill 7921302Sbill v++; 7931302Sbill if (v[0] && v[0][0] == '-') { 7941302Sbill if (v[0][1] == 'l') { 7951302Sbill for (signum = 1; signum <= NSIG; signum++) { 7961302Sbill if (name = mesg[signum].iname) 7971302Sbill printf("%s ", name); 7981302Sbill if (signum == 16) 7991302Sbill printf("\n"); 8001302Sbill } 8011302Sbill printf("\n"); 8021302Sbill return; 8031302Sbill } 8041302Sbill if (digit(v[0][1])) { 8051302Sbill signum = atoi(v[0]+1); 8061302Sbill if (signum < 1 || signum > NSIG) 8071302Sbill bferr("Bad signal number"); 8081302Sbill } else { 8091302Sbill name = &v[0][1]; 8101302Sbill for (signum = 1; signum <= NSIG; signum++) 8111302Sbill if (mesg[signum].iname && 8121302Sbill eq(name, mesg[signum].iname)) 8131302Sbill goto gotsig; 8141302Sbill setname(name); 8151302Sbill bferr("Unknown signal; kill -l lists signals"); 8161302Sbill } 8171302Sbill gotsig: 8181302Sbill v++; 8191302Sbill } else 8201302Sbill signum = SIGTERM; 8211302Sbill pkill(v, signum); 8221302Sbill } 8231302Sbill 8241302Sbill pkill(v, signum) 8251302Sbill char **v; 8261302Sbill int signum; 8271302Sbill { 8281302Sbill register struct process *pp, *np; 8291302Sbill register int jobflags = 0; 8301302Sbill int pid; 8311302Sbill extern char *sys_errlist[]; 8321302Sbill int err = 0; 8331302Sbill 8341302Sbill if (setintr) 8351302Sbill sighold(SIGINT); 8361302Sbill sighold(SIGCHLD); 8371302Sbill while (*v) { 8381302Sbill if (**v == '%') { 8391302Sbill np = pp = pfind(*v); 8401302Sbill do 8411302Sbill jobflags |= np->p_flags; 8421302Sbill while ((np = np->p_friends) != pp); 8431302Sbill switch (signum) { 8441302Sbill 8451302Sbill case SIGSTOP: 8461302Sbill case SIGTSTP: 8471302Sbill case SIGTTIN: 8481302Sbill case SIGTTOU: 8491302Sbill if ((jobflags & PRUNNING) == 0) { 8501302Sbill printf("%s: Already stopped\n", *v); 8511302Sbill err++; 8521302Sbill goto cont; 8531302Sbill } 8541302Sbill } 8551302Sbill killpg(pp->p_jobid, signum); 8561302Sbill if (signum == SIGTERM || signum == SIGHUP) 8571302Sbill killpg(pp->p_jobid, SIGCONT); 8581302Sbill } else if (!digit(**v)) 8591302Sbill bferr("Arguments should be jobs or process id's"); 8601302Sbill else { 8611302Sbill pid = atoi(*v); 8621302Sbill if (kill(pid, signum) < 0) { 8631302Sbill printf("%d: ", pid); 8641302Sbill printf("%s\n", sys_errlist[errno]); 8651302Sbill err++; 8661302Sbill goto cont; 8671302Sbill } 8681302Sbill if (signum == SIGTERM || signum == SIGHUP) 8691302Sbill kill(pid, SIGCONT); 8701302Sbill } 8711302Sbill cont: 8721302Sbill v++; 8731302Sbill } 8741302Sbill sigrelse(SIGCHLD); 8751302Sbill if (setintr) 8761302Sbill sigrelse(SIGINT); 8771302Sbill if (err) 8781302Sbill error(NOSTR); 8791302Sbill } 8801302Sbill 8811302Sbill /* 8821302Sbill * pstart - start the job in foreground/background 8831302Sbill */ 8841302Sbill pstart(pp, foregnd) 8851302Sbill register struct process *pp; 8861302Sbill int foregnd; 8871302Sbill { 8881302Sbill register struct process *np; 8891302Sbill int jobflags = 0; 8901302Sbill 8911302Sbill sighold(SIGCHLD); 8921302Sbill np = pp; 8931302Sbill do { 8941302Sbill jobflags |= np->p_flags; 8951302Sbill if (np->p_flags&(PRUNNING|PSTOPPED)) { 8961302Sbill np->p_flags |= PRUNNING; 8971302Sbill np->p_flags &= ~PSTOPPED; 8981302Sbill if (foregnd) 8991302Sbill np->p_flags |= PFOREGND; 9001302Sbill else 9011302Sbill np->p_flags &= ~PFOREGND; 9021302Sbill } 9031302Sbill } while((np = np->p_friends) != pp); 9042357Swnj if (!foregnd) 9052357Swnj pclrcurr(pp); 9061302Sbill pprint(pp, foregnd ? NAME|JOBDIR : NUMBER|NAME|AMPERSAND); 9071302Sbill if (foregnd) 9081302Sbill ioctl(FSHTTY, TIOCSPGRP, &pp->p_jobid); 9091302Sbill if (jobflags&PSTOPPED) 9101302Sbill killpg(pp->p_jobid, SIGCONT); 9111302Sbill sigrelse(SIGCHLD); 9121302Sbill } 9131302Sbill 9141302Sbill panystop(neednl) 9151302Sbill { 9161302Sbill register struct process *pp; 9171302Sbill 9181302Sbill chkstop = 2; 9191302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 9201302Sbill if (pp->p_flags & PSTOPPED) 9211302Sbill error("\nThere are stopped jobs" + 1 - neednl); 9221302Sbill } 9231302Sbill 9241302Sbill struct process * 9251302Sbill pfind(cp) 9261302Sbill char *cp; 9271302Sbill { 9281302Sbill register struct process *pp, *np; 9291302Sbill 9301302Sbill if (cp == 0 || cp[1] == 0 || eq(cp, "%%") || eq(cp, "%+")) { 9311302Sbill if (pcurrent == PNULL) 9321302Sbill bferr("No current job"); 9331302Sbill return (pcurrent); 9341302Sbill } 9351302Sbill if (eq(cp, "%-") || eq(cp, "%#")) { 9361302Sbill if (pprevious == PNULL) 9371302Sbill bferr("No previous job"); 9381302Sbill return (pprevious); 9391302Sbill } 9401302Sbill if (digit(cp[1])) { 9411302Sbill int index = atoi(cp+1); 9421302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 9431302Sbill if (pp->p_index == index && pp->p_pid == pp->p_jobid) 9441302Sbill return (pp); 9451302Sbill bferr("No such job"); 9461302Sbill } 9471302Sbill np = PNULL; 9481302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 9491302Sbill if (pp->p_pid == pp->p_jobid) { 9501302Sbill if (cp[1] == '?') { 9511302Sbill register char *dp; 9521302Sbill for (dp = pp->p_command; *dp; dp++) { 9531302Sbill if (*dp != cp[2]) 9541302Sbill continue; 9551302Sbill if (prefix(cp+2, dp)) 9561302Sbill goto match; 9571302Sbill } 9581302Sbill } else if (prefix(cp+1, pp->p_command)) { 9591302Sbill match: 9601302Sbill if (np) 9611302Sbill bferr("Ambiguous"); 9621302Sbill np = pp; 9631302Sbill } 9641302Sbill } 9651302Sbill if (np) 9661302Sbill return (np); 9671302Sbill if (cp[1] == '?') 9681302Sbill bferr("No job matches pattern"); 9691302Sbill else 9701302Sbill bferr("No such job"); 9711302Sbill } 9721302Sbill 9731302Sbill /* 9742357Swnj * pgetcurr - find most recent job that is not pp, preferably stopped 9751302Sbill */ 9761302Sbill struct process * 9771302Sbill pgetcurr(pp) 9781302Sbill register struct process *pp; 9791302Sbill { 9801302Sbill register struct process *np; 9812357Swnj register struct process *xp = PNULL; 9821302Sbill 9831302Sbill for (np = proclist.p_next; np; np = np->p_next) 9841302Sbill if (np != pcurrent && np != pp && np->p_pid && 9851302Sbill np->p_pid == np->p_jobid) { 9862357Swnj if (np->p_flags & PSTOPPED) 9872357Swnj return (np); 9882357Swnj if (xp == PNULL) 9892357Swnj xp = np; 9901302Sbill } 9912357Swnj return (xp); 9921302Sbill } 9931302Sbill 9941302Sbill /* 9951302Sbill * donotify - flag the job so as to report termination asynchronously 9961302Sbill */ 9971302Sbill donotify(v) 9981302Sbill char **v; 9991302Sbill { 10001302Sbill register struct process *pp; 10011302Sbill 10021302Sbill pp = pfind(*++v); 10031302Sbill pp->p_flags |= PNOTIFY; 10041302Sbill } 10051302Sbill 10061302Sbill /* 10071302Sbill * Do the fork and whatever should be done in the child side that 10081302Sbill * should not be done if we are not forking at all (like for simple builtin's) 10091302Sbill * Also do everything that needs any signals fiddled with in the parent side 10101302Sbill * 10111302Sbill * Wanttty tells whether process and/or tty pgrps are to be manipulated: 10121302Sbill * -1: leave tty alone; inherit pgrp from parent 10131302Sbill * 0: already have tty; manipulate process pgrps only 10141302Sbill * 1: want to claim tty; manipulate process and tty pgrps 10151302Sbill * It is usually just the value of tpgrp. 10161302Sbill */ 10171302Sbill pfork(t, wanttty) 10181302Sbill struct command *t; /* command we are forking for */ 10191302Sbill int wanttty; 10201302Sbill { 10211302Sbill register int pid; 10221302Sbill bool ignint = 0; 10231302Sbill int pgrp; 10241302Sbill 10251302Sbill /* 10261302Sbill * A child will be uninterruptible only under very special 10271302Sbill * conditions. Remember that the semantics of '&' is 10281302Sbill * implemented by disconnecting the process from the tty so 10291302Sbill * signals do not need to ignored just for '&'. 10301302Sbill * Thus signals are set to default action for children unless: 10311302Sbill * we have had an "onintr -" (then specifically ignored) 10321302Sbill * we are not playing with signals (inherit action) 10331302Sbill */ 10341302Sbill if (setintr) 10351302Sbill ignint = (tpgrp == -1 && (t->t_dflg&FINT)) 10361302Sbill || (gointr && eq(gointr, "-")); 10371302Sbill /* 10381302Sbill * Hold SIGCHLD until we have the process installed in our table. 10391302Sbill */ 10401302Sbill sighold(SIGCHLD); 10411302Sbill while ((pid = fork()) < 0) 10421302Sbill if (setintr == 0) 10431302Sbill sleep(FORKSLEEP); 10441302Sbill else { 10451302Sbill sigrelse(SIGINT); 10461302Sbill sigrelse(SIGCHLD); 10471302Sbill error("No more processes"); 10481302Sbill } 10491302Sbill if (pid == 0) { 10501302Sbill settimes(); 10511302Sbill pgrp = pcurrjob ? pcurrjob->p_jobid : getpid(); 10521302Sbill pflushall(); 10531302Sbill pcurrjob = PNULL; 10541302Sbill timesdone = 0; 10551302Sbill child++; 10561302Sbill if (setintr) { 10571302Sbill setintr = 0; /* until I think otherwise */ 10581302Sbill sigrelse(SIGCHLD); 10591302Sbill /* 10601302Sbill * Children just get blown away on SIGINT, SIGQUIT 10611302Sbill * unless "onintr -" seen. 10621302Sbill */ 10631302Sbill signal(SIGINT, ignint ? SIG_IGN : SIG_DFL); 10641302Sbill signal(SIGQUIT, ignint ? SIG_IGN : SIG_DFL); 10651302Sbill if (wanttty >= 0) { 10661302Sbill /* make stoppable */ 10671302Sbill signal(SIGTSTP, SIG_DFL); 10681302Sbill signal(SIGTTIN, SIG_DFL); 10691302Sbill signal(SIGTTOU, SIG_DFL); 10701302Sbill } 10711302Sbill signal(SIGTERM, parterm); 10721302Sbill } else if (tpgrp == -1 && (t->t_dflg&FINT)) { 10731302Sbill signal(SIGINT, SIG_IGN); 10741302Sbill signal(SIGQUIT, SIG_IGN); 10751302Sbill } 10761302Sbill if (wanttty > 0) 10771302Sbill ioctl(FSHTTY, TIOCSPGRP, &pgrp); 10781302Sbill if (wanttty >= 0 && tpgrp >= 0) 10791302Sbill setpgrp(0, pgrp); 10801302Sbill if (tpgrp > 0) 10811302Sbill tpgrp = 0; /* gave tty away */ 10821302Sbill /* 10831302Sbill * Nohup and nice apply only to TCOM's but it would be 10841302Sbill * nice (?!?) if you could say "nohup (foo;bar)" 10851302Sbill * Then the parser would have to know about nice/nohup/time 10861302Sbill */ 10871302Sbill if (t->t_dflg & FNOHUP) 10881302Sbill signal(SIGHUP, SIG_IGN); 10891302Sbill if (t->t_dflg & FNICE) { 10901302Sbill /* sigh... 10911302Sbill nice(20); 10921302Sbill nice(-10); 10931302Sbill */ 10941302Sbill nice(t->t_nice); 10951302Sbill } 10961302Sbill 10971302Sbill } else { 10981302Sbill palloc(pid, t); 10991302Sbill sigrelse(SIGCHLD); 11001302Sbill } 11011302Sbill 11021302Sbill return (pid); 11031302Sbill } 11041302Sbill 11051302Sbill okpcntl() 11061302Sbill { 11071302Sbill 11081302Sbill if (tpgrp == -1) 11091302Sbill error("No job control in this shell"); 11101302Sbill if (tpgrp == 0) 11111302Sbill error("No job control in subshells"); 11121302Sbill } 1113