sh.proc.c revision 195609
1195609Smp/* $Header: /p/tcsh/cvsroot/tcsh/sh.proc.c,v 3.109 2009/06/25 21:15:37 christos Exp $ */ 259243Sobrien/* 359243Sobrien * sh.proc.c: Job manipulations 459243Sobrien */ 559243Sobrien/*- 659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California. 759243Sobrien * All rights reserved. 859243Sobrien * 959243Sobrien * Redistribution and use in source and binary forms, with or without 1059243Sobrien * modification, are permitted provided that the following conditions 1159243Sobrien * are met: 1259243Sobrien * 1. Redistributions of source code must retain the above copyright 1359243Sobrien * notice, this list of conditions and the following disclaimer. 1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1559243Sobrien * notice, this list of conditions and the following disclaimer in the 1659243Sobrien * documentation and/or other materials provided with the distribution. 17100616Smp * 3. Neither the name of the University nor the names of its contributors 1859243Sobrien * may be used to endorse or promote products derived from this software 1959243Sobrien * without specific prior written permission. 2059243Sobrien * 2159243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2459243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3059243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3159243Sobrien * SUCH DAMAGE. 3259243Sobrien */ 3359243Sobrien#include "sh.h" 3459243Sobrien 35195609SmpRCSID("$tcsh: sh.proc.c,v 3.109 2009/06/25 21:15:37 christos Exp $") 3659243Sobrien 3759243Sobrien#include "ed.h" 3859243Sobrien#include "tc.h" 3959243Sobrien#include "tc.wait.h" 4059243Sobrien 4169408Sache#ifdef WINNT_NATIVE 4259243Sobrien#undef POSIX 4359243Sobrien#define POSIX 4469408Sache#endif /* WINNT_NATIVE */ 4559243Sobrien#ifdef aiws 4659243Sobrien# undef HZ 4759243Sobrien# define HZ 16 4859243Sobrien#endif /* aiws */ 4959243Sobrien 50145479Smp#if defined(_BSD) || (defined(IRIS4D) && __STDC__) || defined(__lucid) || defined(linux) || defined(__GNU__) || defined(__GLIBC__) 5159243Sobrien# define BSDWAIT 52145479Smp#endif /* _BSD || (IRIS4D && __STDC__) || __lucid || glibc */ 5359243Sobrien#ifndef WTERMSIG 5459243Sobrien# define WTERMSIG(w) (((union wait *) &(w))->w_termsig) 5559243Sobrien# ifndef BSDWAIT 5659243Sobrien# define BSDWAIT 5759243Sobrien# endif /* !BSDWAIT */ 5859243Sobrien#endif /* !WTERMSIG */ 5959243Sobrien#ifndef WEXITSTATUS 6059243Sobrien# define WEXITSTATUS(w) (((union wait *) &(w))->w_retcode) 6159243Sobrien#endif /* !WEXITSTATUS */ 6259243Sobrien#ifndef WSTOPSIG 6359243Sobrien# define WSTOPSIG(w) (((union wait *) &(w))->w_stopsig) 6459243Sobrien#endif /* !WSTOPSIG */ 6559243Sobrien 6659243Sobrien#ifdef __osf__ 6759243Sobrien# ifndef WCOREDUMP 6859243Sobrien# define WCOREDUMP(x) (_W_INT(x) & WCOREFLAG) 6959243Sobrien# endif 7059243Sobrien#endif 7159243Sobrien 7259243Sobrien#ifndef WCOREDUMP 7359243Sobrien# ifdef BSDWAIT 7459243Sobrien# define WCOREDUMP(w) (((union wait *) &(w))->w_coredump) 7559243Sobrien# else /* !BSDWAIT */ 7659243Sobrien# define WCOREDUMP(w) ((w) & 0200) 7759243Sobrien# endif /* !BSDWAIT */ 7859243Sobrien#endif /* !WCOREDUMP */ 7959243Sobrien 80167465Smp#ifndef JOBDEBUG 81167465Smp# define jobdebug_xprintf(x) (void)0 82167465Smp# define jobdebug_flush() (void)0 83167465Smp#else 84167465Smp# define jobdebug_xprintf(s) xprintf s 85167465Smp# define jobdebug_flush() flush() 86167465Smp#endif 87167465Smp 8859243Sobrien/* 8959243Sobrien * C Shell - functions that manage processes, handling hanging, termination 9059243Sobrien */ 9159243Sobrien 9259243Sobrien#define BIGINDEX 9 /* largest desirable job index */ 9359243Sobrien 9459243Sobrien#ifdef BSDTIMES 9559243Sobrien# ifdef convex 9659243Sobrien/* use 'cvxrusage' to get parallel statistics */ 9759243Sobrienstatic struct cvxrusage zru = {{0L, 0L}, {0L, 0L}, 0L, 0L, 0L, 0L, 9859243Sobrien 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 9959243Sobrien {0L, 0L}, 0LL, 0LL, 0LL, 0LL, 0L, 0L, 0L, 10059243Sobrien 0LL, 0LL, {0L, 0L, 0L, 0L, 0L}}; 10159243Sobrien# else 10259243Sobrienstatic struct rusage zru; 10359243Sobrien# endif /* convex */ 10459243Sobrien#else /* !BSDTIMES */ 10559243Sobrien# ifdef _SEQUENT_ 10659243Sobrienstatic struct process_stats zru = {{0L, 0L}, {0L, 0L}, 0, 0, 0, 0, 0, 0, 0, 10759243Sobrien 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 10859243Sobrien# else /* !_SEQUENT_ */ 10959243Sobrien# ifdef _SX 11059243Sobrienstatic struct tms zru = {0, 0, 0, 0}, lru = {0, 0, 0, 0}; 11159243Sobrien# else /* !_SX */ 11259243Sobrienstatic struct tms zru = {0L, 0L, 0L, 0L}, lru = {0L, 0L, 0L, 0L}; 11359243Sobrien# endif /* !_SX */ 11459243Sobrien# endif /* !_SEQUENT_ */ 11559243Sobrien#endif /* !BSDTIMES */ 11659243Sobrien 117167465Smp#ifndef BSDTIMES 118167465Smpstatic int timesdone; /* shtimes buffer full ? */ 119167465Smp#endif /* BSDTIMES */ 120167465Smp 12159243Sobrien#ifndef RUSAGE_CHILDREN 12259243Sobrien# define RUSAGE_CHILDREN -1 12359243Sobrien#endif /* RUSAGE_CHILDREN */ 12459243Sobrien 125167465Smpstatic void pflushall (void); 126167465Smpstatic void pflush (struct process *); 127167465Smpstatic void pfree (struct process *); 128167465Smpstatic void pclrcurr (struct process *); 129195609Smpstatic void morecommand (size_t); 130167465Smpstatic void padd (struct command *); 131167465Smpstatic int pprint (struct process *, int); 132167465Smpstatic void ptprint (struct process *); 133167465Smpstatic void pads (Char *); 134167465Smpstatic void pkill (Char **, int); 135167465Smpstatic struct process *pgetcurr (struct process *); 136167465Smpstatic void okpcntl (void); 137167465Smpstatic void setttypgrp (int); 13859243Sobrien 13959243Sobrien/* 140167465Smp * pchild - call queued by the SIGCHLD signal 14159243Sobrien * indicating that at least one child has terminated or stopped 14259243Sobrien * thus at least one wait system call will definitely return a 14359243Sobrien * childs status. Top level routines (like pwait) must be sure 14459243Sobrien * to mask interrupts when playing with the proclist data structures! 14559243Sobrien */ 146167465Smpvoid 147167465Smppchild(void) 14859243Sobrien{ 149145479Smp struct process *pp; 150145479Smp struct process *fp; 151167465Smp pid_t pid; 15259243Sobrien#ifdef BSDWAIT 15359243Sobrien union wait w; 15459243Sobrien#else /* !BSDWAIT */ 15559243Sobrien int w; 15659243Sobrien#endif /* !BSDWAIT */ 15759243Sobrien int jobflags; 15859243Sobrien#ifdef BSDTIMES 15959243Sobrien struct sysrusage ru; 16059243Sobrien#else /* !BSDTIMES */ 16159243Sobrien# ifdef _SEQUENT_ 16259243Sobrien struct process_stats ru; 16359243Sobrien struct process_stats cpst1, cpst2; 16459243Sobrien timeval_t tv; 16559243Sobrien# else /* !_SEQUENT_ */ 16659243Sobrien struct tms proctimes; 16759243Sobrien 16859243Sobrien if (!timesdone) { 16959243Sobrien timesdone++; 17059243Sobrien (void) times(&shtimes); 17159243Sobrien } 17259243Sobrien# endif /* !_SEQUENT_ */ 17359243Sobrien#endif /* !BSDTIMES */ 17459243Sobrien 175167465Smp jobdebug_xprintf(("pchild()\n")); 17659243Sobrien 17759243Sobrienloop: 178167465Smp jobdebug_xprintf(("Waiting...\n")); 179167465Smp jobdebug_flush(); 18059243Sobrien errno = 0; /* reset, just in case */ 181167465Smp 18269408Sache#ifndef WINNT_NATIVE 18359243Sobrien# ifdef BSDJOBS 18459243Sobrien# ifdef BSDTIMES 18559243Sobrien# ifdef convex 18659243Sobrien /* use 'cvxwait' to get parallel statistics */ 18759243Sobrien pid = cvxwait(&w, 18859243Sobrien (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG), &ru); 18959243Sobrien# else 19059243Sobrien /* both a wait3 and rusage */ 191167465Smp# if !defined(BSDWAIT) || defined(NeXT) || defined(MACH) || defined(linux) || defined(__GNU__) || defined(__GLIBC__) || (defined(IRIS4D) && SYSVREL <= 3) || defined(__lucid) || defined(__osf__) 19259243Sobrien pid = wait3(&w, 19359243Sobrien (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG), &ru); 19459243Sobrien# else /* BSDWAIT */ 19559243Sobrien pid = wait3(&w.w_status, 19659243Sobrien (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG), &ru); 19759243Sobrien# endif /* BSDWAIT */ 19859243Sobrien# endif /* convex */ 19959243Sobrien# else /* !BSDTIMES */ 20059243Sobrien# ifdef _SEQUENT_ 20159243Sobrien (void) get_process_stats(&tv, PS_SELF, 0, &cpst1); 20259243Sobrien pid = waitpid(-1, &w, 20359243Sobrien (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG)); 20459243Sobrien (void) get_process_stats(&tv, PS_SELF, 0, &cpst2); 20559243Sobrien pr_stat_sub(&cpst2, &cpst1, &ru); 20659243Sobrien# else /* !_SEQUENT_ */ 20759243Sobrien# ifndef POSIX 20859243Sobrien /* we have a wait3, but no rusage stuff */ 20959243Sobrien pid = wait3(&w.w_status, 21059243Sobrien (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG), 0); 21159243Sobrien# else /* POSIX */ 21259243Sobrien pid = waitpid(-1, &w, 21359243Sobrien (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG)); 21459243Sobrien# endif /* POSIX */ 21559243Sobrien# endif /* !_SEQUENT_ */ 21659243Sobrien# endif /* !BSDTIMES */ 21759243Sobrien# else /* !BSDJOBS */ 21859243Sobrien# ifdef BSDTIMES 21959243Sobrien# define HAVEwait3 22059243Sobrien /* both a wait3 and rusage */ 22159243Sobrien# ifdef hpux 22259243Sobrien pid = wait3(&w.w_status, WNOHANG, 0); 22359243Sobrien# else /* !hpux */ 22459243Sobrien pid = wait3(&w.w_status, WNOHANG, &ru); 22559243Sobrien# endif /* !hpux */ 22659243Sobrien# else /* !BSDTIMES */ 22759243Sobrien# ifdef ODT /* For Sco Unix 3.2.0 or ODT 1.0 */ 22859243Sobrien# define HAVEwait3 229167465Smp pid = waitpid(-1, &w, 23059243Sobrien (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG)); 23159243Sobrien# endif /* ODT */ 23259243Sobrien# if defined(aiws) || defined(uts) 23359243Sobrien# define HAVEwait3 23459243Sobrien pid = wait3(&w.w_status, 23559243Sobrien (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG), 0); 23659243Sobrien# endif /* aiws || uts */ 23759243Sobrien# ifndef HAVEwait3 238167465Smp# ifndef BSDWAIT 23959243Sobrien /* no wait3, therefore no rusage */ 24059243Sobrien /* on Sys V, this may hang. I hope it's not going to be a problem */ 241167465Smp pid = wait(&w); 242167465Smp# else /* BSDWAIT */ 24359243Sobrien /* 24459243Sobrien * XXX: for greater than 3 we should use waitpid(). 24559243Sobrien * but then again, SVR4 falls into the POSIX/BSDJOBS category. 24659243Sobrien */ 247167465Smp pid = wait(&w.w_status); 248167465Smp# endif /* BSDWAIT */ 24959243Sobrien# endif /* !HAVEwait3 */ 25059243Sobrien# endif /* !BSDTIMES */ 25159243Sobrien# endif /* !BSDJOBS */ 25269408Sache#else /* WINNT_NATIVE */ 253167465Smp pid = waitpid(-1, &w, 25459243Sobrien (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG)); 25569408Sache#endif /* WINNT_NATIVE */ 25659243Sobrien 257167465Smp jobdebug_xprintf(("parent %d pid %d, retval %x termsig %x retcode %x\n", 258167465Smp (int)getpid(), (int)pid, w, WTERMSIG(w), 259167465Smp WEXITSTATUS(w))); 260167465Smp jobdebug_flush(); 26159243Sobrien 26259243Sobrien if ((pid == 0) || (pid == -1)) { 263167465Smp handle_pending_signals(); 264167465Smp jobdebug_xprintf(("errno == %d\n", errno)); 265167465Smp if (errno == EINTR) 26659243Sobrien goto loop; 267145479Smp goto end; 26859243Sobrien } 26959243Sobrien for (pp = proclist.p_next; pp != NULL; pp = pp->p_next) 27059243Sobrien if (pid == pp->p_procid) 27159243Sobrien goto found; 27269408Sache#if !defined(BSDJOBS) && !defined(WINNT_NATIVE) 27359243Sobrien /* this should never have happened */ 27459243Sobrien stderror(ERR_SYNC, pid); 27559243Sobrien xexit(0); 27669408Sache#else /* BSDJOBS || WINNT_NATIVE */ 27759243Sobrien goto loop; 27869408Sache#endif /* !BSDJOBS && !WINNT_NATIVE */ 27959243Sobrienfound: 28059243Sobrien pp->p_flags &= ~(PRUNNING | PSTOPPED | PREPORTED); 28159243Sobrien if (WIFSTOPPED(w)) { 28259243Sobrien pp->p_flags |= PSTOPPED; 28359243Sobrien pp->p_reason = WSTOPSIG(w); 28459243Sobrien } 28559243Sobrien else { 28659243Sobrien if (pp->p_flags & (PTIME | PPTIME) || adrof(STRtime)) 28759243Sobrien#ifndef BSDTIMES 28859243Sobrien# ifdef _SEQUENT_ 28959243Sobrien (void) get_process_stats(&pp->p_etime, PS_SELF, NULL, NULL); 29059243Sobrien# else /* !_SEQUENT_ */ 29159243Sobrien pp->p_etime = times(&proctimes); 29259243Sobrien# endif /* !_SEQUENT_ */ 29359243Sobrien#else /* BSDTIMES */ 29459243Sobrien (void) gettimeofday(&pp->p_etime, NULL); 29559243Sobrien#endif /* BSDTIMES */ 29659243Sobrien 29759243Sobrien 29859243Sobrien#if defined(BSDTIMES) || defined(_SEQUENT_) 29959243Sobrien pp->p_rusage = ru; 30059243Sobrien#else /* !BSDTIMES && !_SEQUENT_ */ 30159243Sobrien (void) times(&proctimes); 30259243Sobrien pp->p_utime = proctimes.tms_cutime - shtimes.tms_cutime; 30359243Sobrien pp->p_stime = proctimes.tms_cstime - shtimes.tms_cstime; 30459243Sobrien shtimes = proctimes; 30559243Sobrien#endif /* !BSDTIMES && !_SEQUENT_ */ 30659243Sobrien if (WIFSIGNALED(w)) { 30759243Sobrien if (WTERMSIG(w) == SIGINT) 30859243Sobrien pp->p_flags |= PINTERRUPTED; 30959243Sobrien else 31059243Sobrien pp->p_flags |= PSIGNALED; 31159243Sobrien if (WCOREDUMP(w)) 31259243Sobrien pp->p_flags |= PDUMPED; 31359243Sobrien pp->p_reason = WTERMSIG(w); 31459243Sobrien } 31559243Sobrien else { 31659243Sobrien pp->p_reason = WEXITSTATUS(w); 31759243Sobrien if (pp->p_reason != 0) 31859243Sobrien pp->p_flags |= PAEXITED; 31959243Sobrien else 32059243Sobrien pp->p_flags |= PNEXITED; 32159243Sobrien } 32259243Sobrien } 32359243Sobrien jobflags = 0; 32459243Sobrien fp = pp; 32559243Sobrien do { 32659243Sobrien if ((fp->p_flags & (PPTIME | PRUNNING | PSTOPPED)) == 0 && 32759243Sobrien !child && adrof(STRtime) && 32859243Sobrien#ifdef BSDTIMES 32959243Sobrien fp->p_rusage.ru_utime.tv_sec + fp->p_rusage.ru_stime.tv_sec 33059243Sobrien#else /* !BSDTIMES */ 33159243Sobrien# ifdef _SEQUENT_ 33259243Sobrien fp->p_rusage.ps_utime.tv_sec + fp->p_rusage.ps_stime.tv_sec 33359243Sobrien# else /* !_SEQUENT_ */ 33459243Sobrien# ifndef POSIX 33559243Sobrien (fp->p_utime + fp->p_stime) / HZ 33659243Sobrien# else /* POSIX */ 33759243Sobrien (fp->p_utime + fp->p_stime) / clk_tck 33859243Sobrien# endif /* POSIX */ 33959243Sobrien# endif /* !_SEQUENT_ */ 34059243Sobrien#endif /* !BSDTIMES */ 34159243Sobrien >= atoi(short2str(varval(STRtime)))) 34259243Sobrien fp->p_flags |= PTIME; 34359243Sobrien jobflags |= fp->p_flags; 34459243Sobrien } while ((fp = fp->p_friends) != pp); 34559243Sobrien pp->p_flags &= ~PFOREGND; 34659243Sobrien if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { 34759243Sobrien pp->p_flags &= ~PPTIME; 34859243Sobrien pp->p_flags |= PTIME; 34959243Sobrien } 35059243Sobrien if ((jobflags & (PRUNNING | PREPORTED)) == 0) { 35159243Sobrien fp = pp; 35259243Sobrien do { 35359243Sobrien if (fp->p_flags & PSTOPPED) 35459243Sobrien fp->p_flags |= PREPORTED; 35559243Sobrien } while ((fp = fp->p_friends) != pp); 35659243Sobrien while (fp->p_procid != fp->p_jobid) 35759243Sobrien fp = fp->p_friends; 35859243Sobrien if (jobflags & PSTOPPED) { 35959243Sobrien if (pcurrent && pcurrent != fp) 36059243Sobrien pprevious = pcurrent; 36159243Sobrien pcurrent = fp; 36259243Sobrien } 36359243Sobrien else 36459243Sobrien pclrcurr(fp); 36559243Sobrien if (jobflags & PFOREGND) { 36659243Sobrien if (!(jobflags & (PSIGNALED | PSTOPPED | PPTIME) || 36759243Sobrien#ifdef notdef 36859243Sobrien jobflags & PAEXITED || 36959243Sobrien#endif /* notdef */ 37059243Sobrien !eq(dcwd->di_name, fp->p_cwd->di_name))) { 37159243Sobrien /* PWP: print a newline after ^C */ 37259243Sobrien if (jobflags & PINTERRUPTED) { 373167465Smp xputchar('\r' | QUOTE); 374167465Smp xputchar('\n'); 37559243Sobrien } 37659243Sobrien#ifdef notdef 37759243Sobrien else if ((jobflags & (PTIME|PSTOPPED)) == PTIME) 37859243Sobrien ptprint(fp); 37959243Sobrien#endif /* notdef */ 38059243Sobrien } 38159243Sobrien } 38259243Sobrien else { 38359243Sobrien if (jobflags & PNOTIFY || adrof(STRnotify)) { 384167465Smp xputchar('\r' | QUOTE); 385167465Smp xputchar('\n'); 38659243Sobrien (void) pprint(pp, NUMBER | NAME | REASON); 38759243Sobrien if ((jobflags & PSTOPPED) == 0) 38859243Sobrien pflush(pp); 389167465Smp if (GettingInput) { 390167465Smp errno = 0; 391167465Smp (void) Rawmode(); 39259243Sobrien#ifdef notdef 393167465Smp /* 394167465Smp * don't really want to do that, because it 395167465Smp * will erase our message in case of multi-line 396167465Smp * input 397167465Smp */ 398167465Smp ClearLines(); 39959243Sobrien#endif /* notdef */ 400167465Smp ClearDisp(); 401167465Smp Refresh(); 40259243Sobrien } 40359243Sobrien } 40459243Sobrien else { 40559243Sobrien fp->p_flags |= PNEEDNOTE; 406167465Smp neednote = 1; 40759243Sobrien } 40859243Sobrien } 40959243Sobrien } 410167465Smp#if defined(BSDJOBS) || defined(HAVEwait3) ||defined(WINNT_NATIVE) 41159243Sobrien goto loop; 41259243Sobrien#endif /* BSDJOBS || HAVEwait3 */ 413145479Smp end: 414145479Smp ; 41559243Sobrien} 41659243Sobrien 41759243Sobrienvoid 418167465Smppnote(void) 41959243Sobrien{ 420145479Smp struct process *pp; 42159243Sobrien int flags; 42259243Sobrien 42359243Sobrien neednote = 0; 42459243Sobrien for (pp = proclist.p_next; pp != NULL; pp = pp->p_next) { 42559243Sobrien if (pp->p_flags & PNEEDNOTE) { 426167465Smp pchild_disabled++; 427167465Smp cleanup_push(&pchild_disabled, disabled_cleanup); 42859243Sobrien pp->p_flags &= ~PNEEDNOTE; 42959243Sobrien flags = pprint(pp, NUMBER | NAME | REASON); 43059243Sobrien if ((flags & (PRUNNING | PSTOPPED)) == 0) 43159243Sobrien pflush(pp); 432167465Smp cleanup_until(&pchild_disabled); 43359243Sobrien } 43459243Sobrien } 43559243Sobrien} 43659243Sobrien 43759243Sobrien 43859243Sobrienstatic void 439167465Smppfree(struct process *pp) 44059243Sobrien{ 441167465Smp xfree(pp->p_command); 44259243Sobrien if (pp->p_cwd && --pp->p_cwd->di_count == 0) 44359243Sobrien if (pp->p_cwd->di_next == 0) 44459243Sobrien dfree(pp->p_cwd); 445167465Smp xfree(pp); 44659243Sobrien} 44759243Sobrien 44859243Sobrien 44959243Sobrien/* 45059243Sobrien * pwait - wait for current job to terminate, maintaining integrity 45159243Sobrien * of current and previous job indicators. 45259243Sobrien */ 45359243Sobrienvoid 454167465Smppwait(void) 45559243Sobrien{ 456145479Smp struct process *fp, *pp; 45759243Sobrien 45859243Sobrien /* 45959243Sobrien * Here's where dead procs get flushed. 46059243Sobrien */ 46159243Sobrien for (pp = (fp = &proclist)->p_next; pp != NULL; pp = (fp = pp)->p_next) 46259243Sobrien if (pp->p_procid == 0) { 46359243Sobrien fp->p_next = pp->p_next; 46459243Sobrien pfree(pp); 46559243Sobrien pp = fp; 46659243Sobrien } 46759243Sobrien pjwait(pcurrjob); 46859243Sobrien} 46959243Sobrien 47059243Sobrien 47159243Sobrien/* 47259243Sobrien * pjwait - wait for a job to finish or become stopped 47359243Sobrien * It is assumed to be in the foreground state (PFOREGND) 47459243Sobrien */ 47559243Sobrienvoid 476167465Smppjwait(struct process *pp) 47759243Sobrien{ 478145479Smp struct process *fp; 47959243Sobrien int jobflags, reason; 480167465Smp sigset_t oset, set, pause_mask; 481167465Smp Char *reason_str; 482167465Smp 48359243Sobrien while (pp->p_procid != pp->p_jobid) 48459243Sobrien pp = pp->p_friends; 48559243Sobrien fp = pp; 48659243Sobrien 48759243Sobrien do { 48859243Sobrien if ((fp->p_flags & (PFOREGND | PRUNNING)) == PRUNNING) 489195609Smp xprintf("%s", CGETS(17, 1, "BUG: waiting for background job!\n")); 49059243Sobrien } while ((fp = fp->p_friends) != pp); 49159243Sobrien /* 49259243Sobrien * Now keep pausing as long as we are not interrupted (SIGINT), and the 49359243Sobrien * target process, or any of its friends, are running 49459243Sobrien */ 49559243Sobrien fp = pp; 496167465Smp sigemptyset(&set); 497167465Smp sigaddset(&set, SIGINT); 498167465Smp sigaddset(&set, SIGCHLD); 499167465Smp (void)sigprocmask(SIG_BLOCK, &set, &oset); 500167465Smp cleanup_push(&oset, sigprocmask_cleanup); 501167465Smp pause_mask = oset; 502167465Smp sigdelset(&pause_mask, SIGCHLD); 50359243Sobrien for (;;) { 504167465Smp handle_pending_signals(); 50559243Sobrien jobflags = 0; 50659243Sobrien do 50759243Sobrien jobflags |= fp->p_flags; 50859243Sobrien while ((fp = (fp->p_friends)) != pp); 50959243Sobrien if ((jobflags & PRUNNING) == 0) 51059243Sobrien break; 511167465Smp jobdebug_xprintf(("%d starting to sigsuspend for SIGCHLD on %d\n", 512167465Smp getpid(), fp->p_procid)); 513167465Smp sigsuspend(&pause_mask); 51459243Sobrien } 515167465Smp cleanup_until(&oset); 516167465Smp jobdebug_xprintf(("%d returned from sigsuspend loop\n", getpid())); 51759243Sobrien#ifdef BSDJOBS 51859243Sobrien if (tpgrp > 0) /* get tty back */ 51959243Sobrien (void) tcsetpgrp(FSHTTY, tpgrp); 52059243Sobrien#endif /* BSDJOBS */ 52159243Sobrien if ((jobflags & (PSIGNALED | PSTOPPED | PTIME)) || 522195609Smp fp->p_cwd == NULL || !eq(dcwd->di_name, fp->p_cwd->di_name)) { 52359243Sobrien if (jobflags & PSTOPPED) { 52459243Sobrien xputchar('\n'); 52559243Sobrien if (adrof(STRlistjobs)) { 52659243Sobrien Char *jobcommand[3]; 52759243Sobrien 52859243Sobrien jobcommand[0] = STRjobs; 52959243Sobrien if (eq(varval(STRlistjobs), STRlong)) 53059243Sobrien jobcommand[1] = STRml; 53159243Sobrien else 53259243Sobrien jobcommand[1] = NULL; 53359243Sobrien jobcommand[2] = NULL; 53459243Sobrien 53559243Sobrien dojobs(jobcommand, NULL); 53659243Sobrien (void) pprint(pp, SHELLDIR); 53759243Sobrien } 53859243Sobrien else 53959243Sobrien (void) pprint(pp, AREASON | SHELLDIR); 54059243Sobrien } 54159243Sobrien else 54259243Sobrien (void) pprint(pp, AREASON | SHELLDIR); 54359243Sobrien } 54459243Sobrien if ((jobflags & (PINTERRUPTED | PSTOPPED)) && setintr && 54559243Sobrien (!gointr || !eq(gointr, STRminus))) { 54659243Sobrien if ((jobflags & PSTOPPED) == 0) 54759243Sobrien pflush(pp); 54859243Sobrien pintr1(0); 54959243Sobrien /* NOTREACHED */ 55059243Sobrien } 55159243Sobrien reason = 0; 55259243Sobrien fp = pp; 55359243Sobrien do { 55459243Sobrien if (fp->p_reason) 55559243Sobrien reason = fp->p_flags & (PSIGNALED | PINTERRUPTED) ? 55659243Sobrien fp->p_reason | META : fp->p_reason; 55759243Sobrien } while ((fp = fp->p_friends) != pp); 55859243Sobrien /* 55959243Sobrien * Don't report on backquoted jobs, cause it will mess up 56059243Sobrien * their output. 56159243Sobrien */ 56259243Sobrien if ((reason != 0) && (adrof(STRprintexitvalue)) && 56359243Sobrien (pp->p_flags & PBACKQ) == 0) 56459243Sobrien xprintf(CGETS(17, 2, "Exit %d\n"), reason); 565167465Smp reason_str = putn(reason); 566167465Smp cleanup_push(reason_str, xfree); 567167465Smp setv(STRstatus, reason_str, VAR_READWRITE); 568167465Smp cleanup_ignore(reason_str); 569167465Smp cleanup_until(reason_str); 57059243Sobrien if (reason && exiterr) 57159243Sobrien exitstat(); 57259243Sobrien pflush(pp); 57359243Sobrien} 57459243Sobrien 57559243Sobrien/* 57659243Sobrien * dowait - wait for all processes to finish 57759243Sobrien */ 57859243Sobrien 57959243Sobrien/*ARGSUSED*/ 58059243Sobrienvoid 581167465Smpdowait(Char **v, struct command *c) 58259243Sobrien{ 583145479Smp struct process *pp; 584167465Smp sigset_t pause_mask; 58559243Sobrien 58659243Sobrien USE(c); 58759243Sobrien USE(v); 58859243Sobrien pjobs++; 589167465Smp sigprocmask(SIG_BLOCK, NULL, &pause_mask); 590167465Smp sigdelset(&pause_mask, SIGCHLD); 59159243Sobrien if (setintr) 592167465Smp sigdelset(&pause_mask, SIGINT); 59359243Sobrienloop: 59459243Sobrien for (pp = proclist.p_next; pp; pp = pp->p_next) 59559243Sobrien if (pp->p_procid && /* pp->p_procid == pp->p_jobid && */ 59659243Sobrien pp->p_flags & PRUNNING) { 597167465Smp handle_pending_signals(); 598167465Smp sigsuspend(&pause_mask); 599167465Smp handle_pending_signals(); 60059243Sobrien goto loop; 60159243Sobrien } 60259243Sobrien pjobs = 0; 60359243Sobrien} 60459243Sobrien 60559243Sobrien/* 60659243Sobrien * pflushall - flush all jobs from list (e.g. at fork()) 60759243Sobrien */ 60859243Sobrienstatic void 609167465Smppflushall(void) 61059243Sobrien{ 611145479Smp struct process *pp; 61259243Sobrien 61359243Sobrien for (pp = proclist.p_next; pp != NULL; pp = pp->p_next) 61459243Sobrien if (pp->p_procid) 61559243Sobrien pflush(pp); 61659243Sobrien} 61759243Sobrien 61859243Sobrien/* 61959243Sobrien * pflush - flag all process structures in the same job as the 62059243Sobrien * the argument process for deletion. The actual free of the 62159243Sobrien * space is not done here since pflush is called at interrupt level. 62259243Sobrien */ 62359243Sobrienstatic void 624167465Smppflush(struct process *pp) 62559243Sobrien{ 626145479Smp struct process *np; 627145479Smp int idx; 62859243Sobrien 62959243Sobrien if (pp->p_procid == 0) { 630195609Smp xprintf("%s", CGETS(17, 3, "BUG: process flushed twice")); 63159243Sobrien return; 63259243Sobrien } 63359243Sobrien while (pp->p_procid != pp->p_jobid) 63459243Sobrien pp = pp->p_friends; 63559243Sobrien pclrcurr(pp); 63659243Sobrien if (pp == pcurrjob) 63759243Sobrien pcurrjob = 0; 63859243Sobrien idx = pp->p_index; 63959243Sobrien np = pp; 64059243Sobrien do { 64159243Sobrien np->p_index = np->p_procid = 0; 64259243Sobrien np->p_flags &= ~PNEEDNOTE; 64359243Sobrien } while ((np = np->p_friends) != pp); 64459243Sobrien if (idx == pmaxindex) { 64559243Sobrien for (np = proclist.p_next, idx = 0; np; np = np->p_next) 64659243Sobrien if (np->p_index > idx) 64759243Sobrien idx = np->p_index; 64859243Sobrien pmaxindex = idx; 64959243Sobrien } 65059243Sobrien} 65159243Sobrien 65259243Sobrien/* 65359243Sobrien * pclrcurr - make sure the given job is not the current or previous job; 65459243Sobrien * pp MUST be the job leader 65559243Sobrien */ 65659243Sobrienstatic void 657167465Smppclrcurr(struct process *pp) 65859243Sobrien{ 65959243Sobrien if (pp == pcurrent) { 66059243Sobrien if (pprevious != NULL) { 66159243Sobrien pcurrent = pprevious; 66259243Sobrien pprevious = pgetcurr(pp); 66359243Sobrien } 66459243Sobrien else { 66559243Sobrien pcurrent = pgetcurr(pp); 66659243Sobrien pprevious = pgetcurr(pp); 66759243Sobrien } 66859243Sobrien } 66959243Sobrien else if (pp == pprevious) 67059243Sobrien pprevious = pgetcurr(pp); 67159243Sobrien} 67259243Sobrien 67359243Sobrien/* +4 here is 1 for '\0', 1 ea for << >& >> */ 674195609Smpstatic Char *cmdstr; 675195609Smpstatic size_t cmdmax; 676167465Smpstatic size_t cmdlen; 67759243Sobrienstatic Char *cmdp; 678195609Smp#define CMD_INIT 1024 679195609Smp#define CMD_INCR 64 68059243Sobrien 681195609Smpstatic void 682195609Smpmorecommand(size_t s) 683195609Smp{ 684195609Smp Char *ncmdstr; 685195609Smp ptrdiff_t d; 686195609Smp 687195609Smp cmdmax += s; 688195609Smp ncmdstr = xrealloc(cmdstr, cmdmax * sizeof(*cmdstr)); 689195609Smp d = ncmdstr - cmdstr; 690195609Smp cmdstr = ncmdstr; 691195609Smp cmdp += d; 692195609Smp} 693195609Smp 69483098Smp/* GrP 69583098Smp * unparse - Export padd() functionality 69683098Smp */ 69783098SmpChar * 698167465Smpunparse(struct command *t) 69983098Smp{ 700195609Smp if (cmdmax == 0) 701195609Smp morecommand(CMD_INIT); 702195609Smp cmdp = cmdstr; 70383098Smp cmdlen = 0; 70483098Smp padd(t); 70583098Smp *cmdp++ = '\0'; 706195609Smp return Strsave(cmdstr); 70783098Smp} 70883098Smp 70983098Smp 71059243Sobrien/* 71159243Sobrien * palloc - allocate a process structure and fill it up. 71259243Sobrien * an important assumption is made that the process is running. 71359243Sobrien */ 71459243Sobrienvoid 715167465Smppalloc(pid_t pid, struct command *t) 71659243Sobrien{ 717145479Smp struct process *pp; 71859243Sobrien int i; 71959243Sobrien 720167465Smp pp = xcalloc(1, sizeof(struct process)); 72159243Sobrien pp->p_procid = pid; 72259243Sobrien pp->p_flags = ((t->t_dflg & F_AMPERSAND) ? 0 : PFOREGND) | PRUNNING; 72359243Sobrien if (t->t_dflg & F_TIME) 72459243Sobrien pp->p_flags |= PPTIME; 72559243Sobrien if (t->t_dflg & F_BACKQ) 72659243Sobrien pp->p_flags |= PBACKQ; 72759243Sobrien if (t->t_dflg & F_HUP) 72859243Sobrien pp->p_flags |= PHUP; 729195609Smp if (cmdmax == 0) 730195609Smp morecommand(CMD_INIT); 731195609Smp cmdp = cmdstr; 73259243Sobrien cmdlen = 0; 73359243Sobrien padd(t); 73459243Sobrien *cmdp++ = 0; 73559243Sobrien if (t->t_dflg & F_PIPEOUT) { 73659243Sobrien pp->p_flags |= PPOU; 73759243Sobrien if (t->t_dflg & F_STDERR) 73859243Sobrien pp->p_flags |= PDIAG; 73959243Sobrien } 740195609Smp pp->p_command = Strsave(cmdstr); 74159243Sobrien if (pcurrjob) { 74259243Sobrien struct process *fp; 74359243Sobrien 74459243Sobrien /* careful here with interrupt level */ 74559243Sobrien pp->p_cwd = 0; 74659243Sobrien pp->p_index = pcurrjob->p_index; 74759243Sobrien pp->p_friends = pcurrjob; 74859243Sobrien pp->p_jobid = pcurrjob->p_procid; 74959243Sobrien for (fp = pcurrjob; fp->p_friends != pcurrjob; fp = fp->p_friends) 75059243Sobrien continue; 75159243Sobrien fp->p_friends = pp; 75259243Sobrien } 75359243Sobrien else { 75459243Sobrien pcurrjob = pp; 75559243Sobrien pp->p_jobid = pid; 75659243Sobrien pp->p_friends = pp; 75759243Sobrien pp->p_cwd = dcwd; 75859243Sobrien dcwd->di_count++; 75959243Sobrien if (pmaxindex < BIGINDEX) 76059243Sobrien pp->p_index = ++pmaxindex; 76159243Sobrien else { 76259243Sobrien struct process *np; 76359243Sobrien 76459243Sobrien for (i = 1;; i++) { 76559243Sobrien for (np = proclist.p_next; np; np = np->p_next) 76659243Sobrien if (np->p_index == i) 76759243Sobrien goto tryagain; 76859243Sobrien pp->p_index = i; 76959243Sobrien if (i > pmaxindex) 77059243Sobrien pmaxindex = i; 77159243Sobrien break; 77259243Sobrien tryagain:; 77359243Sobrien } 77459243Sobrien } 77559243Sobrien if (pcurrent == NULL) 77659243Sobrien pcurrent = pp; 77759243Sobrien else if (pprevious == NULL) 77859243Sobrien pprevious = pp; 77959243Sobrien } 78059243Sobrien pp->p_next = proclist.p_next; 78159243Sobrien proclist.p_next = pp; 78259243Sobrien#ifdef BSDTIMES 78359243Sobrien (void) gettimeofday(&pp->p_btime, NULL); 78459243Sobrien#else /* !BSDTIMES */ 78559243Sobrien# ifdef _SEQUENT_ 78659243Sobrien (void) get_process_stats(&pp->p_btime, PS_SELF, NULL, NULL); 78759243Sobrien# else /* !_SEQUENT_ */ 78859243Sobrien { 78959243Sobrien struct tms tmptimes; 79059243Sobrien 79159243Sobrien pp->p_btime = times(&tmptimes); 79259243Sobrien } 79359243Sobrien# endif /* !_SEQUENT_ */ 79459243Sobrien#endif /* !BSDTIMES */ 79559243Sobrien} 79659243Sobrien 79759243Sobrienstatic void 798167465Smppadd(struct command *t) 79959243Sobrien{ 80059243Sobrien Char **argp; 80159243Sobrien 80259243Sobrien if (t == 0) 80359243Sobrien return; 80459243Sobrien switch (t->t_dtyp) { 80559243Sobrien 80659243Sobrien case NODE_PAREN: 80759243Sobrien pads(STRLparensp); 80859243Sobrien padd(t->t_dspr); 80959243Sobrien pads(STRspRparen); 81059243Sobrien break; 81159243Sobrien 81259243Sobrien case NODE_COMMAND: 81359243Sobrien for (argp = t->t_dcom; *argp; argp++) { 81459243Sobrien pads(*argp); 81559243Sobrien if (argp[1]) 81659243Sobrien pads(STRspace); 81759243Sobrien } 81859243Sobrien break; 81959243Sobrien 82059243Sobrien case NODE_OR: 82159243Sobrien case NODE_AND: 82259243Sobrien case NODE_PIPE: 82359243Sobrien case NODE_LIST: 82459243Sobrien padd(t->t_dcar); 82559243Sobrien switch (t->t_dtyp) { 82659243Sobrien case NODE_OR: 82759243Sobrien pads(STRspor2sp); 82859243Sobrien break; 82959243Sobrien case NODE_AND: 83059243Sobrien pads(STRspand2sp); 83159243Sobrien break; 83259243Sobrien case NODE_PIPE: 83359243Sobrien pads(STRsporsp); 83459243Sobrien break; 83559243Sobrien case NODE_LIST: 83659243Sobrien pads(STRsemisp); 83759243Sobrien break; 83859243Sobrien default: 83959243Sobrien break; 84059243Sobrien } 84159243Sobrien padd(t->t_dcdr); 84259243Sobrien return; 84359243Sobrien 84459243Sobrien default: 84559243Sobrien break; 84659243Sobrien } 84759243Sobrien if ((t->t_dflg & F_PIPEIN) == 0 && t->t_dlef) { 84859243Sobrien pads((t->t_dflg & F_READ) ? STRspLarrow2sp : STRspLarrowsp); 84959243Sobrien pads(t->t_dlef); 85059243Sobrien } 85159243Sobrien if ((t->t_dflg & F_PIPEOUT) == 0 && t->t_drit) { 85259243Sobrien pads((t->t_dflg & F_APPEND) ? STRspRarrow2 : STRspRarrow); 85359243Sobrien if (t->t_dflg & F_STDERR) 85459243Sobrien pads(STRand); 85559243Sobrien pads(STRspace); 85659243Sobrien pads(t->t_drit); 85759243Sobrien } 85859243Sobrien} 85959243Sobrien 86059243Sobrienstatic void 861167465Smppads(Char *cp) 86259243Sobrien{ 863195609Smp size_t i, len; 86459243Sobrien 86559243Sobrien /* 86659243Sobrien * Avoid the Quoted Space alias hack! Reported by: 86759243Sobrien * sam@john-bigboote.ICS.UCI.EDU (Sam Horrocks) 86859243Sobrien */ 86959243Sobrien if (cp[0] == STRQNULL[0]) 87059243Sobrien cp++; 87159243Sobrien 872167465Smp i = Strlen(cp); 87359243Sobrien 874195609Smp len = cmdlen + i + CMD_INCR; 875195609Smp if (len >= cmdmax) 876195609Smp morecommand(len); 87759243Sobrien (void) Strcpy(cmdp, cp); 87859243Sobrien cmdp += i; 87959243Sobrien cmdlen += i; 88059243Sobrien} 88159243Sobrien 88259243Sobrien/* 88359243Sobrien * psavejob - temporarily save the current job on a one level stack 88459243Sobrien * so another job can be created. Used for { } in exp6 88559243Sobrien * and `` in globbing. 88659243Sobrien */ 88759243Sobrienvoid 888167465Smppsavejob(void) 88959243Sobrien{ 89059243Sobrien pholdjob = pcurrjob; 89159243Sobrien pcurrjob = NULL; 89259243Sobrien} 89359243Sobrien 89459243Sobrienvoid 895167465Smppsavejob_cleanup(void *dummy) 89659243Sobrien{ 897167465Smp USE(dummy); 89859243Sobrien pcurrjob = pholdjob; 89959243Sobrien pholdjob = NULL; 90059243Sobrien} 90159243Sobrien 90259243Sobrien/* 90359243Sobrien * pendjob - indicate that a job (set of commands) has been completed 90459243Sobrien * or is about to begin. 90559243Sobrien */ 90659243Sobrienvoid 907167465Smppendjob(void) 90859243Sobrien{ 909145479Smp struct process *pp, *tp; 91059243Sobrien 91159243Sobrien if (pcurrjob && (pcurrjob->p_flags & (PFOREGND | PSTOPPED)) == 0) { 91259243Sobrien pp = pcurrjob; 91359243Sobrien while (pp->p_procid != pp->p_jobid) 91459243Sobrien pp = pp->p_friends; 91559243Sobrien xprintf("[%d]", pp->p_index); 91659243Sobrien tp = pp; 91759243Sobrien do { 91859243Sobrien xprintf(" %d", pp->p_procid); 91959243Sobrien pp = pp->p_friends; 92059243Sobrien } while (pp != tp); 92159243Sobrien xputchar('\n'); 92259243Sobrien } 92359243Sobrien pholdjob = pcurrjob = 0; 92459243Sobrien} 92559243Sobrien 92659243Sobrien/* 92759243Sobrien * pprint - print a job 92859243Sobrien */ 92959243Sobrien 93059243Sobrien/* 93159243Sobrien * Hacks have been added for SVR4 to deal with pipe's being spawned in 93259243Sobrien * reverse order 93359243Sobrien * 93459243Sobrien * David Dawes (dawes@physics.su.oz.au) Oct 1991 93559243Sobrien */ 93659243Sobrien 93759243Sobrienstatic int 938167465Smppprint(struct process *pp, int flag) 93959243Sobrien{ 94059243Sobrien int status, reason; 94159243Sobrien struct process *tp; 94259243Sobrien int jobflags, pstatus, pcond; 943145479Smp const char *format; 94459243Sobrien 94559243Sobrien#ifdef BACKPIPE 94659243Sobrien struct process *pipehead = NULL, *pipetail = NULL, *pmarker = NULL; 94759243Sobrien int inpipe = 0; 94859243Sobrien#endif /* BACKPIPE */ 94959243Sobrien 95059243Sobrien while (pp->p_procid != pp->p_jobid) 95159243Sobrien pp = pp->p_friends; 95259243Sobrien if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { 95359243Sobrien pp->p_flags &= ~PPTIME; 95459243Sobrien pp->p_flags |= PTIME; 95559243Sobrien } 95659243Sobrien tp = pp; 95759243Sobrien status = reason = -1; 95859243Sobrien jobflags = 0; 95959243Sobrien do { 96059243Sobrien#ifdef BACKPIPE 96159243Sobrien /* 96259243Sobrien * The pipeline is reversed, so locate the real head of the pipeline 96359243Sobrien * if pp is at the tail of a pipe (and not already in a pipeline) 96459243Sobrien */ 96559243Sobrien if ((pp->p_friends->p_flags & PPOU) && !inpipe && (flag & NAME)) { 96659243Sobrien inpipe = 1; 96759243Sobrien pipetail = pp; 96859243Sobrien do 96959243Sobrien pp = pp->p_friends; 97059243Sobrien while (pp->p_friends->p_flags & PPOU); 97159243Sobrien pipehead = pp; 97259243Sobrien pmarker = pp; 97359243Sobrien /* 97459243Sobrien * pmarker is used to hold the place of the proc being processed, so 97559243Sobrien * we can search for the next one downstream later. 97659243Sobrien */ 97759243Sobrien } 978167465Smp pcond = (tp != pp || (inpipe && tp == pp)); 97959243Sobrien#else /* !BACKPIPE */ 980167465Smp pcond = (tp != pp); 98159243Sobrien#endif /* BACKPIPE */ 98259243Sobrien 98359243Sobrien jobflags |= pp->p_flags; 98459243Sobrien pstatus = (int) (pp->p_flags & PALLSTATES); 98559243Sobrien if (pcond && linp != linbuf && !(flag & FANCY) && 98659243Sobrien ((pstatus == status && pp->p_reason == reason) || 98759243Sobrien !(flag & REASON))) 98859243Sobrien xputchar(' '); 98959243Sobrien else { 99059243Sobrien if (pcond && linp != linbuf) 99159243Sobrien xputchar('\n'); 99259243Sobrien if (flag & NUMBER) { 99359243Sobrien#ifdef BACKPIPE 99459243Sobrien pcond = ((pp == tp && !inpipe) || 99559243Sobrien (inpipe && pipetail == tp && pp == pipehead)); 99659243Sobrien#else /* BACKPIPE */ 99759243Sobrien pcond = (pp == tp); 99859243Sobrien#endif /* BACKPIPE */ 99959243Sobrien if (pcond) 100059243Sobrien xprintf("[%d]%s %c ", pp->p_index, 100159243Sobrien pp->p_index < 10 ? " " : "", 100259243Sobrien pp == pcurrent ? '+' : 100359243Sobrien (pp == pprevious ? '-' : ' ')); 100459243Sobrien else 100559243Sobrien xprintf(" "); 100659243Sobrien } 100759243Sobrien if (flag & FANCY) { 100859243Sobrien xprintf("%5d ", pp->p_procid); 100959243Sobrien#ifdef TCF 101059243Sobrien xprintf("%11s ", sitename(pp->p_procid)); 101159243Sobrien#endif /* TCF */ 101259243Sobrien } 101359243Sobrien if (flag & (REASON | AREASON)) { 101459243Sobrien if (flag & NAME) 101559243Sobrien format = "%-30s"; 101659243Sobrien else 101759243Sobrien format = "%s"; 101859243Sobrien if (pstatus == status) { 101959243Sobrien if (pp->p_reason == reason) { 102059243Sobrien xprintf(format, ""); 102159243Sobrien goto prcomd; 102259243Sobrien } 102359243Sobrien else 102459243Sobrien reason = (int) pp->p_reason; 102559243Sobrien } 102659243Sobrien else { 102759243Sobrien status = pstatus; 102859243Sobrien reason = (int) pp->p_reason; 102959243Sobrien } 103059243Sobrien switch (status) { 103159243Sobrien 103259243Sobrien case PRUNNING: 103359243Sobrien xprintf(format, CGETS(17, 4, "Running ")); 103459243Sobrien break; 103559243Sobrien 103659243Sobrien case PINTERRUPTED: 103759243Sobrien case PSTOPPED: 103859243Sobrien case PSIGNALED: 103959243Sobrien /* 104059243Sobrien * tell what happened to the background job 1041167465Smp * From: Michael Schroeder 104259243Sobrien * <mlschroe@immd4.informatik.uni-erlangen.de> 104359243Sobrien */ 104459243Sobrien if ((flag & REASON) 104559243Sobrien || ((flag & AREASON) 104659243Sobrien && reason != SIGINT 104759243Sobrien && (reason != SIGPIPE 104859243Sobrien || (pp->p_flags & PPOU) == 0))) { 1049167465Smp char *ptr; 1050167465Smp int free_ptr; 105159243Sobrien 1052167465Smp free_ptr = 0; 1053167465Smp ptr = (char *)(intptr_t)mesg[pp->p_reason & 0177].pname; 1054167465Smp if (ptr == NULL) { 1055167465Smp ptr = xasprintf("%s %d", CGETS(17, 5, "Signal"), 1056167465Smp pp->p_reason & 0177); 1057167465Smp cleanup_push(ptr, xfree); 1058167465Smp free_ptr = 1; 1059145479Smp } 106059243Sobrien xprintf(format, ptr); 1061167465Smp if (free_ptr != 0) 1062167465Smp cleanup_until(ptr); 106359243Sobrien } 106459243Sobrien else 106559243Sobrien reason = -1; 106659243Sobrien break; 106759243Sobrien 106859243Sobrien case PNEXITED: 106959243Sobrien case PAEXITED: 107059243Sobrien if (flag & REASON) { 107159243Sobrien if (pp->p_reason) 107259243Sobrien xprintf(CGETS(17, 6, "Exit %-25d"), pp->p_reason); 107359243Sobrien else 107459243Sobrien xprintf(format, CGETS(17, 7, "Done")); 107559243Sobrien } 107659243Sobrien break; 107759243Sobrien 107859243Sobrien default: 107959243Sobrien xprintf(CGETS(17, 8, "BUG: status=%-9o"), 108059243Sobrien status); 108159243Sobrien } 108259243Sobrien } 108359243Sobrien } 108459243Sobrienprcomd: 108559243Sobrien if (flag & NAME) { 108659243Sobrien xprintf("%S", pp->p_command); 108759243Sobrien if (pp->p_flags & PPOU) 108859243Sobrien xprintf(" |"); 108959243Sobrien if (pp->p_flags & PDIAG) 109059243Sobrien xprintf("&"); 109159243Sobrien } 109259243Sobrien if (flag & (REASON | AREASON) && pp->p_flags & PDUMPED) 1093195609Smp xprintf("%s", CGETS(17, 9, " (core dumped)")); 109459243Sobrien if (tp == pp->p_friends) { 109559243Sobrien if (flag & AMPERSAND) 109659243Sobrien xprintf(" &"); 109759243Sobrien if (flag & JOBDIR && 109859243Sobrien !eq(tp->p_cwd->di_name, dcwd->di_name)) { 1099195609Smp xprintf("%s", CGETS(17, 10, " (wd: ")); 110059243Sobrien dtildepr(tp->p_cwd->di_name); 110159243Sobrien xprintf(")"); 110259243Sobrien } 110359243Sobrien } 110459243Sobrien if (pp->p_flags & PPTIME && !(status & (PSTOPPED | PRUNNING))) { 110559243Sobrien if (linp != linbuf) 110659243Sobrien xprintf("\n\t"); 110759243Sobrien#if defined(BSDTIMES) || defined(_SEQUENT_) 110859243Sobrien prusage(&zru, &pp->p_rusage, &pp->p_etime, 110959243Sobrien &pp->p_btime); 111059243Sobrien#else /* !BSDTIMES && !SEQUENT */ 111159243Sobrien lru.tms_utime = pp->p_utime; 111259243Sobrien lru.tms_stime = pp->p_stime; 111359243Sobrien lru.tms_cutime = 0; 111459243Sobrien lru.tms_cstime = 0; 111559243Sobrien prusage(&zru, &lru, pp->p_etime, 111659243Sobrien pp->p_btime); 111759243Sobrien#endif /* !BSDTIMES && !SEQUENT */ 111859243Sobrien 111959243Sobrien } 112059243Sobrien#ifdef BACKPIPE 112159243Sobrien pcond = ((tp == pp->p_friends && !inpipe) || 112259243Sobrien (inpipe && pipehead->p_friends == tp && pp == pipetail)); 112359243Sobrien#else /* !BACKPIPE */ 112459243Sobrien pcond = (tp == pp->p_friends); 112559243Sobrien#endif /* BACKPIPE */ 112659243Sobrien if (pcond) { 112759243Sobrien if (linp != linbuf) 112859243Sobrien xputchar('\n'); 112959243Sobrien if (flag & SHELLDIR && !eq(tp->p_cwd->di_name, dcwd->di_name)) { 1130195609Smp xprintf("%s", CGETS(17, 11, "(wd now: ")); 113159243Sobrien dtildepr(dcwd->di_name); 113259243Sobrien xprintf(")\n"); 113359243Sobrien } 113459243Sobrien } 113559243Sobrien#ifdef BACKPIPE 113659243Sobrien if (inpipe) { 113759243Sobrien /* 113859243Sobrien * if pmaker == pipetail, we are finished that pipeline, and 113959243Sobrien * can now skip to past the head 114059243Sobrien */ 114159243Sobrien if (pmarker == pipetail) { 114259243Sobrien inpipe = 0; 114359243Sobrien pp = pipehead; 114459243Sobrien } 114559243Sobrien else { 114659243Sobrien /* 114759243Sobrien * set pp to one before the one we want next, so the while below 114859243Sobrien * increments to the correct spot. 114959243Sobrien */ 115059243Sobrien do 115159243Sobrien pp = pp->p_friends; 115259243Sobrien while (pp->p_friends->p_friends != pmarker); 115359243Sobrien pmarker = pp->p_friends; 115459243Sobrien } 115559243Sobrien } 115659243Sobrien pcond = ((pp = pp->p_friends) != tp || inpipe); 115759243Sobrien#else /* !BACKPIPE */ 115859243Sobrien pcond = ((pp = pp->p_friends) != tp); 115959243Sobrien#endif /* BACKPIPE */ 116059243Sobrien } while (pcond); 116159243Sobrien 116259243Sobrien if (jobflags & PTIME && (jobflags & (PSTOPPED | PRUNNING)) == 0) { 116359243Sobrien if (jobflags & NUMBER) 116459243Sobrien xprintf(" "); 116559243Sobrien ptprint(tp); 116659243Sobrien } 116759243Sobrien return (jobflags); 116859243Sobrien} 116959243Sobrien 117059243Sobrien/* 117159243Sobrien * All 4.3 BSD derived implementations are buggy and I've had enough. 117259243Sobrien * The following implementation produces similar code and works in all 117359243Sobrien * cases. The 4.3BSD one works only for <, >, != 117459243Sobrien */ 117559243Sobrien# undef timercmp 117659243Sobrien# define timercmp(tvp, uvp, cmp) \ 117759243Sobrien (((tvp)->tv_sec == (uvp)->tv_sec) ? \ 117859243Sobrien ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ 117959243Sobrien ((tvp)->tv_sec cmp (uvp)->tv_sec)) 118059243Sobrien 118159243Sobrienstatic void 1182167465Smpptprint(struct process *tp) 118359243Sobrien{ 118459243Sobrien#ifdef BSDTIMES 118559243Sobrien struct timeval tetime, diff; 118659243Sobrien static struct timeval ztime; 118759243Sobrien struct sysrusage ru; 1188145479Smp struct process *pp = tp; 118959243Sobrien 119059243Sobrien ru = zru; 119159243Sobrien tetime = ztime; 119259243Sobrien do { 119359243Sobrien ruadd(&ru, &pp->p_rusage); 119459243Sobrien tvsub(&diff, &pp->p_etime, &pp->p_btime); 119559243Sobrien if (timercmp(&diff, &tetime, >)) 119659243Sobrien tetime = diff; 119759243Sobrien } while ((pp = pp->p_friends) != tp); 119859243Sobrien prusage(&zru, &ru, &tetime, &ztime); 119959243Sobrien#else /* !BSDTIMES */ 120059243Sobrien# ifdef _SEQUENT_ 120159243Sobrien timeval_t tetime, diff; 120259243Sobrien static timeval_t ztime; 120359243Sobrien struct process_stats ru; 1204145479Smp struct process *pp = tp; 120559243Sobrien 120659243Sobrien ru = zru; 120759243Sobrien tetime = ztime; 120859243Sobrien do { 120959243Sobrien ruadd(&ru, &pp->p_rusage); 121059243Sobrien tvsub(&diff, &pp->p_etime, &pp->p_btime); 121159243Sobrien if (timercmp(&diff, &tetime, >)) 121259243Sobrien tetime = diff; 121359243Sobrien } while ((pp = pp->p_friends) != tp); 121459243Sobrien prusage(&zru, &ru, &tetime, &ztime); 121559243Sobrien# else /* !_SEQUENT_ */ 121659243Sobrien# ifndef POSIX 121759243Sobrien static time_t ztime = 0; 121859243Sobrien static time_t zu_time = 0; 121959243Sobrien static time_t zs_time = 0; 122059243Sobrien time_t tetime, diff; 122159243Sobrien time_t u_time, s_time; 122259243Sobrien 122359243Sobrien# else /* POSIX */ 122459243Sobrien static clock_t ztime = 0; 122559243Sobrien static clock_t zu_time = 0; 122659243Sobrien static clock_t zs_time = 0; 122759243Sobrien clock_t tetime, diff; 122859243Sobrien clock_t u_time, s_time; 122959243Sobrien 123059243Sobrien# endif /* POSIX */ 123159243Sobrien struct tms zts, rts; 1232145479Smp struct process *pp = tp; 123359243Sobrien 123459243Sobrien u_time = zu_time; 123559243Sobrien s_time = zs_time; 123659243Sobrien tetime = ztime; 123759243Sobrien do { 123859243Sobrien u_time += pp->p_utime; 123959243Sobrien s_time += pp->p_stime; 124059243Sobrien diff = pp->p_etime - pp->p_btime; 124159243Sobrien if (diff > tetime) 124259243Sobrien tetime = diff; 124359243Sobrien } while ((pp = pp->p_friends) != tp); 124459243Sobrien zts.tms_utime = zu_time; 124559243Sobrien zts.tms_stime = zs_time; 124659243Sobrien zts.tms_cutime = 0; 124759243Sobrien zts.tms_cstime = 0; 124859243Sobrien rts.tms_utime = u_time; 124959243Sobrien rts.tms_stime = s_time; 125059243Sobrien rts.tms_cutime = 0; 125159243Sobrien rts.tms_cstime = 0; 125259243Sobrien prusage(&zts, &rts, tetime, ztime); 125359243Sobrien# endif /* !_SEQUENT_ */ 125459243Sobrien#endif /* !BSDTIMES */ 125559243Sobrien} 125659243Sobrien 125759243Sobrien/* 125859243Sobrien * dojobs - print all jobs 125959243Sobrien */ 126059243Sobrien/*ARGSUSED*/ 126159243Sobrienvoid 1262167465Smpdojobs(Char **v, struct command *c) 126359243Sobrien{ 1264145479Smp struct process *pp; 1265145479Smp int flag = NUMBER | NAME | REASON; 126659243Sobrien int i; 126759243Sobrien 126859243Sobrien USE(c); 126959243Sobrien if (chkstop) 127059243Sobrien chkstop = 2; 127159243Sobrien if (*++v) { 127259243Sobrien if (v[1] || !eq(*v, STRml)) 127359243Sobrien stderror(ERR_JOBS); 127459243Sobrien flag |= FANCY | JOBDIR; 127559243Sobrien } 127659243Sobrien for (i = 1; i <= pmaxindex; i++) 127759243Sobrien for (pp = proclist.p_next; pp; pp = pp->p_next) 127859243Sobrien if (pp->p_index == i && pp->p_procid == pp->p_jobid) { 127959243Sobrien pp->p_flags &= ~PNEEDNOTE; 128059243Sobrien if (!(pprint(pp, flag) & (PRUNNING | PSTOPPED))) 128159243Sobrien pflush(pp); 128259243Sobrien break; 128359243Sobrien } 128459243Sobrien} 128559243Sobrien 128659243Sobrien/* 128759243Sobrien * dofg - builtin - put the job into the foreground 128859243Sobrien */ 128959243Sobrien/*ARGSUSED*/ 129059243Sobrienvoid 1291167465Smpdofg(Char **v, struct command *c) 129259243Sobrien{ 1293145479Smp struct process *pp; 129459243Sobrien 129559243Sobrien USE(c); 129659243Sobrien okpcntl(); 129759243Sobrien ++v; 129859243Sobrien do { 129959243Sobrien pp = pfind(*v); 130059243Sobrien if (!pstart(pp, 1)) { 130159243Sobrien pp->p_procid = 0; 130259243Sobrien stderror(ERR_NAME|ERR_BADJOB, pp->p_command, strerror(errno)); 130359243Sobrien continue; 130459243Sobrien } 130559243Sobrien pjwait(pp); 130659243Sobrien } while (*v && *++v); 130759243Sobrien} 130859243Sobrien 130959243Sobrien/* 131059243Sobrien * %... - builtin - put the job into the foreground 131159243Sobrien */ 131259243Sobrien/*ARGSUSED*/ 131359243Sobrienvoid 1314167465Smpdofg1(Char **v, struct command *c) 131559243Sobrien{ 1316145479Smp struct process *pp; 131759243Sobrien 131859243Sobrien USE(c); 131959243Sobrien okpcntl(); 132059243Sobrien pp = pfind(v[0]); 132159243Sobrien if (!pstart(pp, 1)) { 132259243Sobrien pp->p_procid = 0; 132359243Sobrien stderror(ERR_NAME|ERR_BADJOB, pp->p_command, strerror(errno)); 132459243Sobrien return; 132559243Sobrien } 132659243Sobrien pjwait(pp); 132759243Sobrien} 132859243Sobrien 132959243Sobrien/* 133059243Sobrien * dobg - builtin - put the job into the background 133159243Sobrien */ 133259243Sobrien/*ARGSUSED*/ 133359243Sobrienvoid 1334167465Smpdobg(Char **v, struct command *c) 133559243Sobrien{ 1336145479Smp struct process *pp; 133759243Sobrien 133859243Sobrien USE(c); 133959243Sobrien okpcntl(); 134059243Sobrien ++v; 134159243Sobrien do { 134259243Sobrien pp = pfind(*v); 134359243Sobrien if (!pstart(pp, 0)) { 134459243Sobrien pp->p_procid = 0; 134559243Sobrien stderror(ERR_NAME|ERR_BADJOB, pp->p_command, strerror(errno)); 134659243Sobrien } 134759243Sobrien } while (*v && *++v); 134859243Sobrien} 134959243Sobrien 135059243Sobrien/* 135159243Sobrien * %... & - builtin - put the job into the background 135259243Sobrien */ 135359243Sobrien/*ARGSUSED*/ 135459243Sobrienvoid 1355167465Smpdobg1(Char **v, struct command *c) 135659243Sobrien{ 1357145479Smp struct process *pp; 135859243Sobrien 135959243Sobrien USE(c); 136059243Sobrien pp = pfind(v[0]); 136159243Sobrien if (!pstart(pp, 0)) { 136259243Sobrien pp->p_procid = 0; 136359243Sobrien stderror(ERR_NAME|ERR_BADJOB, pp->p_command, strerror(errno)); 136459243Sobrien } 136559243Sobrien} 136659243Sobrien 136759243Sobrien/* 136859243Sobrien * dostop - builtin - stop the job 136959243Sobrien */ 137059243Sobrien/*ARGSUSED*/ 137159243Sobrienvoid 1372167465Smpdostop(Char **v, struct command *c) 137359243Sobrien{ 137459243Sobrien USE(c); 137559243Sobrien#ifdef BSDJOBS 137659243Sobrien pkill(++v, SIGSTOP); 137759243Sobrien#endif /* BSDJOBS */ 137859243Sobrien} 137959243Sobrien 138059243Sobrien/* 138159243Sobrien * dokill - builtin - superset of kill (1) 138259243Sobrien */ 138359243Sobrien/*ARGSUSED*/ 138459243Sobrienvoid 1385167465Smpdokill(Char **v, struct command *c) 138659243Sobrien{ 1387145479Smp int signum, len = 0; 1388145479Smp const char *name; 138983098Smp Char *sigptr; 139059243Sobrien 139159243Sobrien USE(c); 139259243Sobrien v++; 139359243Sobrien if (v[0] && v[0][0] == '-') { 139459243Sobrien if (v[0][1] == 'l') { 139559243Sobrien for (signum = 0; signum <= nsig; signum++) { 139659243Sobrien if ((name = mesg[signum].iname) != NULL) { 139759243Sobrien len += strlen(name) + 1; 1398167465Smp if (len >= TermH - 1) { 139959243Sobrien xputchar('\n'); 140059243Sobrien len = strlen(name) + 1; 140159243Sobrien } 140259243Sobrien xprintf("%s ", name); 140359243Sobrien } 140459243Sobrien } 140559243Sobrien xputchar('\n'); 140659243Sobrien return; 140759243Sobrien } 140883098Smp sigptr = &v[0][1]; 140983098Smp if (v[0][1] == 's') { 141083098Smp if (v[1]) { 141183098Smp v++; 141283098Smp sigptr = &v[0][0]; 141383098Smp } else { 141483098Smp stderror(ERR_NAME | ERR_TOOFEW); 141583098Smp } 141683098Smp } 141783098Smp if (Isdigit(*sigptr)) { 1418131962Smp char *ep; 1419131962Smp signum = strtoul(short2str(sigptr), &ep, 0); 1420131962Smp if (*ep || signum < 0 || signum > (MAXSIG-1)) 142159243Sobrien stderror(ERR_NAME | ERR_BADSIG); 142259243Sobrien } 142359243Sobrien else { 142459243Sobrien for (signum = 0; signum <= nsig; signum++) 142559243Sobrien if (mesg[signum].iname && 142683098Smp eq(sigptr, str2short(mesg[signum].iname))) 142759243Sobrien goto gotsig; 142883098Smp setname(short2str(sigptr)); 142959243Sobrien stderror(ERR_NAME | ERR_UNKSIG); 143059243Sobrien } 143159243Sobriengotsig: 143259243Sobrien v++; 143359243Sobrien } 143459243Sobrien else 143559243Sobrien signum = SIGTERM; 143659243Sobrien pkill(v, signum); 143759243Sobrien} 143859243Sobrien 143959243Sobrienstatic void 1440167465Smppkill(Char **v, int signum) 144159243Sobrien{ 1442145479Smp struct process *pp, *np; 144359243Sobrien int jobflags = 0, err1 = 0; 144459243Sobrien pid_t pid; 1445167465Smp Char *cp, **vp, **globbed; 144659243Sobrien 144759243Sobrien /* Avoid globbing %?x patterns */ 144859243Sobrien for (vp = v; vp && *vp; vp++) 144959243Sobrien if (**vp == '%') 145059243Sobrien (void) quote(*vp); 145159243Sobrien 1452167465Smp v = glob_all_or_error(v); 1453167465Smp globbed = v; 1454167465Smp cleanup_push(globbed, blk_cleanup); 145559243Sobrien 1456172665Smp pchild_disabled++; 1457172665Smp cleanup_push(&pchild_disabled, disabled_cleanup); 1458172665Smp if (setintr) { 1459172665Smp pintr_disabled++; 1460172665Smp cleanup_push(&pintr_disabled, disabled_cleanup); 1461172665Smp } 146259243Sobrien 146359243Sobrien while (v && (cp = *v)) { 146459243Sobrien if (*cp == '%') { 146559243Sobrien np = pp = pfind(cp); 146659243Sobrien do 146759243Sobrien jobflags |= np->p_flags; 146859243Sobrien while ((np = np->p_friends) != pp); 146959243Sobrien#ifdef BSDJOBS 147059243Sobrien switch (signum) { 147159243Sobrien 147259243Sobrien case SIGSTOP: 147359243Sobrien case SIGTSTP: 147459243Sobrien case SIGTTIN: 147559243Sobrien case SIGTTOU: 147659243Sobrien if ((jobflags & PRUNNING) == 0) { 147759243Sobrien# ifdef SUSPENDED 147859243Sobrien xprintf(CGETS(17, 12, "%S: Already suspended\n"), cp); 147959243Sobrien# else /* !SUSPENDED */ 148059243Sobrien xprintf(CGETS(17, 13, "%S: Already stopped\n"), cp); 148159243Sobrien# endif /* !SUSPENDED */ 148259243Sobrien err1++; 148359243Sobrien goto cont; 148459243Sobrien } 148559243Sobrien break; 148659243Sobrien /* 148759243Sobrien * suspend a process, kill -CONT %, then type jobs; the shell 148859243Sobrien * says it is suspended, but it is running; thanks jaap.. 148959243Sobrien */ 149059243Sobrien case SIGCONT: 149159243Sobrien if (!pstart(pp, 0)) { 149259243Sobrien pp->p_procid = 0; 149359243Sobrien stderror(ERR_NAME|ERR_BADJOB, pp->p_command, 149459243Sobrien strerror(errno)); 149559243Sobrien } 149659243Sobrien goto cont; 149759243Sobrien default: 149859243Sobrien break; 149959243Sobrien } 150059243Sobrien#endif /* BSDJOBS */ 150159243Sobrien if (killpg(pp->p_jobid, signum) < 0) { 150259243Sobrien xprintf("%S: %s\n", cp, strerror(errno)); 150359243Sobrien err1++; 150459243Sobrien } 150559243Sobrien#ifdef BSDJOBS 150659243Sobrien if (signum == SIGTERM || signum == SIGHUP) 150759243Sobrien (void) killpg(pp->p_jobid, SIGCONT); 150859243Sobrien#endif /* BSDJOBS */ 150959243Sobrien } 151059243Sobrien else if (!(Isdigit(*cp) || *cp == '-')) 151159243Sobrien stderror(ERR_NAME | ERR_JOBARGS); 151259243Sobrien else { 1513131962Smp char *ep; 151469408Sache#ifndef WINNT_NATIVE 1515131962Smp pid = strtol(short2str(cp), &ep, 10); 151659243Sobrien#else 1517131962Smp pid = strtoul(short2str(cp), &ep, 0); 151869408Sache#endif /* WINNT_NATIVE */ 1519131962Smp if (*ep) 1520131962Smp stderror(ERR_NAME | ERR_JOBARGS); 1521131962Smp else if (kill(pid, signum) < 0) { 152259243Sobrien xprintf("%d: %s\n", pid, strerror(errno)); 152359243Sobrien err1++; 152459243Sobrien goto cont; 152559243Sobrien } 152659243Sobrien#ifdef BSDJOBS 152759243Sobrien if (signum == SIGTERM || signum == SIGHUP) 152859243Sobrien (void) kill(pid, SIGCONT); 152959243Sobrien#endif /* BSDJOBS */ 153059243Sobrien } 153159243Sobriencont: 153259243Sobrien v++; 153359243Sobrien } 1534167465Smp cleanup_until(&pchild_disabled); 153559243Sobrien if (err1) 153659243Sobrien stderror(ERR_SILENT); 153759243Sobrien} 153859243Sobrien 153959243Sobrien/* 154059243Sobrien * pstart - start the job in foreground/background 154159243Sobrien */ 154259243Sobrienint 1543167465Smppstart(struct process *pp, int foregnd) 154459243Sobrien{ 154559243Sobrien int rv = 0; 1546145479Smp struct process *np; 154759243Sobrien /* We don't use jobflags in this function right now (see below) */ 154859243Sobrien /* long jobflags = 0; */ 154959243Sobrien 1550167465Smp pchild_disabled++; 1551167465Smp cleanup_push(&pchild_disabled, disabled_cleanup); 155259243Sobrien np = pp; 155359243Sobrien do { 155459243Sobrien /* We don't use jobflags in this function right now (see below) */ 155559243Sobrien /* jobflags |= np->p_flags; */ 155659243Sobrien if (np->p_flags & (PRUNNING | PSTOPPED)) { 155759243Sobrien np->p_flags |= PRUNNING; 155859243Sobrien np->p_flags &= ~PSTOPPED; 155959243Sobrien if (foregnd) 156059243Sobrien np->p_flags |= PFOREGND; 156159243Sobrien else 156259243Sobrien np->p_flags &= ~PFOREGND; 156359243Sobrien } 156459243Sobrien } while ((np = np->p_friends) != pp); 156559243Sobrien if (!foregnd) 156659243Sobrien pclrcurr(pp); 156759243Sobrien (void) pprint(pp, foregnd ? NAME | JOBDIR : NUMBER | NAME | AMPERSAND); 156883098Smp 156983098Smp /* GrP run jobcmd hook if foregrounding */ 157083098Smp if (foregnd) { 157183098Smp job_cmd(pp->p_command); 157283098Smp } 157383098Smp 157459243Sobrien#ifdef BSDJOBS 157559243Sobrien if (foregnd) { 157659243Sobrien rv = tcsetpgrp(FSHTTY, pp->p_jobid); 157759243Sobrien } 157859243Sobrien /* 157959243Sobrien * 1. child process of csh (shell script) receives SIGTTIN/SIGTTOU 158059243Sobrien * 2. parent process (csh) receives SIGCHLD 158159243Sobrien * 3. The "csh" signal handling function pchild() is invoked 158259243Sobrien * with a SIGCHLD signal. 158359243Sobrien * 4. pchild() calls wait3(WNOHANG) which returns 0. 158459243Sobrien * The child process is NOT ready to be waited for at this time. 158559243Sobrien * pchild() returns without picking-up the correct status 1586167465Smp * for the child process which generated the SIGCHLD. 158759243Sobrien * 5. CONSEQUENCE : csh is UNaware that the process is stopped 158859243Sobrien * 6. THIS LINE HAS BEEN COMMENTED OUT : if (jobflags&PSTOPPED) 158959243Sobrien * (beto@aixwiz.austin.ibm.com - aug/03/91) 159059243Sobrien * 7. I removed the line completely and added extra checks for 159159243Sobrien * pstart, so that if a job gets attached to and dies inside 159259243Sobrien * a debugger it does not confuse the shell. [christos] 159359243Sobrien * 8. on the nec sx-4 there seems to be a problem, which requires 159459243Sobrien * a syscall(151, getpid(), getpid()) in osinit. Don't ask me 159559243Sobrien * what this is doing. [schott@rzg.mpg.de] 159659243Sobrien */ 159759243Sobrien 159859243Sobrien if (rv != -1) 159959243Sobrien rv = killpg(pp->p_jobid, SIGCONT); 160059243Sobrien#endif /* BSDJOBS */ 1601167465Smp cleanup_until(&pchild_disabled); 160259243Sobrien return rv != -1; 160359243Sobrien} 160459243Sobrien 160559243Sobrienvoid 1606167465Smppanystop(int neednl) 160759243Sobrien{ 1608145479Smp struct process *pp; 160959243Sobrien 161059243Sobrien chkstop = 2; 161159243Sobrien for (pp = proclist.p_next; pp; pp = pp->p_next) 161259243Sobrien if (pp->p_flags & PSTOPPED) 161359243Sobrien stderror(ERR_STOPPED, neednl ? "\n" : ""); 161459243Sobrien} 161559243Sobrien 161659243Sobrienstruct process * 1617167465Smppfind(Char *cp) 161859243Sobrien{ 1619145479Smp struct process *pp, *np; 162059243Sobrien 162159243Sobrien if (cp == 0 || cp[1] == 0 || eq(cp, STRcent2) || eq(cp, STRcentplus)) { 162259243Sobrien if (pcurrent == NULL) 162359243Sobrien stderror(ERR_NAME | ERR_JOBCUR); 162459243Sobrien return (pcurrent); 162559243Sobrien } 162659243Sobrien if (eq(cp, STRcentminus) || eq(cp, STRcenthash)) { 162759243Sobrien if (pprevious == NULL) 162859243Sobrien stderror(ERR_NAME | ERR_JOBPREV); 162959243Sobrien return (pprevious); 163059243Sobrien } 163159243Sobrien if (Isdigit(cp[1])) { 163259243Sobrien int idx = atoi(short2str(cp + 1)); 163359243Sobrien 163459243Sobrien for (pp = proclist.p_next; pp; pp = pp->p_next) 163559243Sobrien if (pp->p_index == idx && pp->p_procid == pp->p_jobid) 163659243Sobrien return (pp); 163759243Sobrien stderror(ERR_NAME | ERR_NOSUCHJOB); 163859243Sobrien } 163959243Sobrien np = NULL; 164059243Sobrien for (pp = proclist.p_next; pp; pp = pp->p_next) 164159243Sobrien if (pp->p_procid == pp->p_jobid) { 164259243Sobrien if (cp[1] == '?') { 1643145479Smp Char *dp; 164459243Sobrien 164559243Sobrien for (dp = pp->p_command; *dp; dp++) { 164659243Sobrien if (*dp != cp[2]) 164759243Sobrien continue; 164859243Sobrien if (prefix(cp + 2, dp)) 164959243Sobrien goto match; 165059243Sobrien } 165159243Sobrien } 165259243Sobrien else if (prefix(cp + 1, pp->p_command)) { 165359243Sobrien match: 165459243Sobrien if (np) 165559243Sobrien stderror(ERR_NAME | ERR_AMBIG); 165659243Sobrien np = pp; 165759243Sobrien } 165859243Sobrien } 165959243Sobrien if (np) 166059243Sobrien return (np); 166159243Sobrien stderror(ERR_NAME | (cp[1] == '?' ? ERR_JOBPAT : ERR_NOSUCHJOB)); 166259243Sobrien /* NOTREACHED */ 166359243Sobrien return (0); 166459243Sobrien} 166559243Sobrien 166659243Sobrien 166759243Sobrien/* 166859243Sobrien * pgetcurr - find most recent job that is not pp, preferably stopped 166959243Sobrien */ 167059243Sobrienstatic struct process * 1671167465Smppgetcurr(struct process *pp) 167259243Sobrien{ 1673145479Smp struct process *np; 1674145479Smp struct process *xp = NULL; 167559243Sobrien 167659243Sobrien for (np = proclist.p_next; np; np = np->p_next) 167759243Sobrien if (np != pcurrent && np != pp && np->p_procid && 167859243Sobrien np->p_procid == np->p_jobid) { 167959243Sobrien if (np->p_flags & PSTOPPED) 168059243Sobrien return (np); 168159243Sobrien if (xp == NULL) 168259243Sobrien xp = np; 168359243Sobrien } 168459243Sobrien return (xp); 168559243Sobrien} 168659243Sobrien 168759243Sobrien/* 168859243Sobrien * donotify - flag the job so as to report termination asynchronously 168959243Sobrien */ 169059243Sobrien/*ARGSUSED*/ 169159243Sobrienvoid 1692167465Smpdonotify(Char **v, struct command *c) 169359243Sobrien{ 1694145479Smp struct process *pp; 169559243Sobrien 169659243Sobrien USE(c); 169759243Sobrien pp = pfind(*++v); 169859243Sobrien pp->p_flags |= PNOTIFY; 169959243Sobrien} 170059243Sobrien 1701167465Smp#ifdef SIGSYNCH 1702167465Smpstatic void 1703167465Smpsynch_handler(int sno) 1704167465Smp{ 1705167465Smp USE(sno); 1706167465Smp} 1707167465Smp#endif /* SIGSYNCH */ 1708167465Smp 170959243Sobrien/* 171059243Sobrien * Do the fork and whatever should be done in the child side that 171159243Sobrien * should not be done if we are not forking at all (like for simple builtin's) 171259243Sobrien * Also do everything that needs any signals fiddled with in the parent side 171359243Sobrien * 171459243Sobrien * Wanttty tells whether process and/or tty pgrps are to be manipulated: 171559243Sobrien * -1: leave tty alone; inherit pgrp from parent 171659243Sobrien * 0: already have tty; manipulate process pgrps only 171759243Sobrien * 1: want to claim tty; manipulate process and tty pgrps 171859243Sobrien * It is usually just the value of tpgrp. 171959243Sobrien */ 172059243Sobrien 1721167465Smppid_t 1722167465Smppfork(struct command *t, int wanttty) 172359243Sobrien{ 1724167465Smp pid_t pid; 1725145479Smp int ignint = 0; 1726167465Smp pid_t pgrp; 172759243Sobrien#ifdef SIGSYNCH 1728167465Smp struct sigaction osa, nsa; 172959243Sobrien#endif /* SIGSYNCH */ 173059243Sobrien 173159243Sobrien /* 173259243Sobrien * A child will be uninterruptible only under very special conditions. 173359243Sobrien * Remember that the semantics of '&' is implemented by disconnecting the 173459243Sobrien * process from the tty so signals do not need to ignored just for '&'. 173559243Sobrien * Thus signals are set to default action for children unless: we have had 173659243Sobrien * an "onintr -" (then specifically ignored) we are not playing with 173759243Sobrien * signals (inherit action) 173859243Sobrien */ 173959243Sobrien if (setintr) 174059243Sobrien ignint = (tpgrp == -1 && (t->t_dflg & F_NOINTERRUPT)) 174159243Sobrien || (gointr && eq(gointr, STRminus)); 174259243Sobrien 174359243Sobrien /* 174459243Sobrien * Check for maximum nesting of 16 processes to avoid Forking loops 174559243Sobrien */ 174659243Sobrien if (child == 16) 174759243Sobrien stderror(ERR_NESTING, 16); 174859243Sobrien#ifdef SIGSYNCH 1749167465Smp nsa.sa_handler = synch_handler; 1750167465Smp sigfillset(&nsa.sa_mask); 1751167465Smp nsa.sa_flags = SA_RESTART; 1752167465Smp if (sigaction(SIGSYNCH, &nsa, &osa)) 1753167465Smp stderror(ERR_SYSTEM, "pfork: sigaction set", strerror(errno)); 175459243Sobrien#endif /* SIGSYNCH */ 175559243Sobrien /* 1756167465Smp * Hold pchild() until we have the process installed in our table. 175759243Sobrien */ 175859243Sobrien if (wanttty < 0) { 1759167465Smp pchild_disabled++; 1760167465Smp cleanup_push(&pchild_disabled, disabled_cleanup); 176159243Sobrien } 176259243Sobrien while ((pid = fork()) == -1) 176359243Sobrien if (setintr == 0) 176459243Sobrien (void) sleep(FORKSLEEP); 1765167465Smp else 176659243Sobrien stderror(ERR_NOPROC); 176759243Sobrien if (pid == 0) { 1768167465Smp (void)cleanup_push_mark(); /* Never to be popped */ 1769167465Smp pchild_disabled = 0; 177059243Sobrien settimes(); 177159243Sobrien pgrp = pcurrjob ? pcurrjob->p_jobid : getpid(); 177259243Sobrien pflushall(); 177359243Sobrien pcurrjob = NULL; 177459243Sobrien#if !defined(BSDTIMES) && !defined(_SEQUENT_) 177559243Sobrien timesdone = 0; 177659243Sobrien#endif /* !defined(BSDTIMES) && !defined(_SEQUENT_) */ 177759243Sobrien child++; 177859243Sobrien if (setintr) { 177959243Sobrien setintr = 0; /* until I think otherwise */ 178059243Sobrien /* 178159243Sobrien * Children just get blown away on SIGINT, SIGQUIT unless "onintr 178259243Sobrien * -" seen. 178359243Sobrien */ 178459243Sobrien (void) signal(SIGINT, ignint ? SIG_IGN : SIG_DFL); 178559243Sobrien (void) signal(SIGQUIT, ignint ? SIG_IGN : SIG_DFL); 178659243Sobrien#ifdef BSDJOBS 178759243Sobrien if (wanttty >= 0) { 178859243Sobrien /* make stoppable */ 178959243Sobrien (void) signal(SIGTSTP, SIG_DFL); 179059243Sobrien (void) signal(SIGTTIN, SIG_DFL); 179159243Sobrien (void) signal(SIGTTOU, SIG_DFL); 179259243Sobrien } 179359243Sobrien#endif /* BSDJOBS */ 1794167465Smp sigaction(SIGTERM, &parterm, NULL); 179559243Sobrien } 179659243Sobrien else if (tpgrp == -1 && (t->t_dflg & F_NOINTERRUPT)) { 179759243Sobrien (void) signal(SIGINT, SIG_IGN); 179859243Sobrien (void) signal(SIGQUIT, SIG_IGN); 179959243Sobrien } 180059243Sobrien#ifdef OREO 1801167465Smp signal(SIGIO, SIG_IGN); /* ignore SIGIO in child too */ 180259243Sobrien#endif /* OREO */ 180359243Sobrien 180459243Sobrien pgetty(wanttty, pgrp); 180559243Sobrien /* 180659243Sobrien * Nohup and nice apply only to NODE_COMMAND's but it would be nice 180759243Sobrien * (?!?) if you could say "nohup (foo;bar)" Then the parser would have 180859243Sobrien * to know about nice/nohup/time 180959243Sobrien */ 181059243Sobrien if (t->t_dflg & F_NOHUP) 181159243Sobrien (void) signal(SIGHUP, SIG_IGN); 181259243Sobrien if (t->t_dflg & F_NICE) { 181359243Sobrien int nval = SIGN_EXTEND_CHAR(t->t_nice); 1814145479Smp#ifdef HAVE_SETPRIORITY 181583098Smp if (setpriority(PRIO_PROCESS, 0, nval) == -1 && errno) 1816167465Smp stderror(ERR_SYSTEM, "setpriority", strerror(errno)); 1817145479Smp#else /* !HAVE_SETPRIORITY */ 181859243Sobrien (void) nice(nval); 1819145479Smp#endif /* !HAVE_SETPRIORITY */ 182059243Sobrien } 182159243Sobrien#ifdef F_VER 182259243Sobrien if (t->t_dflg & F_VER) { 182359243Sobrien tsetenv(STRSYSTYPE, t->t_systype ? STRbsd43 : STRsys53); 182459243Sobrien dohash(NULL, NULL); 182559243Sobrien } 182659243Sobrien#endif /* F_VER */ 182759243Sobrien#ifdef SIGSYNCH 182859243Sobrien /* rfw 8/89 now parent can continue */ 182959243Sobrien if (kill(getppid(), SIGSYNCH)) 183059243Sobrien stderror(ERR_SYSTEM, "pfork child: kill", strerror(errno)); 183159243Sobrien#endif /* SIGSYNCH */ 183259243Sobrien 183359243Sobrien } 183459243Sobrien else { 183559243Sobrien#ifdef POSIXJOBS 183659243Sobrien if (wanttty >= 0) { 183759243Sobrien /* 183859243Sobrien * `Walking' process group fix from Beto Appleton. 183959243Sobrien * (beto@aixwiz.austin.ibm.com) 184059243Sobrien * If setpgid fails at this point that means that 184159243Sobrien * our process leader has died. We flush the current 184259243Sobrien * job and become the process leader ourselves. 184359243Sobrien * The parent will figure that out later. 184459243Sobrien */ 184559243Sobrien pgrp = pcurrjob ? pcurrjob->p_jobid : pid; 184659243Sobrien if (setpgid(pid, pgrp) == -1 && errno == EPERM) { 184759243Sobrien pcurrjob = NULL; 184859243Sobrien /* 184959243Sobrien * We don't care if this causes an error here; 185059243Sobrien * then we are already in the right process group 185159243Sobrien */ 185259243Sobrien (void) setpgid(pid, pgrp = pid); 185359243Sobrien } 185459243Sobrien } 185559243Sobrien#endif /* POSIXJOBS */ 185659243Sobrien palloc(pid, t); 185759243Sobrien#ifdef SIGSYNCH 1858167465Smp { 1859167465Smp sigset_t pause_mask; 1860167465Smp 186159243Sobrien /* 186259243Sobrien * rfw 8/89 Wait for child to own terminal. Solves half of ugly 186359243Sobrien * synchronization problem. With this change, we know that the only 186459243Sobrien * reason setpgrp to a previous process in a pipeline can fail is that 186559243Sobrien * the previous process has already exited. Without this hack, he may 186659243Sobrien * either have exited or not yet started to run. Two uglies become 186759243Sobrien * one. 186859243Sobrien */ 1869167465Smp sigprocmask(SIG_BLOCK, NULL, &pause); 1870167465Smp sigdelset(&pause_mask, SIGCHLD); 1871167465Smp sigdelset(&pause_mask, SIGSYNCH); 1872167465Smp sigsuspend(&pause_mask); 1873167465Smp handle_pending_signals(); 1874167465Smp if (sigaction(SIGSYNCH, &osa, NULL)) 1875167465Smp stderror(ERR_SYSTEM, "pfork parent: sigaction restore", 1876167465Smp strerror(errno)); 1877167465Smp } 187859243Sobrien#endif /* SIGSYNCH */ 187959243Sobrien 1880167465Smp if (wanttty < 0) 1881167465Smp cleanup_until(&pchild_disabled); 188259243Sobrien } 188359243Sobrien return (pid); 188459243Sobrien} 188559243Sobrien 188659243Sobrienstatic void 1887167465Smpokpcntl(void) 188859243Sobrien{ 188959243Sobrien if (tpgrp == -1) 189059243Sobrien stderror(ERR_JOBCONTROL); 189159243Sobrien if (tpgrp == 0) 189259243Sobrien stderror(ERR_JOBCTRLSUB); 189359243Sobrien} 189459243Sobrien 189559243Sobrien 189659243Sobrienstatic void 1897167465Smpsetttypgrp(int pgrp) 189859243Sobrien{ 189959243Sobrien /* 190059243Sobrien * If we are piping out a builtin, eg. 'echo | more' things can go 190159243Sobrien * out of sequence, i.e. the more can run before the echo. This 190259243Sobrien * can happen even if we have vfork, since the echo will be forked 190359243Sobrien * with the regular fork. In this case, we need to set the tty 190459243Sobrien * pgrp ourselves. If that happens, then the process will be still 190559243Sobrien * alive. And the tty process group will already be set. 190659243Sobrien * This should fix the famous sequent problem as a side effect: 190759243Sobrien * The controlling terminal is lost if all processes in the 190859243Sobrien * terminal process group are zombies. In this case tcgetpgrp() 190959243Sobrien * returns 0. If this happens we must set the terminal process 191059243Sobrien * group again. 191159243Sobrien */ 191259243Sobrien if (tcgetpgrp(FSHTTY) != pgrp) { 191359243Sobrien#ifdef POSIXJOBS 1914167465Smp struct sigaction old; 1915167465Smp 191659243Sobrien /* 191759243Sobrien * tcsetpgrp will set SIGTTOU to all the the processes in 191859243Sobrien * the background according to POSIX... We ignore this here. 191959243Sobrien */ 1920167465Smp sigaction(SIGTTOU, NULL, &old); 1921167465Smp signal(SIGTTOU, SIG_IGN); 192259243Sobrien#endif 192359243Sobrien (void) tcsetpgrp(FSHTTY, pgrp); 192459243Sobrien# ifdef POSIXJOBS 1925167465Smp sigaction(SIGTTOU, &old, NULL); 192659243Sobrien# endif 192759243Sobrien 192859243Sobrien } 192959243Sobrien} 193059243Sobrien 193159243Sobrien 193259243Sobrien/* 193359243Sobrien * if we don't have vfork(), things can still go in the wrong order 193459243Sobrien * resulting in the famous 'Stopped (tty output)'. But some systems 193559243Sobrien * don't permit the setpgid() call, (these are more recent secure 193659243Sobrien * systems such as ibm's aix), when they do. Then we'd rather print 193759243Sobrien * an error message than hang the shell! 193859243Sobrien * I am open to suggestions how to fix that. 193959243Sobrien */ 194059243Sobrienvoid 1941167465Smppgetty(int wanttty, pid_t pgrp) 194259243Sobrien{ 194359243Sobrien#ifdef BSDJOBS 1944167465Smp# ifdef POSIXJOBS 1945167465Smp sigset_t oset, set; 1946167465Smp# endif /* POSIXJOBS */ 194759243Sobrien 1948167465Smp jobdebug_xprintf(("wanttty %d pid %d opgrp%d pgrp %d tpgrp %d\n", 1949167465Smp wanttty, (int)getpid(), (int)pgrp, (int)mygetpgrp(), 1950167465Smp (int)tcgetpgrp(FSHTTY))); 195159243Sobrien# ifdef POSIXJOBS 195259243Sobrien /* 195359243Sobrien * christos: I am blocking the tty signals till I've set things 195459243Sobrien * correctly.... 195559243Sobrien */ 1956167465Smp if (wanttty > 0) { 1957167465Smp sigemptyset(&set); 1958167465Smp sigaddset(&set, SIGTSTP); 1959167465Smp sigaddset(&set, SIGTTIN); 1960167465Smp (void)sigprocmask(SIG_BLOCK, &set, &oset); 1961167465Smp cleanup_push(&oset, sigprocmask_cleanup); 196259243Sobrien } 196359243Sobrien# endif /* POSIXJOBS */ 196459243Sobrien 196559243Sobrien# ifndef POSIXJOBS 196659243Sobrien if (wanttty > 0) 196759243Sobrien setttypgrp(pgrp); 196859243Sobrien# endif /* !POSIXJOBS */ 196959243Sobrien 197059243Sobrien /* 197159243Sobrien * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de> 197259243Sobrien * Don't check for tpgrp >= 0 so even non-interactive shells give 197359243Sobrien * background jobs process groups Same for the comparison in the other part 197459243Sobrien * of the #ifdef 197559243Sobrien */ 197659243Sobrien if (wanttty >= 0) { 197759243Sobrien if (setpgid(0, pgrp) == -1) { 197859243Sobrien# ifdef POSIXJOBS 197959243Sobrien /* Walking process group fix; see above */ 198059243Sobrien if (setpgid(0, pgrp = getpid()) == -1) { 198159243Sobrien# endif /* POSIXJOBS */ 198259243Sobrien stderror(ERR_SYSTEM, "setpgid child:\n", strerror(errno)); 198359243Sobrien xexit(0); 198459243Sobrien# ifdef POSIXJOBS 198559243Sobrien } 198659243Sobrien wanttty = pgrp; /* Now we really want the tty, since we became the 198759243Sobrien * the process group leader 198859243Sobrien */ 198959243Sobrien# endif /* POSIXJOBS */ 199059243Sobrien } 199159243Sobrien } 199259243Sobrien 199359243Sobrien# ifdef POSIXJOBS 1994167465Smp if (wanttty > 0) { 199559243Sobrien setttypgrp(pgrp); 1996167465Smp cleanup_until(&oset); 1997167465Smp } 199859243Sobrien# endif /* POSIXJOBS */ 199959243Sobrien 2000167465Smp jobdebug_xprintf(("wanttty %d pid %d pgrp %d tpgrp %d\n", 2001167465Smp wanttty, getpid(), mygetpgrp(), tcgetpgrp(FSHTTY))); 200259243Sobrien 200359243Sobrien if (tpgrp > 0) 200459243Sobrien tpgrp = 0; /* gave tty away */ 200559243Sobrien#endif /* BSDJOBS */ 200659243Sobrien} 2007