1*47823Sbostic /*- 2*47823Sbostic * Copyright (c) 1980, 1991 The Regents of the University of California. 3*47823Sbostic * All rights reserved. 4*47823Sbostic * 5*47823Sbostic * %sccs.include.redist.c% 621940Sdist */ 721940Sdist 817509Sedward #ifndef lint 9*47823Sbostic static char sccsid[] = "@(#)proc.c 5.15 (Berkeley) 04/04/91"; 10*47823Sbostic #endif /* not lint */ 111302Sbill 121302Sbill #include "sh.h" 131302Sbill #include "sh.dir.h" 141302Sbill #include "sh.proc.h" 1544759Smarc #include <string.h> 1613590Swnj #include <sys/wait.h> 171302Sbill #include <sys/ioctl.h> 181302Sbill 191302Sbill /* 201302Sbill * C Shell - functions that manage processes, handling hanging, termination 211302Sbill */ 221302Sbill 231302Sbill #define BIGINDEX 9 /* largest desirable job index */ 241302Sbill 251302Sbill /* 261302Sbill * pchild - called at interrupt level by the SIGCHLD signal 271302Sbill * indicating that at least one child has terminated or stopped 281302Sbill * thus at least one wait system call will definitely return a 291302Sbill * childs status. Top level routines (like pwait) must be sure 301302Sbill * to mask interrupts when playing with the proclist data structures! 311302Sbill */ 3246657Sbostic void 331302Sbill pchild() 341302Sbill { 351302Sbill register struct process *pp; 361302Sbill register struct process *fp; 371302Sbill register int pid; 381302Sbill union wait w; 391302Sbill int jobflags; 4010035Ssam struct rusage ru; 4133369Sbostic extern int insource; 421302Sbill 431302Sbill loop: 4446657Sbostic pid = wait3((int *)&w, 4546657Sbostic (setintr && (intty || insource) ? WNOHANG|WUNTRACED:WNOHANG), &ru); 461302Sbill if (pid <= 0) { 471302Sbill if (errno == EINTR) { 481302Sbill errno = 0; 491302Sbill goto loop; 501302Sbill } 511302Sbill pnoprocesses = pid == -1; 521302Sbill return; 531302Sbill } 541302Sbill for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) 551302Sbill if (pid == pp->p_pid) 561302Sbill goto found; 571302Sbill goto loop; 581302Sbill found: 591302Sbill if (pid == atoi(value("child"))) 601302Sbill unsetv("child"); 611302Sbill pp->p_flags &= ~(PRUNNING|PSTOPPED|PREPORTED); 621302Sbill if (WIFSTOPPED(w)) { 631302Sbill pp->p_flags |= PSTOPPED; 641302Sbill pp->p_reason = w.w_stopsig; 651302Sbill } else { 6617509Sedward if (pp->p_flags & (PTIME|PPTIME) || adrof("time")) 6717509Sedward (void) gettimeofday(&pp->p_etime, (struct timezone *)0); 6810035Ssam pp->p_rusage = ru; 691302Sbill if (WIFSIGNALED(w)) { 701302Sbill if (w.w_termsig == SIGINT) 711302Sbill pp->p_flags |= PINTERRUPTED; 721302Sbill else 731302Sbill pp->p_flags |= PSIGNALED; 741302Sbill if (w.w_coredump) 751302Sbill pp->p_flags |= PDUMPED; 761302Sbill pp->p_reason = w.w_termsig; 771302Sbill } else { 781302Sbill pp->p_reason = w.w_retcode; 791302Sbill if (pp->p_reason != 0) 801302Sbill pp->p_flags |= PAEXITED; 811302Sbill else 821302Sbill pp->p_flags |= PNEXITED; 831302Sbill } 841302Sbill } 851302Sbill jobflags = 0; 861302Sbill fp = pp; 871302Sbill do { 881302Sbill if ((fp->p_flags & (PPTIME|PRUNNING|PSTOPPED)) == 0 && 891302Sbill !child && adrof("time") && 9017509Sedward fp->p_rusage.ru_utime.tv_sec+fp->p_rusage.ru_stime.tv_sec >= 911302Sbill atoi(value("time"))) 921302Sbill fp->p_flags |= PTIME; 931302Sbill jobflags |= fp->p_flags; 941302Sbill } while ((fp = fp->p_friends) != pp); 951302Sbill pp->p_flags &= ~PFOREGND; 961302Sbill if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { 971302Sbill pp->p_flags &= ~PPTIME; 981302Sbill pp->p_flags |= PTIME; 991302Sbill } 1001302Sbill if ((jobflags & (PRUNNING|PREPORTED)) == 0) { 1011302Sbill fp = pp; 1021302Sbill do { 1031302Sbill if (fp->p_flags&PSTOPPED) 1041302Sbill fp->p_flags |= PREPORTED; 1051302Sbill } while((fp = fp->p_friends) != pp); 1061302Sbill while(fp->p_pid != fp->p_jobid) 1071302Sbill fp = fp->p_friends; 1082357Swnj if (jobflags&PSTOPPED) { 1092357Swnj if (pcurrent && pcurrent != fp) 1102357Swnj pprevious = pcurrent; 1112357Swnj pcurrent = fp; 1122357Swnj } else 1132357Swnj pclrcurr(fp); 1141302Sbill if (jobflags&PFOREGND) { 1151302Sbill if (jobflags & (PSIGNALED|PSTOPPED|PPTIME) || 1161302Sbill !eq(dcwd->di_name, fp->p_cwd->di_name)) { 1173213Swnj ; /* print in pjwait */ 1183646Sroot } 1193646Sroot /* 1203646Sroot else if ((jobflags & (PTIME|PSTOPPED)) == PTIME) 1211302Sbill ptprint(fp); 1223646Sroot */ 1231302Sbill } else { 1241302Sbill if (jobflags&PNOTIFY || adrof("notify")) { 1251302Sbill printf("\215\n"); 12617509Sedward (void) pprint(pp, NUMBER|NAME|REASON); 1271302Sbill if ((jobflags&PSTOPPED) == 0) 1281302Sbill pflush(pp); 1291302Sbill } else { 1301302Sbill fp->p_flags |= PNEEDNOTE; 1311302Sbill neednote++; 1321302Sbill } 1331302Sbill } 1341302Sbill } 1351302Sbill goto loop; 1361302Sbill } 1371302Sbill 1381302Sbill pnote() 1391302Sbill { 1401302Sbill register struct process *pp; 14131685Sbostic int flags; 14231685Sbostic long omask; 1431302Sbill 1441302Sbill neednote = 0; 1451302Sbill for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) { 1461302Sbill if (pp->p_flags & PNEEDNOTE) { 14717136Sralph omask = sigblock(sigmask(SIGCHLD)); 1481302Sbill pp->p_flags &= ~PNEEDNOTE; 1491302Sbill flags = pprint(pp, NUMBER|NAME|REASON); 1501302Sbill if ((flags&(PRUNNING|PSTOPPED)) == 0) 1511302Sbill pflush(pp); 15217509Sedward (void) sigsetmask(omask); 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; 16431685Sbostic long omask; 1651302Sbill 1661302Sbill /* 1671302Sbill * Here's where dead procs get flushed. 1681302Sbill */ 16917136Sralph omask = sigblock(sigmask(SIGCHLD)); 1701302Sbill for (pp = (fp = &proclist)->p_next; pp != PNULL; pp = (fp = pp)->p_next) 1711302Sbill if (pp->p_pid == 0) { 1721302Sbill fp->p_next = pp->p_next; 1731302Sbill xfree(pp->p_command); 1741302Sbill if (pp->p_cwd && --pp->p_cwd->di_count == 0) 1751302Sbill if (pp->p_cwd->di_next == 0) 1761302Sbill dfree(pp->p_cwd); 1771302Sbill xfree((char *)pp); 1781302Sbill pp = fp; 1791302Sbill } 18017509Sedward (void) sigsetmask(omask); 1811302Sbill pjwait(pcurrjob); 1821302Sbill } 1831302Sbill 1841302Sbill /* 1851302Sbill * pjwait - wait for a job to finish or become stopped 1861302Sbill * It is assumed to be in the foreground state (PFOREGND) 1871302Sbill */ 1881302Sbill pjwait(pp) 1891302Sbill register struct process *pp; 1901302Sbill { 1911302Sbill register struct process *fp; 19231685Sbostic int jobflags, reason; 19331685Sbostic long omask; 1941302Sbill 19513561Ssam while (pp->p_pid != pp->p_jobid) 19613561Ssam 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; 20717136Sralph omask = sigblock(sigmask(SIGCHLD)); 2081302Sbill for (;;) { 2091302Sbill jobflags = 0; 2101302Sbill do 2111302Sbill jobflags |= fp->p_flags; 21217136Sralph while ((fp = (fp->p_friends)) != pp); 2131302Sbill if ((jobflags & PRUNNING) == 0) 2141302Sbill break; 21531685Sbostic sigpause(sigblock(0L) &~ sigmask(SIGCHLD)); 2161302Sbill } 21717509Sedward (void) sigsetmask(omask); 21817509Sedward if (tpgrp > 0) /* get tty back */ 21917509Sedward (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&tpgrp); 2203487Sroot if ((jobflags&(PSIGNALED|PSTOPPED|PTIME)) || 2213487Sroot !eq(dcwd->di_name, fp->p_cwd->di_name)) { 2223487Sroot if (jobflags&PSTOPPED) 2233487Sroot printf("\n"); 22417509Sedward (void) 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; 25231685Sbostic long omask; 2531302Sbill 2541302Sbill pjobs++; 25517136Sralph omask = sigblock(sigmask(SIGCHLD)); 2561302Sbill loop: 2571302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 25813561Ssam if (pp->p_pid && /* pp->p_pid == pp->p_jobid && */ 2591302Sbill pp->p_flags&PRUNNING) { 26031685Sbostic sigpause(0L); 2611302Sbill goto loop; 2621302Sbill } 26317509Sedward (void) sigsetmask(omask); 2641302Sbill pjobs = 0; 2651302Sbill } 2661302Sbill 2671302Sbill /* 2681302Sbill * pflushall - flush all jobs from list (e.g. at fork()) 2691302Sbill */ 2701302Sbill pflushall() 2711302Sbill { 2721302Sbill register struct process *pp; 2731302Sbill 2741302Sbill for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) 2751302Sbill if (pp->p_pid) 2761302Sbill pflush(pp); 2771302Sbill } 2781302Sbill 2791302Sbill /* 2801302Sbill * pflush - flag all process structures in the same job as the 2811302Sbill * the argument process for deletion. The actual free of the 2821302Sbill * space is not done here since pflush is called at interrupt level. 2831302Sbill */ 2841302Sbill pflush(pp) 2851302Sbill register struct process *pp; 2861302Sbill { 2871302Sbill register struct process *np; 2881302Sbill register int index; 2891302Sbill 2901302Sbill if (pp->p_pid == 0) { 2911302Sbill printf("BUG: process flushed twice"); 2921302Sbill return; 2931302Sbill } 2941302Sbill while (pp->p_pid != pp->p_jobid) 2951302Sbill pp = pp->p_friends; 2961302Sbill pclrcurr(pp); 2971302Sbill if (pp == pcurrjob) 2981302Sbill pcurrjob = 0; 2991302Sbill index = pp->p_index; 3001302Sbill np = pp; 3011302Sbill do { 3021302Sbill np->p_index = np->p_pid = 0; 3031302Sbill np->p_flags &= ~PNEEDNOTE; 3041302Sbill } while ((np = np->p_friends) != pp); 3051302Sbill if (index == pmaxindex) { 3061302Sbill for (np = proclist.p_next, index = 0; np; np = np->p_next) 3071302Sbill if (np->p_index > index) 3081302Sbill index = np->p_index; 3091302Sbill pmaxindex = index; 3101302Sbill } 3111302Sbill } 3121302Sbill 3131302Sbill /* 3141302Sbill * pclrcurr - make sure the given job is not the current or previous job; 3151302Sbill * pp MUST be the job leader 3161302Sbill */ 3171302Sbill pclrcurr(pp) 3181302Sbill register struct process *pp; 3191302Sbill { 3201302Sbill 3211302Sbill if (pp == pcurrent) 3221302Sbill if (pprevious != PNULL) { 3231302Sbill pcurrent = pprevious; 3241302Sbill pprevious = pgetcurr(pp); 3251302Sbill } else { 3261302Sbill pcurrent = pgetcurr(pp); 3271302Sbill pprevious = pgetcurr(pp); 3281302Sbill } 3291302Sbill else if (pp == pprevious) 3301302Sbill pprevious = pgetcurr(pp); 3311302Sbill } 3321302Sbill 3331302Sbill /* +4 here is 1 for '\0', 1 ea for << >& >> */ 3341302Sbill char command[PMAXLEN+4]; 3351302Sbill int cmdlen; 3361302Sbill char *cmdp; 3371302Sbill /* 3381302Sbill * palloc - allocate a process structure and fill it up. 3391302Sbill * an important assumption is made that the process is running. 3401302Sbill */ 3411302Sbill palloc(pid, t) 3421302Sbill int pid; 3431302Sbill register struct command *t; 3441302Sbill { 3451302Sbill register struct process *pp; 3461302Sbill int i; 3471302Sbill 3481302Sbill pp = (struct process *)calloc(1, sizeof(struct process)); 3491302Sbill pp->p_pid = pid; 35047724Sbostic pp->p_flags = t->t_dflg & F_AMPERSAND ? PRUNNING : PRUNNING|PFOREGND; 35147724Sbostic if (t->t_dflg & F_TIME) 3521302Sbill pp->p_flags |= PPTIME; 3531302Sbill cmdp = command; 3541302Sbill cmdlen = 0; 3551302Sbill padd(t); 3561302Sbill *cmdp++ = 0; 35747724Sbostic if (t->t_dflg & F_PIPEOUT) { 3581302Sbill pp->p_flags |= PPOU; 35947724Sbostic if (t->t_dflg & F_STDERR) 3601302Sbill pp->p_flags |= PDIAG; 3611302Sbill } 3621302Sbill pp->p_command = savestr(command); 3631302Sbill if (pcurrjob) { 3641302Sbill struct process *fp; 3651302Sbill /* careful here with interrupt level */ 3661302Sbill pp->p_cwd = 0; 3671302Sbill pp->p_index = pcurrjob->p_index; 3681302Sbill pp->p_friends = pcurrjob; 3691302Sbill pp->p_jobid = pcurrjob->p_pid; 3701302Sbill for (fp = pcurrjob; fp->p_friends != pcurrjob; fp = fp->p_friends) 3711302Sbill ; 3721302Sbill fp->p_friends = pp; 3731302Sbill } else { 3741302Sbill pcurrjob = pp; 3751302Sbill pp->p_jobid = pid; 3761302Sbill pp->p_friends = pp; 3771302Sbill pp->p_cwd = dcwd; 3781302Sbill dcwd->di_count++; 3791302Sbill if (pmaxindex < BIGINDEX) 3801302Sbill pp->p_index = ++pmaxindex; 3811302Sbill else { 3821302Sbill struct process *np; 3831302Sbill 3841302Sbill for (i = 1; ; i++) { 3851302Sbill for (np = proclist.p_next; np; np = np->p_next) 3861302Sbill if (np->p_index == i) 3871302Sbill goto tryagain; 3882357Swnj pp->p_index = i; 3892357Swnj if (i > pmaxindex) 3902357Swnj pmaxindex = i; 3911302Sbill break; 3921302Sbill tryagain:; 3931302Sbill } 3941302Sbill } 3951302Sbill if (pcurrent == PNULL) 3961302Sbill pcurrent = pp; 3971302Sbill else if (pprevious == PNULL) 3981302Sbill pprevious = pp; 3991302Sbill } 4001302Sbill pp->p_next = proclist.p_next; 4011302Sbill proclist.p_next = pp; 40217509Sedward (void) gettimeofday(&pp->p_btime, (struct timezone *)0); 4031302Sbill } 4041302Sbill 4051302Sbill padd(t) 4061302Sbill register struct command *t; 4071302Sbill { 4081302Sbill char **argp; 4091302Sbill 4101302Sbill if (t == 0) 4111302Sbill return; 4121302Sbill switch (t->t_dtyp) { 4131302Sbill 41447724Sbostic case NODE_PAREN: 4151302Sbill pads("( "); 4161302Sbill padd(t->t_dspr); 4171302Sbill pads(" )"); 4181302Sbill break; 4191302Sbill 42047724Sbostic case NODE_COMMAND: 4211302Sbill for (argp = t->t_dcom; *argp; argp++) { 4221302Sbill pads(*argp); 4231302Sbill if (argp[1]) 4241302Sbill pads(" "); 4251302Sbill } 4261302Sbill break; 4271302Sbill 42847724Sbostic case NODE_OR: 42947724Sbostic case NODE_AND: 43047724Sbostic case NODE_PIPE: 43147724Sbostic case NODE_LIST: 4321302Sbill padd(t->t_dcar); 43317903Sedward switch (t->t_dtyp) { 43447724Sbostic case NODE_OR: 43517903Sedward pads(" || "); 43617903Sedward break; 43747724Sbostic case NODE_AND: 43817903Sedward pads(" && "); 43917903Sedward break; 44047724Sbostic case NODE_PIPE: 44117903Sedward pads(" | "); 44217903Sedward break; 44347724Sbostic case NODE_LIST: 44417903Sedward pads("; "); 44517903Sedward break; 44617903Sedward } 4471302Sbill padd(t->t_dcdr); 4481302Sbill return; 4491302Sbill } 45047724Sbostic if ((t->t_dflg & F_PIPEIN) == 0 && t->t_dlef) { 45147724Sbostic pads((t->t_dflg & F_READ) ? " << " : " < "); 4521302Sbill pads(t->t_dlef); 4531302Sbill } 45447724Sbostic if ((t->t_dflg & F_PIPEOUT) == 0 && t->t_drit) { 45547724Sbostic pads((t->t_dflg & F_APPEND) ? " >>" : " >"); 45647724Sbostic if (t->t_dflg & F_STDERR) 4571302Sbill pads("&"); 4581302Sbill pads(" "); 4591302Sbill pads(t->t_drit); 4601302Sbill } 4611302Sbill } 4621302Sbill 4631302Sbill pads(cp) 4641302Sbill char *cp; 4651302Sbill { 4661302Sbill register int i = strlen(cp); 4671302Sbill 4681302Sbill if (cmdlen >= PMAXLEN) 4691302Sbill return; 4701302Sbill if (cmdlen + i >= PMAXLEN) { 47117509Sedward (void) strcpy(cmdp, " ..."); 4721302Sbill cmdlen = PMAXLEN; 4731302Sbill cmdp += 4; 4741302Sbill return; 4751302Sbill } 47617509Sedward (void) strcpy(cmdp, cp); 4771302Sbill cmdp += i; 4781302Sbill cmdlen += i; 4791302Sbill } 4801302Sbill 4811302Sbill /* 4821302Sbill * psavejob - temporarily save the current job on a one level stack 4831302Sbill * so another job can be created. Used for { } in exp6 4841302Sbill * and `` in globbing. 4851302Sbill */ 4861302Sbill psavejob() 4871302Sbill { 4881302Sbill 4891302Sbill pholdjob = pcurrjob; 4901302Sbill pcurrjob = PNULL; 4911302Sbill } 4921302Sbill 4931302Sbill /* 4941302Sbill * prestjob - opposite of psavejob. This may be missed if we are interrupted 4951302Sbill * somewhere, but pendjob cleans up anyway. 4961302Sbill */ 4971302Sbill prestjob() 4981302Sbill { 4991302Sbill 5001302Sbill pcurrjob = pholdjob; 5011302Sbill pholdjob = PNULL; 5021302Sbill } 5031302Sbill 5041302Sbill /* 5051302Sbill * pendjob - indicate that a job (set of commands) has been completed 5061302Sbill * or is about to begin. 5071302Sbill */ 5081302Sbill pendjob() 5091302Sbill { 5101302Sbill register struct process *pp, *tp; 5111302Sbill 5121302Sbill if (pcurrjob && (pcurrjob->p_flags&(PFOREGND|PSTOPPED)) == 0) { 5131302Sbill pp = pcurrjob; 5141302Sbill while (pp->p_pid != pp->p_jobid) 5151302Sbill pp = pp->p_friends; 5161302Sbill printf("[%d]", pp->p_index); 5171302Sbill tp = pp; 5181302Sbill do { 5191302Sbill printf(" %d", pp->p_pid); 5201302Sbill pp = pp->p_friends; 5211302Sbill } while (pp != tp); 5221302Sbill printf("\n"); 5231302Sbill } 5241302Sbill pholdjob = pcurrjob = 0; 5251302Sbill } 5261302Sbill 5271302Sbill /* 5281302Sbill * pprint - print a job 5291302Sbill */ 5301302Sbill pprint(pp, flag) 5311302Sbill register struct process *pp; 5321302Sbill { 5331302Sbill register status, reason; 5341302Sbill struct process *tp; 5351302Sbill extern char *linp, linbuf[]; 5361302Sbill int jobflags, pstatus; 5371302Sbill char *format; 5381302Sbill 5391302Sbill while (pp->p_pid != pp->p_jobid) 5401302Sbill pp = pp->p_friends; 5411302Sbill if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { 5421302Sbill pp->p_flags &= ~PPTIME; 5431302Sbill pp->p_flags |= PTIME; 5441302Sbill } 5451302Sbill tp = pp; 5461302Sbill status = reason = -1; 5471302Sbill jobflags = 0; 5481302Sbill do { 5491302Sbill jobflags |= pp->p_flags; 5501302Sbill pstatus = pp->p_flags & PALLSTATES; 5511302Sbill if (tp != pp && linp != linbuf && !(flag&FANCY) && 5521302Sbill (pstatus == status && pp->p_reason == reason || 5531302Sbill !(flag&REASON))) 5541302Sbill printf(" "); 5551302Sbill else { 5561302Sbill if (tp != pp && linp != linbuf) 5571302Sbill printf("\n"); 5581302Sbill if(flag&NUMBER) 5591302Sbill if (pp == tp) 5601302Sbill printf("[%d]%s %c ", pp->p_index, 5611302Sbill pp->p_index < 10 ? " " : "", 5621302Sbill pp==pcurrent ? '+' : 5631302Sbill (pp == pprevious ? '-' : ' ')); 5641302Sbill else 5651302Sbill printf(" "); 5661302Sbill if (flag&FANCY) 5671302Sbill printf("%5d ", pp->p_pid); 5681302Sbill if (flag&(REASON|AREASON)) { 5691302Sbill if (flag&NAME) 57032205Sbostic format = "%-23s"; 5711302Sbill else 5721302Sbill format = "%s"; 5731302Sbill if (pstatus == status) 5741302Sbill if (pp->p_reason == reason) { 5751302Sbill printf(format, ""); 5761302Sbill goto prcomd; 5771302Sbill } else 5781302Sbill reason = pp->p_reason; 5791302Sbill else { 5801302Sbill status = pstatus; 5811302Sbill reason = pp->p_reason; 5821302Sbill } 5831302Sbill switch (status) { 5841302Sbill 5851302Sbill case PRUNNING: 5861302Sbill printf(format, "Running "); 5871302Sbill break; 5881302Sbill 5891302Sbill case PINTERRUPTED: 5901302Sbill case PSTOPPED: 5911302Sbill case PSIGNALED: 59228054Slepreau if ((flag&(REASON|AREASON)) 59328054Slepreau && reason != SIGINT 59428054Slepreau && reason != SIGPIPE) 5951302Sbill printf(format, mesg[pp->p_reason].pname); 5961302Sbill break; 5971302Sbill 5981302Sbill case PNEXITED: 5991302Sbill case PAEXITED: 6001302Sbill if (flag & REASON) 6011302Sbill if (pp->p_reason) 6021302Sbill printf("Exit %-16d", pp->p_reason); 6031302Sbill else 6041302Sbill printf(format, "Done"); 6051302Sbill break; 6061302Sbill 6071302Sbill default: 6081302Sbill printf("BUG: status=%-9o", status); 6091302Sbill } 6101302Sbill } 6111302Sbill } 6121302Sbill prcomd: 6131302Sbill if (flag&NAME) { 6141302Sbill printf("%s", pp->p_command); 6151302Sbill if (pp->p_flags & PPOU) 6161302Sbill printf(" |"); 6171302Sbill if (pp->p_flags & PDIAG) 6181302Sbill printf("&"); 6191302Sbill } 6201302Sbill if (flag&(REASON|AREASON) && pp->p_flags&PDUMPED) 6211302Sbill printf(" (core dumped)"); 6221302Sbill if (tp == pp->p_friends) { 6231302Sbill if (flag&ERSAND) 6241302Sbill printf(" &"); 6251302Sbill if (flag&JOBDIR && 6261302Sbill !eq(tp->p_cwd->di_name, dcwd->di_name)) { 6271302Sbill printf(" (wd: "); 6281302Sbill dtildepr(value("home"), tp->p_cwd->di_name); 6291302Sbill printf(")"); 6301302Sbill } 6311302Sbill } 6321302Sbill if (pp->p_flags&PPTIME && !(status&(PSTOPPED|PRUNNING))) { 6331302Sbill if (linp != linbuf) 6341302Sbill printf("\n\t"); 63510035Ssam { static struct rusage zru; 63610704Ssam prusage(&zru, &pp->p_rusage, &pp->p_etime, 63710704Ssam &pp->p_btime); 63810035Ssam } 6391302Sbill } 6401302Sbill if (tp == pp->p_friends) { 6411302Sbill if (linp != linbuf) 6421302Sbill printf("\n"); 6431302Sbill if (flag&SHELLDIR && !eq(tp->p_cwd->di_name, dcwd->di_name)) { 6441302Sbill printf("(wd now: "); 6451302Sbill dtildepr(value("home"), dcwd->di_name); 6461302Sbill printf(")\n"); 6471302Sbill } 6481302Sbill } 6491302Sbill } while ((pp = pp->p_friends) != tp); 6501302Sbill if (jobflags&PTIME && (jobflags&(PSTOPPED|PRUNNING)) == 0) { 6511302Sbill if (jobflags & NUMBER) 6521302Sbill printf(" "); 6531302Sbill ptprint(tp); 6541302Sbill } 6551302Sbill return (jobflags); 6561302Sbill } 6571302Sbill 6581302Sbill ptprint(tp) 6591302Sbill register struct process *tp; 6601302Sbill { 66110704Ssam struct timeval tetime, diff; 66210704Ssam static struct timeval ztime; 66310035Ssam struct rusage ru; 66410035Ssam static struct rusage zru; 6651302Sbill register struct process *pp = tp; 6661302Sbill 66710035Ssam ru = zru; 66810704Ssam tetime = ztime; 6691302Sbill do { 67010035Ssam ruadd(&ru, &pp->p_rusage); 67110704Ssam tvsub(&diff, &pp->p_etime, &pp->p_btime); 67210704Ssam if (timercmp(&diff, &tetime, >)) 67310704Ssam tetime = diff; 6741302Sbill } while ((pp = pp->p_friends) != tp); 67510704Ssam prusage(&zru, &ru, &tetime, &ztime); 6761302Sbill } 6771302Sbill 6781302Sbill /* 6791302Sbill * dojobs - print all jobs 6801302Sbill */ 6811302Sbill dojobs(v) 6821302Sbill char **v; 6831302Sbill { 6841302Sbill register struct process *pp; 6851302Sbill register int flag = NUMBER|NAME|REASON; 6861302Sbill int i; 6871302Sbill 6881302Sbill if (chkstop) 6891302Sbill chkstop = 2; 6901302Sbill if (*++v) { 6911302Sbill if (v[1] || !eq(*v, "-l")) 6921302Sbill error("Usage: jobs [ -l ]"); 6931302Sbill flag |= FANCY|JOBDIR; 6941302Sbill } 6951302Sbill for (i = 1; i <= pmaxindex; i++) 6961302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 6971302Sbill if (pp->p_index == i && pp->p_pid == pp->p_jobid) { 6981302Sbill pp->p_flags &= ~PNEEDNOTE; 6991302Sbill if (!(pprint(pp, flag) & (PRUNNING|PSTOPPED))) 7001302Sbill pflush(pp); 7011302Sbill break; 7021302Sbill } 7031302Sbill } 7041302Sbill 7051302Sbill /* 7061302Sbill * dofg - builtin - put the job into the foreground 7071302Sbill */ 7081302Sbill dofg(v) 7091302Sbill char **v; 7101302Sbill { 7111302Sbill register struct process *pp; 7121302Sbill 7131302Sbill okpcntl(); 7141302Sbill ++v; 7151302Sbill do { 7161302Sbill pp = pfind(*v); 7171302Sbill pstart(pp, 1); 7181302Sbill pjwait(pp); 7191302Sbill } while (*v && *++v); 7201302Sbill } 7211302Sbill 7221302Sbill /* 7231302Sbill * %... - builtin - put the job into the foreground 7241302Sbill */ 7251302Sbill dofg1(v) 7261302Sbill char **v; 7271302Sbill { 7281302Sbill register struct process *pp; 7291302Sbill 7301302Sbill okpcntl(); 7311302Sbill pp = pfind(v[0]); 7321302Sbill pstart(pp, 1); 7331302Sbill pjwait(pp); 7341302Sbill } 7351302Sbill 7361302Sbill /* 7371302Sbill * dobg - builtin - put the job into the background 7381302Sbill */ 7391302Sbill dobg(v) 7401302Sbill char **v; 7411302Sbill { 7421302Sbill register struct process *pp; 7431302Sbill 7441302Sbill okpcntl(); 7451302Sbill ++v; 7461302Sbill do { 7471302Sbill pp = pfind(*v); 7481302Sbill pstart(pp, 0); 7491302Sbill } while (*v && *++v); 7501302Sbill } 7511302Sbill 7521302Sbill /* 7531302Sbill * %... & - builtin - put the job into the background 7541302Sbill */ 7551302Sbill dobg1(v) 7561302Sbill char **v; 7571302Sbill { 7581302Sbill register struct process *pp; 7591302Sbill 7601302Sbill pp = pfind(v[0]); 7611302Sbill pstart(pp, 0); 7621302Sbill } 7631302Sbill 7641302Sbill /* 7651302Sbill * dostop - builtin - stop the job 7661302Sbill */ 7671302Sbill dostop(v) 7681302Sbill char **v; 7691302Sbill { 7701302Sbill 7711302Sbill pkill(++v, SIGSTOP); 7721302Sbill } 7731302Sbill 7741302Sbill /* 7751302Sbill * dokill - builtin - superset of kill (1) 7761302Sbill */ 7771302Sbill dokill(v) 7781302Sbill char **v; 7791302Sbill { 7801302Sbill register int signum; 7811302Sbill register char *name; 7821302Sbill 7831302Sbill v++; 7841302Sbill if (v[0] && v[0][0] == '-') { 7851302Sbill if (v[0][1] == 'l') { 7861302Sbill for (signum = 1; signum <= NSIG; signum++) { 7871302Sbill if (name = mesg[signum].iname) 7881302Sbill printf("%s ", name); 7891302Sbill if (signum == 16) 79034340Sbostic cshputchar('\n'); 7911302Sbill } 79234340Sbostic cshputchar('\n'); 7931302Sbill return; 7941302Sbill } 7951302Sbill if (digit(v[0][1])) { 7961302Sbill signum = atoi(v[0]+1); 79728054Slepreau if (signum < 0 || signum > NSIG) 7981302Sbill bferr("Bad signal number"); 7991302Sbill } else { 8001302Sbill name = &v[0][1]; 8011302Sbill for (signum = 1; signum <= NSIG; signum++) 8021302Sbill if (mesg[signum].iname && 8031302Sbill eq(name, mesg[signum].iname)) 8041302Sbill goto gotsig; 8051302Sbill setname(name); 8061302Sbill bferr("Unknown signal; kill -l lists signals"); 8071302Sbill } 8081302Sbill gotsig: 8091302Sbill v++; 8101302Sbill } else 8111302Sbill signum = SIGTERM; 8121302Sbill pkill(v, signum); 8131302Sbill } 8141302Sbill 8151302Sbill pkill(v, signum) 8161302Sbill char **v; 8171302Sbill int signum; 8181302Sbill { 8191302Sbill register struct process *pp, *np; 8201302Sbill register int jobflags = 0; 82131685Sbostic int pid, err = 0; 82231685Sbostic long omask; 82313561Ssam char *cp; 8241302Sbill 82517136Sralph omask = sigmask(SIGCHLD); 8261302Sbill if (setintr) 82717136Sralph omask |= sigmask(SIGINT); 82817136Sralph omask = sigblock(omask) & ~omask; 8291302Sbill while (*v) { 83013561Ssam cp = globone(*v); 83113561Ssam if (*cp == '%') { 83213561Ssam np = pp = pfind(cp); 8331302Sbill do 8341302Sbill jobflags |= np->p_flags; 8351302Sbill while ((np = np->p_friends) != pp); 8361302Sbill switch (signum) { 8371302Sbill 8381302Sbill case SIGSTOP: 8391302Sbill case SIGTSTP: 8401302Sbill case SIGTTIN: 8411302Sbill case SIGTTOU: 8421302Sbill if ((jobflags & PRUNNING) == 0) { 84332205Sbostic printf("%s: Already suspended\n", cp); 8441302Sbill err++; 8451302Sbill goto cont; 8461302Sbill } 8471302Sbill } 84818424Smckusick if (killpg(pp->p_jobid, signum) < 0) { 84944759Smarc printf("%s: %s\n", cp, strerror(errno)); 85018424Smckusick err++; 85118424Smckusick } 8521302Sbill if (signum == SIGTERM || signum == SIGHUP) 85317509Sedward (void) killpg(pp->p_jobid, SIGCONT); 85428054Slepreau } else if (!(digit(*cp) || *cp == '-')) 8551302Sbill bferr("Arguments should be jobs or process id's"); 8561302Sbill else { 85713561Ssam pid = atoi(cp); 8581302Sbill if (kill(pid, signum) < 0) { 85944759Smarc printf("%d: %s\n", pid, strerror(errno)); 8601302Sbill err++; 8611302Sbill goto cont; 8621302Sbill } 8631302Sbill if (signum == SIGTERM || signum == SIGHUP) 86417509Sedward (void) kill(pid, SIGCONT); 8651302Sbill } 8661302Sbill cont: 86713561Ssam xfree(cp); 8681302Sbill v++; 8691302Sbill } 87017509Sedward (void) sigsetmask(omask); 8711302Sbill if (err) 8721302Sbill error(NOSTR); 8731302Sbill } 8741302Sbill 8751302Sbill /* 8761302Sbill * pstart - start the job in foreground/background 8771302Sbill */ 8781302Sbill pstart(pp, foregnd) 8791302Sbill register struct process *pp; 8801302Sbill int foregnd; 8811302Sbill { 8821302Sbill register struct process *np; 88331685Sbostic int jobflags = 0; 88431685Sbostic long omask; 8851302Sbill 88617136Sralph omask = sigblock(sigmask(SIGCHLD)); 8871302Sbill np = pp; 8881302Sbill do { 8891302Sbill jobflags |= np->p_flags; 8901302Sbill if (np->p_flags&(PRUNNING|PSTOPPED)) { 8911302Sbill np->p_flags |= PRUNNING; 8921302Sbill np->p_flags &= ~PSTOPPED; 8931302Sbill if (foregnd) 8941302Sbill np->p_flags |= PFOREGND; 8951302Sbill else 8961302Sbill np->p_flags &= ~PFOREGND; 8971302Sbill } 8981302Sbill } while((np = np->p_friends) != pp); 8992357Swnj if (!foregnd) 9002357Swnj pclrcurr(pp); 90117509Sedward (void) pprint(pp, foregnd ? NAME|JOBDIR : NUMBER|NAME|AMPERSAND); 9021302Sbill if (foregnd) 90317509Sedward (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&pp->p_jobid); 9041302Sbill if (jobflags&PSTOPPED) 90517509Sedward (void) killpg(pp->p_jobid, SIGCONT); 90617509Sedward (void) sigsetmask(omask); 9071302Sbill } 9081302Sbill 9091302Sbill panystop(neednl) 9101302Sbill { 9111302Sbill register struct process *pp; 9121302Sbill 9131302Sbill chkstop = 2; 9141302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 9151302Sbill if (pp->p_flags & PSTOPPED) 91632205Sbostic error("\nThere are suspended jobs" + 1 - neednl); 9171302Sbill } 9181302Sbill 9191302Sbill struct process * 9201302Sbill pfind(cp) 9211302Sbill char *cp; 9221302Sbill { 9231302Sbill register struct process *pp, *np; 9241302Sbill 9251302Sbill if (cp == 0 || cp[1] == 0 || eq(cp, "%%") || eq(cp, "%+")) { 9261302Sbill if (pcurrent == PNULL) 9271302Sbill bferr("No current job"); 9281302Sbill return (pcurrent); 9291302Sbill } 9301302Sbill if (eq(cp, "%-") || eq(cp, "%#")) { 9311302Sbill if (pprevious == PNULL) 9321302Sbill bferr("No previous job"); 9331302Sbill return (pprevious); 9341302Sbill } 9351302Sbill if (digit(cp[1])) { 9361302Sbill int index = atoi(cp+1); 9371302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 9381302Sbill if (pp->p_index == index && pp->p_pid == pp->p_jobid) 9391302Sbill return (pp); 9401302Sbill bferr("No such job"); 9411302Sbill } 9421302Sbill np = PNULL; 9431302Sbill for (pp = proclist.p_next; pp; pp = pp->p_next) 9441302Sbill if (pp->p_pid == pp->p_jobid) { 9451302Sbill if (cp[1] == '?') { 9461302Sbill register char *dp; 9471302Sbill for (dp = pp->p_command; *dp; dp++) { 9481302Sbill if (*dp != cp[2]) 9491302Sbill continue; 9501302Sbill if (prefix(cp+2, dp)) 9511302Sbill goto match; 9521302Sbill } 9531302Sbill } else if (prefix(cp+1, pp->p_command)) { 9541302Sbill match: 9551302Sbill if (np) 9561302Sbill bferr("Ambiguous"); 9571302Sbill np = pp; 9581302Sbill } 9591302Sbill } 9601302Sbill if (np) 9611302Sbill return (np); 9621302Sbill if (cp[1] == '?') 9631302Sbill bferr("No job matches pattern"); 9641302Sbill else 9651302Sbill bferr("No such job"); 96617509Sedward /*NOTREACHED*/ 9671302Sbill } 9681302Sbill 9691302Sbill /* 9702357Swnj * pgetcurr - find most recent job that is not pp, preferably stopped 9711302Sbill */ 9721302Sbill struct process * 9731302Sbill pgetcurr(pp) 9741302Sbill register struct process *pp; 9751302Sbill { 9761302Sbill register struct process *np; 9772357Swnj register struct process *xp = PNULL; 9781302Sbill 9791302Sbill for (np = proclist.p_next; np; np = np->p_next) 9801302Sbill if (np != pcurrent && np != pp && np->p_pid && 9811302Sbill np->p_pid == np->p_jobid) { 9822357Swnj if (np->p_flags & PSTOPPED) 9832357Swnj return (np); 9842357Swnj if (xp == PNULL) 9852357Swnj xp = np; 9861302Sbill } 9872357Swnj return (xp); 9881302Sbill } 9891302Sbill 9901302Sbill /* 9911302Sbill * donotify - flag the job so as to report termination asynchronously 9921302Sbill */ 9931302Sbill donotify(v) 9941302Sbill char **v; 9951302Sbill { 9961302Sbill register struct process *pp; 9971302Sbill 9981302Sbill pp = pfind(*++v); 9991302Sbill pp->p_flags |= PNOTIFY; 10001302Sbill } 10011302Sbill 10021302Sbill /* 10031302Sbill * Do the fork and whatever should be done in the child side that 10041302Sbill * should not be done if we are not forking at all (like for simple builtin's) 10051302Sbill * Also do everything that needs any signals fiddled with in the parent side 10061302Sbill * 10071302Sbill * Wanttty tells whether process and/or tty pgrps are to be manipulated: 10081302Sbill * -1: leave tty alone; inherit pgrp from parent 10091302Sbill * 0: already have tty; manipulate process pgrps only 10101302Sbill * 1: want to claim tty; manipulate process and tty pgrps 10111302Sbill * It is usually just the value of tpgrp. 10121302Sbill */ 10131302Sbill pfork(t, wanttty) 10141302Sbill struct command *t; /* command we are forking for */ 10151302Sbill int wanttty; 10161302Sbill { 10171302Sbill register int pid; 10181302Sbill bool ignint = 0; 101931685Sbostic int pgrp; 102031685Sbostic long omask; 10211302Sbill 10221302Sbill /* 10231302Sbill * A child will be uninterruptible only under very special 10241302Sbill * conditions. Remember that the semantics of '&' is 10251302Sbill * implemented by disconnecting the process from the tty so 10261302Sbill * signals do not need to ignored just for '&'. 10271302Sbill * Thus signals are set to default action for children unless: 10281302Sbill * we have had an "onintr -" (then specifically ignored) 10291302Sbill * we are not playing with signals (inherit action) 10301302Sbill */ 10311302Sbill if (setintr) 103247724Sbostic ignint = (tpgrp == -1 && (t->t_dflg&F_NOINTERRUPT)) 10331302Sbill || (gointr && eq(gointr, "-")); 10341302Sbill /* 10351302Sbill * Hold SIGCHLD until we have the process installed in our table. 10361302Sbill */ 103717136Sralph omask = sigblock(sigmask(SIGCHLD)); 10381302Sbill while ((pid = fork()) < 0) 10391302Sbill if (setintr == 0) 10401302Sbill sleep(FORKSLEEP); 10411302Sbill else { 104217509Sedward (void) sigsetmask(omask); 10431302Sbill error("No more processes"); 10441302Sbill } 10451302Sbill if (pid == 0) { 10461302Sbill settimes(); 10471302Sbill pgrp = pcurrjob ? pcurrjob->p_jobid : getpid(); 10481302Sbill pflushall(); 10491302Sbill pcurrjob = PNULL; 10501302Sbill child++; 10511302Sbill if (setintr) { 10521302Sbill setintr = 0; /* until I think otherwise */ 10531302Sbill /* 10541302Sbill * Children just get blown away on SIGINT, SIGQUIT 10551302Sbill * unless "onintr -" seen. 10561302Sbill */ 105717509Sedward (void) signal(SIGINT, ignint ? SIG_IGN : SIG_DFL); 105817509Sedward (void) signal(SIGQUIT, ignint ? SIG_IGN : SIG_DFL); 10591302Sbill if (wanttty >= 0) { 10601302Sbill /* make stoppable */ 106117509Sedward (void) signal(SIGTSTP, SIG_DFL); 106217509Sedward (void) signal(SIGTTIN, SIG_DFL); 106317509Sedward (void) signal(SIGTTOU, SIG_DFL); 10641302Sbill } 106517509Sedward (void) signal(SIGTERM, parterm); 106647724Sbostic } else if (tpgrp == -1 && (t->t_dflg&F_NOINTERRUPT)) { 106717509Sedward (void) signal(SIGINT, SIG_IGN); 106817509Sedward (void) signal(SIGQUIT, SIG_IGN); 10691302Sbill } 107044759Smarc if (wanttty >= 0 && tpgrp >= 0) 107144759Smarc (void) setpgrp(0, pgrp); 10721302Sbill if (wanttty > 0) 107317509Sedward (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&pgrp); 10741302Sbill if (tpgrp > 0) 10751302Sbill tpgrp = 0; /* gave tty away */ 10761302Sbill /* 107747724Sbostic * Nohup and nice apply only to NODE_COMMAND's but it would be 107847724Sbostic * nice (?!?) if you could say "nohup (foo;bar)". Then the 107947724Sbostic * parser would have to know about nice/nohup/time. 10801302Sbill */ 108147724Sbostic if (t->t_dflg & F_NOHUP) 108217509Sedward (void) signal(SIGHUP, SIG_IGN); 108347724Sbostic if (t->t_dflg & F_NICE) 108428054Slepreau (void) setpriority(PRIO_PROCESS, 0, t->t_nice); 10851302Sbill } else { 108639545Smarc if (wanttty >= 0 && tpgrp >= 0) 108739545Smarc (void) setpgrp(pid, pcurrjob ? pcurrjob->p_jobid : pid); 10881302Sbill palloc(pid, t); 108917509Sedward (void) sigsetmask(omask); 10901302Sbill } 10911302Sbill 10921302Sbill return (pid); 10931302Sbill } 10941302Sbill 10951302Sbill okpcntl() 10961302Sbill { 10971302Sbill 10981302Sbill if (tpgrp == -1) 10991302Sbill error("No job control in this shell"); 11001302Sbill if (tpgrp == 0) 11011302Sbill error("No job control in subshells"); 11021302Sbill } 1103