1*21940Sdist /* 2*21940Sdist * Copyright (c) 1980 Regents of the University of California. 3*21940Sdist * All rights reserved. The Berkeley software License Agreement 4*21940Sdist * specifies the terms and conditions for redistribution. 5*21940Sdist */ 6*21940Sdist 717509Sedward #ifndef lint 8*21940Sdist static char sccsid[] = "@(#)proc.c 5.1 (Berkeley) 06/04/85"; 9*21940Sdist #endif not lint 101302Sbill 111302Sbill #include "sh.h" 121302Sbill #include "sh.dir.h" 131302Sbill #include "sh.proc.h" 1413590Swnj #include <sys/wait.h> 151302Sbill #include <sys/ioctl.h> 161302Sbill 171302Sbill /* 181302Sbill * C Shell - functions that manage processes, handling hanging, termination 191302Sbill */ 201302Sbill 211302Sbill #define BIGINDEX 9 /* largest desirable job index */ 221302Sbill 231302Sbill /* 241302Sbill * pchild - called at interrupt level by the SIGCHLD signal 251302Sbill * indicating that at least one child has terminated or stopped 261302Sbill * thus at least one wait system call will definitely return a 271302Sbill * childs status. Top level routines (like pwait) must be sure 281302Sbill * to mask interrupts when playing with the proclist data structures! 291302Sbill */ 301302Sbill pchild() 311302Sbill { 321302Sbill register struct process *pp; 331302Sbill register struct process *fp; 341302Sbill register int pid; 351302Sbill union wait w; 361302Sbill int jobflags; 3710035Ssam struct rusage ru; 381302Sbill 391302Sbill loop: 4017509Sedward pid = wait3(&w, (setintr ? WNOHANG|WUNTRACED:WNOHANG), &ru); 411302Sbill if (pid <= 0) { 421302Sbill if (errno == EINTR) { 431302Sbill errno = 0; 441302Sbill goto loop; 451302Sbill } 461302Sbill pnoprocesses = pid == -1; 471302Sbill return; 481302Sbill } 491302Sbill for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) 501302Sbill if (pid == pp->p_pid) 511302Sbill goto found; 521302Sbill goto loop; 531302Sbill found: 541302Sbill if (pid == atoi(value("child"))) 551302Sbill unsetv("child"); 561302Sbill pp->p_flags &= ~(PRUNNING|PSTOPPED|PREPORTED); 571302Sbill if (WIFSTOPPED(w)) { 581302Sbill pp->p_flags |= PSTOPPED; 591302Sbill pp->p_reason = w.w_stopsig; 601302Sbill } else { 6117509Sedward if (pp->p_flags & (PTIME|PPTIME) || adrof("time")) 6217509Sedward (void) gettimeofday(&pp->p_etime, (struct timezone *)0); 6310035Ssam pp->p_rusage = ru; 641302Sbill if (WIFSIGNALED(w)) { 651302Sbill if (w.w_termsig == SIGINT) 661302Sbill pp->p_flags |= PINTERRUPTED; 671302Sbill else 681302Sbill pp->p_flags |= PSIGNALED; 691302Sbill if (w.w_coredump) 701302Sbill pp->p_flags |= PDUMPED; 711302Sbill pp->p_reason = w.w_termsig; 721302Sbill } else { 731302Sbill pp->p_reason = w.w_retcode; 741302Sbill if (pp->p_reason != 0) 751302Sbill pp->p_flags |= PAEXITED; 761302Sbill else 771302Sbill pp->p_flags |= PNEXITED; 781302Sbill } 791302Sbill } 801302Sbill jobflags = 0; 811302Sbill fp = pp; 821302Sbill do { 831302Sbill if ((fp->p_flags & (PPTIME|PRUNNING|PSTOPPED)) == 0 && 841302Sbill !child && adrof("time") && 8517509Sedward fp->p_rusage.ru_utime.tv_sec+fp->p_rusage.ru_stime.tv_sec >= 861302Sbill atoi(value("time"))) 871302Sbill fp->p_flags |= PTIME; 881302Sbill jobflags |= fp->p_flags; 891302Sbill } while ((fp = fp->p_friends) != pp); 901302Sbill pp->p_flags &= ~PFOREGND; 911302Sbill if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { 921302Sbill pp->p_flags &= ~PPTIME; 931302Sbill pp->p_flags |= PTIME; 941302Sbill } 951302Sbill if ((jobflags & (PRUNNING|PREPORTED)) == 0) { 961302Sbill fp = pp; 971302Sbill do { 981302Sbill if (fp->p_flags&PSTOPPED) 991302Sbill fp->p_flags |= PREPORTED; 1001302Sbill } while((fp = fp->p_friends) != pp); 1011302Sbill while(fp->p_pid != fp->p_jobid) 1021302Sbill fp = fp->p_friends; 1032357Swnj if (jobflags&PSTOPPED) { 1042357Swnj if (pcurrent && pcurrent != fp) 1052357Swnj pprevious = pcurrent; 1062357Swnj pcurrent = fp; 1072357Swnj } else 1082357Swnj pclrcurr(fp); 1091302Sbill if (jobflags&PFOREGND) { 1101302Sbill if (jobflags & (PSIGNALED|PSTOPPED|PPTIME) || 1111302Sbill #ifdef IIASA 1121302Sbill jobflags & PAEXITED || 1131302Sbill #endif 1141302Sbill !eq(dcwd->di_name, fp->p_cwd->di_name)) { 1153213Swnj ; /* print in pjwait */ 1163646Sroot } 1173646Sroot /* 1183646Sroot else if ((jobflags & (PTIME|PSTOPPED)) == PTIME) 1191302Sbill ptprint(fp); 1203646Sroot */ 1211302Sbill } else { 1221302Sbill if (jobflags&PNOTIFY || adrof("notify")) { 1231302Sbill printf("\215\n"); 12417509Sedward (void) pprint(pp, NUMBER|NAME|REASON); 1251302Sbill if ((jobflags&PSTOPPED) == 0) 1261302Sbill pflush(pp); 1271302Sbill } else { 1281302Sbill fp->p_flags |= PNEEDNOTE; 1291302Sbill neednote++; 1301302Sbill } 1311302Sbill } 1321302Sbill } 1331302Sbill goto loop; 1341302Sbill } 1351302Sbill 1361302Sbill pnote() 1371302Sbill { 1381302Sbill register struct process *pp; 13917136Sralph int flags, omask; 1401302Sbill 1411302Sbill neednote = 0; 1421302Sbill for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) { 1431302Sbill if (pp->p_flags & PNEEDNOTE) { 14417136Sralph omask = sigblock(sigmask(SIGCHLD)); 1451302Sbill pp->p_flags &= ~PNEEDNOTE; 1461302Sbill flags = pprint(pp, NUMBER|NAME|REASON); 1471302Sbill if ((flags&(PRUNNING|PSTOPPED)) == 0) 1481302Sbill pflush(pp); 14917509Sedward (void) sigsetmask(omask); 1501302Sbill } 1511302Sbill } 1521302Sbill } 1531302Sbill 1541302Sbill /* 1551302Sbill * pwait - wait for current job to terminate, maintaining integrity 1561302Sbill * of current and previous job indicators. 1571302Sbill */ 1581302Sbill pwait() 1591302Sbill { 1601302Sbill register struct process *fp, *pp; 16117136Sralph int omask; 1621302Sbill 1631302Sbill /* 1641302Sbill * Here's where dead procs get flushed. 1651302Sbill */ 16617136Sralph omask = sigblock(sigmask(SIGCHLD)); 1671302Sbill for (pp = (fp = &proclist)->p_next; pp != PNULL; pp = (fp = pp)->p_next) 1681302Sbill if (pp->p_pid == 0) { 1691302Sbill fp->p_next = pp->p_next; 1701302Sbill xfree(pp->p_command); 1711302Sbill if (pp->p_cwd && --pp->p_cwd->di_count == 0) 1721302Sbill if (pp->p_cwd->di_next == 0) 1731302Sbill dfree(pp->p_cwd); 1741302Sbill xfree((char *)pp); 1751302Sbill pp = fp; 1761302Sbill } 17717509Sedward (void) sigsetmask(omask); 1781302Sbill pjwait(pcurrjob); 1791302Sbill } 1801302Sbill 1811302Sbill /* 1821302Sbill * pjwait - wait for a job to finish or become stopped 1831302Sbill * It is assumed to be in the foreground state (PFOREGND) 1841302Sbill */ 1851302Sbill pjwait(pp) 1861302Sbill register struct process *pp; 1871302Sbill { 1881302Sbill register struct process *fp; 18917136Sralph int jobflags, reason, omask; 1901302Sbill 19113561Ssam while (pp->p_pid != pp->p_jobid) 19213561Ssam pp = pp->p_friends; 1931302Sbill fp = pp; 1941302Sbill do { 1951302Sbill if ((fp->p_flags&(PFOREGND|PRUNNING)) == PRUNNING) 1961302Sbill printf("BUG: waiting for background job!\n"); 1971302Sbill } while ((fp = fp->p_friends) != pp); 1981302Sbill /* 1991302Sbill * Now keep pausing as long as we are not interrupted (SIGINT), 2001302Sbill * and the target process, or any of its friends, are running 2011302Sbill */ 2021302Sbill fp = pp; 20317136Sralph omask = sigblock(sigmask(SIGCHLD)); 2041302Sbill for (;;) { 2051302Sbill jobflags = 0; 2061302Sbill do 2071302Sbill jobflags |= fp->p_flags; 20817136Sralph while ((fp = (fp->p_friends)) != pp); 2091302Sbill if ((jobflags & PRUNNING) == 0) 2101302Sbill break; 21117136Sralph sigpause(0); 2121302Sbill } 21317509Sedward (void) sigsetmask(omask); 21417509Sedward if (tpgrp > 0) /* get tty back */ 21517509Sedward (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&tpgrp); 2163487Sroot if ((jobflags&(PSIGNALED|PSTOPPED|PTIME)) || 2173487Sroot !eq(dcwd->di_name, fp->p_cwd->di_name)) { 2183487Sroot if (jobflags&PSTOPPED) 2193487Sroot printf("\n"); 22017509Sedward (void) pprint(pp, AREASON|SHELLDIR); 2213487Sroot } 2223213Swnj if ((jobflags&(PINTERRUPTED|PSTOPPED)) && setintr && 2231302Sbill (!gointr || !eq(gointr, "-"))) { 2243213Swnj if ((jobflags & PSTOPPED) == 0) 2253213Swnj pflush(pp); 2263213Swnj pintr1(0); 2271302Sbill /*NOTREACHED*/ 2281302Sbill } 2291302Sbill reason = 0; 2301302Sbill fp = pp; 2311302Sbill do { 2321302Sbill if (fp->p_reason) 2331302Sbill reason = fp->p_flags & (PSIGNALED|PINTERRUPTED) ? 2341302Sbill fp->p_reason | QUOTE : fp->p_reason; 2351302Sbill } while ((fp = fp->p_friends) != pp); 2361302Sbill set("status", putn(reason)); 2371302Sbill if (reason && exiterr) 2381302Sbill exitstat(); 2391302Sbill pflush(pp); 2401302Sbill } 2411302Sbill 2421302Sbill /* 2431302Sbill * dowait - wait for all processes to finish 2441302Sbill */ 2451302Sbill dowait() 2461302Sbill { 2471302Sbill register struct process *pp; 24817136Sralph int omask; 2491302Sbill 2501302Sbill pjobs++; 25117136Sralph omask = sigblock(sigmask(SIGCHLD)); 2521302Sbill loop: 2531302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 25413561Ssam if (pp->p_pid && /* pp->p_pid == pp->p_jobid && */ 2551302Sbill pp->p_flags&PRUNNING) { 25617136Sralph sigpause(0); 2571302Sbill goto loop; 2581302Sbill } 25917509Sedward (void) sigsetmask(omask); 2601302Sbill pjobs = 0; 2611302Sbill } 2621302Sbill 2631302Sbill /* 2641302Sbill * pflushall - flush all jobs from list (e.g. at fork()) 2651302Sbill */ 2661302Sbill pflushall() 2671302Sbill { 2681302Sbill register struct process *pp; 2691302Sbill 2701302Sbill for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) 2711302Sbill if (pp->p_pid) 2721302Sbill pflush(pp); 2731302Sbill } 2741302Sbill 2751302Sbill /* 2761302Sbill * pflush - flag all process structures in the same job as the 2771302Sbill * the argument process for deletion. The actual free of the 2781302Sbill * space is not done here since pflush is called at interrupt level. 2791302Sbill */ 2801302Sbill pflush(pp) 2811302Sbill register struct process *pp; 2821302Sbill { 2831302Sbill register struct process *np; 2841302Sbill register int index; 2851302Sbill 2861302Sbill if (pp->p_pid == 0) { 2871302Sbill printf("BUG: process flushed twice"); 2881302Sbill return; 2891302Sbill } 2901302Sbill while (pp->p_pid != pp->p_jobid) 2911302Sbill pp = pp->p_friends; 2921302Sbill pclrcurr(pp); 2931302Sbill if (pp == pcurrjob) 2941302Sbill pcurrjob = 0; 2951302Sbill index = pp->p_index; 2961302Sbill np = pp; 2971302Sbill do { 2981302Sbill np->p_index = np->p_pid = 0; 2991302Sbill np->p_flags &= ~PNEEDNOTE; 3001302Sbill } while ((np = np->p_friends) != pp); 3011302Sbill if (index == pmaxindex) { 3021302Sbill for (np = proclist.p_next, index = 0; np; np = np->p_next) 3031302Sbill if (np->p_index > index) 3041302Sbill index = np->p_index; 3051302Sbill pmaxindex = index; 3061302Sbill } 3071302Sbill } 3081302Sbill 3091302Sbill /* 3101302Sbill * pclrcurr - make sure the given job is not the current or previous job; 3111302Sbill * pp MUST be the job leader 3121302Sbill */ 3131302Sbill pclrcurr(pp) 3141302Sbill register struct process *pp; 3151302Sbill { 3161302Sbill 3171302Sbill if (pp == pcurrent) 3181302Sbill if (pprevious != PNULL) { 3191302Sbill pcurrent = pprevious; 3201302Sbill pprevious = pgetcurr(pp); 3211302Sbill } else { 3221302Sbill pcurrent = pgetcurr(pp); 3231302Sbill pprevious = pgetcurr(pp); 3241302Sbill } 3251302Sbill else if (pp == pprevious) 3261302Sbill pprevious = pgetcurr(pp); 3271302Sbill } 3281302Sbill 3291302Sbill /* +4 here is 1 for '\0', 1 ea for << >& >> */ 3301302Sbill char command[PMAXLEN+4]; 3311302Sbill int cmdlen; 3321302Sbill char *cmdp; 3331302Sbill /* 3341302Sbill * palloc - allocate a process structure and fill it up. 3351302Sbill * an important assumption is made that the process is running. 3361302Sbill */ 3371302Sbill palloc(pid, t) 3381302Sbill int pid; 3391302Sbill register struct command *t; 3401302Sbill { 3411302Sbill register struct process *pp; 3421302Sbill int i; 3431302Sbill 3441302Sbill pp = (struct process *)calloc(1, sizeof(struct process)); 3451302Sbill pp->p_pid = pid; 3461302Sbill pp->p_flags = t->t_dflg & FAND ? PRUNNING : PRUNNING|PFOREGND; 3471302Sbill if (t->t_dflg & FTIME) 3481302Sbill pp->p_flags |= PPTIME; 3491302Sbill cmdp = command; 3501302Sbill cmdlen = 0; 3511302Sbill padd(t); 3521302Sbill *cmdp++ = 0; 3531302Sbill if (t->t_dflg & FPOU) { 3541302Sbill pp->p_flags |= PPOU; 3551302Sbill if (t->t_dflg & FDIAG) 3561302Sbill pp->p_flags |= PDIAG; 3571302Sbill } 3581302Sbill pp->p_command = savestr(command); 3591302Sbill if (pcurrjob) { 3601302Sbill struct process *fp; 3611302Sbill /* careful here with interrupt level */ 3621302Sbill pp->p_cwd = 0; 3631302Sbill pp->p_index = pcurrjob->p_index; 3641302Sbill pp->p_friends = pcurrjob; 3651302Sbill pp->p_jobid = pcurrjob->p_pid; 3661302Sbill for (fp = pcurrjob; fp->p_friends != pcurrjob; fp = fp->p_friends) 3671302Sbill ; 3681302Sbill fp->p_friends = pp; 3691302Sbill } else { 3701302Sbill pcurrjob = pp; 3711302Sbill pp->p_jobid = pid; 3721302Sbill pp->p_friends = pp; 3731302Sbill pp->p_cwd = dcwd; 3741302Sbill dcwd->di_count++; 3751302Sbill if (pmaxindex < BIGINDEX) 3761302Sbill pp->p_index = ++pmaxindex; 3771302Sbill else { 3781302Sbill struct process *np; 3791302Sbill 3801302Sbill for (i = 1; ; i++) { 3811302Sbill for (np = proclist.p_next; np; np = np->p_next) 3821302Sbill if (np->p_index == i) 3831302Sbill goto tryagain; 3842357Swnj pp->p_index = i; 3852357Swnj if (i > pmaxindex) 3862357Swnj pmaxindex = i; 3871302Sbill break; 3881302Sbill tryagain:; 3891302Sbill } 3901302Sbill } 3911302Sbill if (pcurrent == PNULL) 3921302Sbill pcurrent = pp; 3931302Sbill else if (pprevious == PNULL) 3941302Sbill pprevious = pp; 3951302Sbill } 3961302Sbill pp->p_next = proclist.p_next; 3971302Sbill proclist.p_next = pp; 39817509Sedward (void) gettimeofday(&pp->p_btime, (struct timezone *)0); 3991302Sbill } 4001302Sbill 4011302Sbill padd(t) 4021302Sbill register struct command *t; 4031302Sbill { 4041302Sbill char **argp; 4051302Sbill 4061302Sbill if (t == 0) 4071302Sbill return; 4081302Sbill switch (t->t_dtyp) { 4091302Sbill 4101302Sbill case TPAR: 4111302Sbill pads("( "); 4121302Sbill padd(t->t_dspr); 4131302Sbill pads(" )"); 4141302Sbill break; 4151302Sbill 4161302Sbill case TCOM: 4171302Sbill for (argp = t->t_dcom; *argp; argp++) { 4181302Sbill pads(*argp); 4191302Sbill if (argp[1]) 4201302Sbill pads(" "); 4211302Sbill } 4221302Sbill break; 4231302Sbill 42417903Sedward case TOR: 42517903Sedward case TAND: 4261302Sbill case TFIL: 4271302Sbill case TLST: 4281302Sbill padd(t->t_dcar); 42917903Sedward switch (t->t_dtyp) { 43017903Sedward case TOR: 43117903Sedward pads(" || "); 43217903Sedward break; 43317903Sedward case TAND: 43417903Sedward pads(" && "); 43517903Sedward break; 43617903Sedward case TFIL: 43717903Sedward pads(" | "); 43817903Sedward break; 43917903Sedward case TLST: 44017903Sedward pads("; "); 44117903Sedward break; 44217903Sedward } 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) { 46717509Sedward (void) strcpy(cmdp, " ..."); 4681302Sbill cmdlen = PMAXLEN; 4691302Sbill cmdp += 4; 4701302Sbill return; 4711302Sbill } 47217509Sedward (void) 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"); 63010035Ssam { static struct rusage zru; 63110704Ssam prusage(&zru, &pp->p_rusage, &pp->p_etime, 63210704Ssam &pp->p_btime); 63310035Ssam } 6341302Sbill } 6351302Sbill if (tp == pp->p_friends) { 6361302Sbill if (linp != linbuf) 6371302Sbill printf("\n"); 6381302Sbill if (flag&SHELLDIR && !eq(tp->p_cwd->di_name, dcwd->di_name)) { 6391302Sbill printf("(wd now: "); 6401302Sbill dtildepr(value("home"), dcwd->di_name); 6411302Sbill printf(")\n"); 6421302Sbill } 6431302Sbill } 6441302Sbill } while ((pp = pp->p_friends) != tp); 6451302Sbill if (jobflags&PTIME && (jobflags&(PSTOPPED|PRUNNING)) == 0) { 6461302Sbill if (jobflags & NUMBER) 6471302Sbill printf(" "); 6481302Sbill ptprint(tp); 6491302Sbill } 6501302Sbill return (jobflags); 6511302Sbill } 6521302Sbill 6531302Sbill ptprint(tp) 6541302Sbill register struct process *tp; 6551302Sbill { 65610704Ssam struct timeval tetime, diff; 65710704Ssam static struct timeval ztime; 65810035Ssam struct rusage ru; 65910035Ssam static struct rusage zru; 6601302Sbill register struct process *pp = tp; 6611302Sbill 66210035Ssam ru = zru; 66310704Ssam tetime = ztime; 6641302Sbill do { 66510035Ssam ruadd(&ru, &pp->p_rusage); 66610704Ssam tvsub(&diff, &pp->p_etime, &pp->p_btime); 66710704Ssam if (timercmp(&diff, &tetime, >)) 66810704Ssam tetime = diff; 6691302Sbill } while ((pp = pp->p_friends) != tp); 67010704Ssam prusage(&zru, &ru, &tetime, &ztime); 6711302Sbill } 6721302Sbill 6731302Sbill /* 6741302Sbill * dojobs - print all jobs 6751302Sbill */ 6761302Sbill dojobs(v) 6771302Sbill char **v; 6781302Sbill { 6791302Sbill register struct process *pp; 6801302Sbill register int flag = NUMBER|NAME|REASON; 6811302Sbill int i; 6821302Sbill 6831302Sbill if (chkstop) 6841302Sbill chkstop = 2; 6851302Sbill if (*++v) { 6861302Sbill if (v[1] || !eq(*v, "-l")) 6871302Sbill error("Usage: jobs [ -l ]"); 6881302Sbill flag |= FANCY|JOBDIR; 6891302Sbill } 6901302Sbill for (i = 1; i <= pmaxindex; i++) 6911302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 6921302Sbill if (pp->p_index == i && pp->p_pid == pp->p_jobid) { 6931302Sbill pp->p_flags &= ~PNEEDNOTE; 6941302Sbill if (!(pprint(pp, flag) & (PRUNNING|PSTOPPED))) 6951302Sbill pflush(pp); 6961302Sbill break; 6971302Sbill } 6981302Sbill } 6991302Sbill 7001302Sbill /* 7011302Sbill * dofg - builtin - put the job into the foreground 7021302Sbill */ 7031302Sbill dofg(v) 7041302Sbill char **v; 7051302Sbill { 7061302Sbill register struct process *pp; 7071302Sbill 7081302Sbill okpcntl(); 7091302Sbill ++v; 7101302Sbill do { 7111302Sbill pp = pfind(*v); 7121302Sbill pstart(pp, 1); 7131302Sbill pjwait(pp); 7141302Sbill } while (*v && *++v); 7151302Sbill } 7161302Sbill 7171302Sbill /* 7181302Sbill * %... - builtin - put the job into the foreground 7191302Sbill */ 7201302Sbill dofg1(v) 7211302Sbill char **v; 7221302Sbill { 7231302Sbill register struct process *pp; 7241302Sbill 7251302Sbill okpcntl(); 7261302Sbill pp = pfind(v[0]); 7271302Sbill pstart(pp, 1); 7281302Sbill pjwait(pp); 7291302Sbill } 7301302Sbill 7311302Sbill /* 7321302Sbill * dobg - builtin - put the job into the background 7331302Sbill */ 7341302Sbill dobg(v) 7351302Sbill char **v; 7361302Sbill { 7371302Sbill register struct process *pp; 7381302Sbill 7391302Sbill okpcntl(); 7401302Sbill ++v; 7411302Sbill do { 7421302Sbill pp = pfind(*v); 7431302Sbill pstart(pp, 0); 7441302Sbill } while (*v && *++v); 7451302Sbill } 7461302Sbill 7471302Sbill /* 7481302Sbill * %... & - builtin - put the job into the background 7491302Sbill */ 7501302Sbill dobg1(v) 7511302Sbill char **v; 7521302Sbill { 7531302Sbill register struct process *pp; 7541302Sbill 7551302Sbill pp = pfind(v[0]); 7561302Sbill pstart(pp, 0); 7571302Sbill } 7581302Sbill 7591302Sbill /* 7601302Sbill * dostop - builtin - stop the job 7611302Sbill */ 7621302Sbill dostop(v) 7631302Sbill char **v; 7641302Sbill { 7651302Sbill 7661302Sbill pkill(++v, SIGSTOP); 7671302Sbill } 7681302Sbill 7691302Sbill /* 7701302Sbill * dokill - builtin - superset of kill (1) 7711302Sbill */ 7721302Sbill dokill(v) 7731302Sbill char **v; 7741302Sbill { 7751302Sbill register int signum; 7761302Sbill register char *name; 7771302Sbill 7781302Sbill v++; 7791302Sbill if (v[0] && v[0][0] == '-') { 7801302Sbill if (v[0][1] == 'l') { 7811302Sbill for (signum = 1; signum <= NSIG; signum++) { 7821302Sbill if (name = mesg[signum].iname) 7831302Sbill printf("%s ", name); 7841302Sbill if (signum == 16) 78517509Sedward putchar('\n'); 7861302Sbill } 78717509Sedward putchar('\n'); 7881302Sbill return; 7891302Sbill } 7901302Sbill if (digit(v[0][1])) { 7911302Sbill signum = atoi(v[0]+1); 7921302Sbill if (signum < 1 || signum > NSIG) 7931302Sbill bferr("Bad signal number"); 7941302Sbill } else { 7951302Sbill name = &v[0][1]; 7961302Sbill for (signum = 1; signum <= NSIG; signum++) 7971302Sbill if (mesg[signum].iname && 7981302Sbill eq(name, mesg[signum].iname)) 7991302Sbill goto gotsig; 8001302Sbill setname(name); 8011302Sbill bferr("Unknown signal; kill -l lists signals"); 8021302Sbill } 8031302Sbill gotsig: 8041302Sbill v++; 8051302Sbill } else 8061302Sbill signum = SIGTERM; 8071302Sbill pkill(v, signum); 8081302Sbill } 8091302Sbill 8101302Sbill pkill(v, signum) 8111302Sbill char **v; 8121302Sbill int signum; 8131302Sbill { 8141302Sbill register struct process *pp, *np; 8151302Sbill register int jobflags = 0; 81617136Sralph int omask, pid, err = 0; 81713561Ssam char *cp; 8181302Sbill extern char *sys_errlist[]; 8191302Sbill 82017136Sralph omask = sigmask(SIGCHLD); 8211302Sbill if (setintr) 82217136Sralph omask |= sigmask(SIGINT); 82317136Sralph omask = sigblock(omask) & ~omask; 8241302Sbill while (*v) { 82513561Ssam cp = globone(*v); 82613561Ssam if (*cp == '%') { 82713561Ssam np = pp = pfind(cp); 8281302Sbill do 8291302Sbill jobflags |= np->p_flags; 8301302Sbill while ((np = np->p_friends) != pp); 8311302Sbill switch (signum) { 8321302Sbill 8331302Sbill case SIGSTOP: 8341302Sbill case SIGTSTP: 8351302Sbill case SIGTTIN: 8361302Sbill case SIGTTOU: 8371302Sbill if ((jobflags & PRUNNING) == 0) { 83813561Ssam printf("%s: Already stopped\n", cp); 8391302Sbill err++; 8401302Sbill goto cont; 8411302Sbill } 8421302Sbill } 84318424Smckusick if (killpg(pp->p_jobid, signum) < 0) { 84418424Smckusick printf("%s: ", cp); 84518424Smckusick printf("%s\n", sys_errlist[errno]); 84618424Smckusick err++; 84718424Smckusick } 8481302Sbill if (signum == SIGTERM || signum == SIGHUP) 84917509Sedward (void) killpg(pp->p_jobid, SIGCONT); 85013561Ssam } else if (!digit(*cp)) 8511302Sbill bferr("Arguments should be jobs or process id's"); 8521302Sbill else { 85313561Ssam pid = atoi(cp); 8541302Sbill if (kill(pid, signum) < 0) { 8551302Sbill printf("%d: ", pid); 8561302Sbill printf("%s\n", sys_errlist[errno]); 8571302Sbill err++; 8581302Sbill goto cont; 8591302Sbill } 8601302Sbill if (signum == SIGTERM || signum == SIGHUP) 86117509Sedward (void) kill(pid, SIGCONT); 8621302Sbill } 8631302Sbill cont: 86413561Ssam xfree(cp); 8651302Sbill v++; 8661302Sbill } 86717509Sedward (void) sigsetmask(omask); 8681302Sbill if (err) 8691302Sbill error(NOSTR); 8701302Sbill } 8711302Sbill 8721302Sbill /* 8731302Sbill * pstart - start the job in foreground/background 8741302Sbill */ 8751302Sbill pstart(pp, foregnd) 8761302Sbill register struct process *pp; 8771302Sbill int foregnd; 8781302Sbill { 8791302Sbill register struct process *np; 88017136Sralph int omask, jobflags = 0; 8811302Sbill 88217136Sralph omask = sigblock(sigmask(SIGCHLD)); 8831302Sbill np = pp; 8841302Sbill do { 8851302Sbill jobflags |= np->p_flags; 8861302Sbill if (np->p_flags&(PRUNNING|PSTOPPED)) { 8871302Sbill np->p_flags |= PRUNNING; 8881302Sbill np->p_flags &= ~PSTOPPED; 8891302Sbill if (foregnd) 8901302Sbill np->p_flags |= PFOREGND; 8911302Sbill else 8921302Sbill np->p_flags &= ~PFOREGND; 8931302Sbill } 8941302Sbill } while((np = np->p_friends) != pp); 8952357Swnj if (!foregnd) 8962357Swnj pclrcurr(pp); 89717509Sedward (void) pprint(pp, foregnd ? NAME|JOBDIR : NUMBER|NAME|AMPERSAND); 8981302Sbill if (foregnd) 89917509Sedward (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&pp->p_jobid); 9001302Sbill if (jobflags&PSTOPPED) 90117509Sedward (void) killpg(pp->p_jobid, SIGCONT); 90217509Sedward (void) sigsetmask(omask); 9031302Sbill } 9041302Sbill 9051302Sbill panystop(neednl) 9061302Sbill { 9071302Sbill register struct process *pp; 9081302Sbill 9091302Sbill chkstop = 2; 9101302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 9111302Sbill if (pp->p_flags & PSTOPPED) 9121302Sbill error("\nThere are stopped jobs" + 1 - neednl); 9131302Sbill } 9141302Sbill 9151302Sbill struct process * 9161302Sbill pfind(cp) 9171302Sbill char *cp; 9181302Sbill { 9191302Sbill register struct process *pp, *np; 9201302Sbill 9211302Sbill if (cp == 0 || cp[1] == 0 || eq(cp, "%%") || eq(cp, "%+")) { 9221302Sbill if (pcurrent == PNULL) 9231302Sbill bferr("No current job"); 9241302Sbill return (pcurrent); 9251302Sbill } 9261302Sbill if (eq(cp, "%-") || eq(cp, "%#")) { 9271302Sbill if (pprevious == PNULL) 9281302Sbill bferr("No previous job"); 9291302Sbill return (pprevious); 9301302Sbill } 9311302Sbill if (digit(cp[1])) { 9321302Sbill int index = atoi(cp+1); 9331302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 9341302Sbill if (pp->p_index == index && pp->p_pid == pp->p_jobid) 9351302Sbill return (pp); 9361302Sbill bferr("No such job"); 9371302Sbill } 9381302Sbill np = PNULL; 9391302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 9401302Sbill if (pp->p_pid == pp->p_jobid) { 9411302Sbill if (cp[1] == '?') { 9421302Sbill register char *dp; 9431302Sbill for (dp = pp->p_command; *dp; dp++) { 9441302Sbill if (*dp != cp[2]) 9451302Sbill continue; 9461302Sbill if (prefix(cp+2, dp)) 9471302Sbill goto match; 9481302Sbill } 9491302Sbill } else if (prefix(cp+1, pp->p_command)) { 9501302Sbill match: 9511302Sbill if (np) 9521302Sbill bferr("Ambiguous"); 9531302Sbill np = pp; 9541302Sbill } 9551302Sbill } 9561302Sbill if (np) 9571302Sbill return (np); 9581302Sbill if (cp[1] == '?') 9591302Sbill bferr("No job matches pattern"); 9601302Sbill else 9611302Sbill bferr("No such job"); 96217509Sedward /*NOTREACHED*/ 9631302Sbill } 9641302Sbill 9651302Sbill /* 9662357Swnj * pgetcurr - find most recent job that is not pp, preferably stopped 9671302Sbill */ 9681302Sbill struct process * 9691302Sbill pgetcurr(pp) 9701302Sbill register struct process *pp; 9711302Sbill { 9721302Sbill register struct process *np; 9732357Swnj register struct process *xp = PNULL; 9741302Sbill 9751302Sbill for (np = proclist.p_next; np; np = np->p_next) 9761302Sbill if (np != pcurrent && np != pp && np->p_pid && 9771302Sbill np->p_pid == np->p_jobid) { 9782357Swnj if (np->p_flags & PSTOPPED) 9792357Swnj return (np); 9802357Swnj if (xp == PNULL) 9812357Swnj xp = np; 9821302Sbill } 9832357Swnj return (xp); 9841302Sbill } 9851302Sbill 9861302Sbill /* 9871302Sbill * donotify - flag the job so as to report termination asynchronously 9881302Sbill */ 9891302Sbill donotify(v) 9901302Sbill char **v; 9911302Sbill { 9921302Sbill register struct process *pp; 9931302Sbill 9941302Sbill pp = pfind(*++v); 9951302Sbill pp->p_flags |= PNOTIFY; 9961302Sbill } 9971302Sbill 9981302Sbill /* 9991302Sbill * Do the fork and whatever should be done in the child side that 10001302Sbill * should not be done if we are not forking at all (like for simple builtin's) 10011302Sbill * Also do everything that needs any signals fiddled with in the parent side 10021302Sbill * 10031302Sbill * Wanttty tells whether process and/or tty pgrps are to be manipulated: 10041302Sbill * -1: leave tty alone; inherit pgrp from parent 10051302Sbill * 0: already have tty; manipulate process pgrps only 10061302Sbill * 1: want to claim tty; manipulate process and tty pgrps 10071302Sbill * It is usually just the value of tpgrp. 10081302Sbill */ 10091302Sbill pfork(t, wanttty) 10101302Sbill struct command *t; /* command we are forking for */ 10111302Sbill int wanttty; 10121302Sbill { 10131302Sbill register int pid; 10141302Sbill bool ignint = 0; 101517136Sralph int pgrp, omask; 10161302Sbill 10171302Sbill /* 10181302Sbill * A child will be uninterruptible only under very special 10191302Sbill * conditions. Remember that the semantics of '&' is 10201302Sbill * implemented by disconnecting the process from the tty so 10211302Sbill * signals do not need to ignored just for '&'. 10221302Sbill * Thus signals are set to default action for children unless: 10231302Sbill * we have had an "onintr -" (then specifically ignored) 10241302Sbill * we are not playing with signals (inherit action) 10251302Sbill */ 10261302Sbill if (setintr) 10271302Sbill ignint = (tpgrp == -1 && (t->t_dflg&FINT)) 10281302Sbill || (gointr && eq(gointr, "-")); 10291302Sbill /* 10301302Sbill * Hold SIGCHLD until we have the process installed in our table. 10311302Sbill */ 103217136Sralph omask = sigblock(sigmask(SIGCHLD)); 10331302Sbill while ((pid = fork()) < 0) 10341302Sbill if (setintr == 0) 10351302Sbill sleep(FORKSLEEP); 10361302Sbill else { 103717509Sedward (void) sigsetmask(omask); 10381302Sbill error("No more processes"); 10391302Sbill } 10401302Sbill if (pid == 0) { 10411302Sbill settimes(); 10421302Sbill pgrp = pcurrjob ? pcurrjob->p_jobid : getpid(); 10431302Sbill pflushall(); 10441302Sbill pcurrjob = PNULL; 10451302Sbill child++; 10461302Sbill if (setintr) { 10471302Sbill setintr = 0; /* until I think otherwise */ 10481302Sbill /* 10491302Sbill * Children just get blown away on SIGINT, SIGQUIT 10501302Sbill * unless "onintr -" seen. 10511302Sbill */ 105217509Sedward (void) signal(SIGINT, ignint ? SIG_IGN : SIG_DFL); 105317509Sedward (void) signal(SIGQUIT, ignint ? SIG_IGN : SIG_DFL); 10541302Sbill if (wanttty >= 0) { 10551302Sbill /* make stoppable */ 105617509Sedward (void) signal(SIGTSTP, SIG_DFL); 105717509Sedward (void) signal(SIGTTIN, SIG_DFL); 105817509Sedward (void) signal(SIGTTOU, SIG_DFL); 10591302Sbill } 106017509Sedward (void) signal(SIGTERM, parterm); 10611302Sbill } else if (tpgrp == -1 && (t->t_dflg&FINT)) { 106217509Sedward (void) signal(SIGINT, SIG_IGN); 106317509Sedward (void) signal(SIGQUIT, SIG_IGN); 10641302Sbill } 10651302Sbill if (wanttty > 0) 106617509Sedward (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&pgrp); 10671302Sbill if (wanttty >= 0 && tpgrp >= 0) 106817509Sedward (void) setpgrp(0, pgrp); 10691302Sbill if (tpgrp > 0) 10701302Sbill tpgrp = 0; /* gave tty away */ 10711302Sbill /* 10721302Sbill * Nohup and nice apply only to TCOM's but it would be 10731302Sbill * nice (?!?) if you could say "nohup (foo;bar)" 10741302Sbill * Then the parser would have to know about nice/nohup/time 10751302Sbill */ 10761302Sbill if (t->t_dflg & FNOHUP) 107717509Sedward (void) signal(SIGHUP, SIG_IGN); 107817136Sralph if (t->t_dflg & FNICE) 107917509Sedward (void) setpriority(PRIO_PROCESS, 0, t->t_nice); 10801302Sbill } else { 10811302Sbill palloc(pid, t); 108217509Sedward (void) sigsetmask(omask); 10831302Sbill } 10841302Sbill 10851302Sbill return (pid); 10861302Sbill } 10871302Sbill 10881302Sbill okpcntl() 10891302Sbill { 10901302Sbill 10911302Sbill if (tpgrp == -1) 10921302Sbill error("No job control in this shell"); 10931302Sbill if (tpgrp == 0) 10941302Sbill error("No job control in subshells"); 10951302Sbill } 1096