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