| GCC Code Coverage Report | |||||||||||||||||||||
        
  | 
    |||||||||||||||||||||
| Line | Branch | Exec | Source | 
1  | 
    /* $OpenBSD: jobs.c,v 1.55 2016/03/17 23:33:23 mmcc Exp $ */  | 
    ||
2  | 
    |||
3  | 
    /*  | 
    ||
4  | 
    * Process and job control  | 
    ||
5  | 
    */  | 
    ||
6  | 
    |||
7  | 
    /*  | 
    ||
8  | 
    * Reworked/Rewritten version of Eric Gisin's/Ron Natalie's code by  | 
    ||
9  | 
    * Larry Bouzane (larry@cs.mun.ca) and hacked again by  | 
    ||
10  | 
    * Michael Rendell (michael@cs.mun.ca)  | 
    ||
11  | 
    *  | 
    ||
12  | 
    * The interface to the rest of the shell should probably be changed  | 
    ||
13  | 
    * to allow use of vfork() when available but that would be way too much  | 
    ||
14  | 
    * work :)  | 
    ||
15  | 
    *  | 
    ||
16  | 
    */  | 
    ||
17  | 
    |||
18  | 
    #include <sys/resource.h>  | 
    ||
19  | 
    #include <sys/stat.h>  | 
    ||
20  | 
    #include <sys/time.h>  | 
    ||
21  | 
    #include <sys/wait.h>  | 
    ||
22  | 
    |||
23  | 
    #include <ctype.h>  | 
    ||
24  | 
    #include <errno.h>  | 
    ||
25  | 
    #include <fcntl.h>  | 
    ||
26  | 
    #include <limits.h>  | 
    ||
27  | 
    #include <stdlib.h>  | 
    ||
28  | 
    #include <string.h>  | 
    ||
29  | 
    #include <unistd.h>  | 
    ||
30  | 
    |||
31  | 
    #include "sh.h"  | 
    ||
32  | 
    #include "tty.h"  | 
    ||
33  | 
    |||
34  | 
    /* Order important! */  | 
    ||
35  | 
    #define PRUNNING 0  | 
    ||
36  | 
    #define PEXITED 1  | 
    ||
37  | 
    #define PSIGNALLED 2  | 
    ||
38  | 
    #define PSTOPPED 3  | 
    ||
39  | 
    |||
40  | 
    typedef struct proc Proc;  | 
    ||
41  | 
    struct proc { | 
    ||
42  | 
    Proc *next; /* next process in pipeline (if any) */  | 
    ||
43  | 
    int state;  | 
    ||
44  | 
    int status; /* wait status */  | 
    ||
45  | 
    pid_t pid; /* process id */  | 
    ||
46  | 
    char command[48]; /* process command string */  | 
    ||
47  | 
    };  | 
    ||
48  | 
    |||
49  | 
    /* Notify/print flag - j_print() argument */  | 
    ||
50  | 
    #define JP_NONE 0 /* don't print anything */  | 
    ||
51  | 
    #define JP_SHORT 1 /* print signals processes were killed by */  | 
    ||
52  | 
    #define JP_MEDIUM 2 /* print [job-num] -/+ command */  | 
    ||
53  | 
    #define JP_LONG 3 /* print [job-num] -/+ pid command */  | 
    ||
54  | 
    #define JP_PGRP 4 /* print pgrp */  | 
    ||
55  | 
    |||
56  | 
    /* put_job() flags */  | 
    ||
57  | 
    #define PJ_ON_FRONT 0 /* at very front */  | 
    ||
58  | 
    #define PJ_PAST_STOPPED 1 /* just past any stopped jobs */  | 
    ||
59  | 
    |||
60  | 
    /* Job.flags values */  | 
    ||
61  | 
    #define JF_STARTED 0x001 /* set when all processes in job are started */  | 
    ||
62  | 
    #define JF_WAITING 0x002 /* set if j_waitj() is waiting on job */  | 
    ||
63  | 
    #define JF_W_ASYNCNOTIFY 0x004 /* set if waiting and async notification ok */  | 
    ||
64  | 
    #define JF_XXCOM 0x008 /* set for `command` jobs */  | 
    ||
65  | 
    #define JF_FG 0x010 /* running in foreground (also has tty pgrp) */  | 
    ||
66  | 
    #define JF_SAVEDTTY 0x020 /* j->ttystate is valid */  | 
    ||
67  | 
    #define JF_CHANGED 0x040 /* process has changed state */  | 
    ||
68  | 
    #define JF_KNOWN 0x080 /* $! referenced */  | 
    ||
69  | 
    #define JF_ZOMBIE 0x100 /* known, unwaited process */  | 
    ||
70  | 
    #define JF_REMOVE 0x200 /* flagged for removal (j_jobs()/j_noityf()) */  | 
    ||
71  | 
    #define JF_USETTYMODE 0x400 /* tty mode saved if process exits normally */  | 
    ||
72  | 
    #define JF_SAVEDTTYPGRP 0x800 /* j->saved_ttypgrp is valid */  | 
    ||
73  | 
    |||
74  | 
    typedef struct job Job;  | 
    ||
75  | 
    struct job { | 
    ||
76  | 
    Job *next; /* next job in list */  | 
    ||
77  | 
    int job; /* job number: %n */  | 
    ||
78  | 
    int flags; /* see JF_* */  | 
    ||
79  | 
    int state; /* job state */  | 
    ||
80  | 
    int status; /* exit status of last process */  | 
    ||
81  | 
    pid_t pgrp; /* process group of job */  | 
    ||
82  | 
    pid_t ppid; /* pid of process that forked job */  | 
    ||
83  | 
    int age; /* number of jobs started */  | 
    ||
84  | 
    struct timeval systime; /* system time used by job */  | 
    ||
85  | 
    struct timeval usrtime; /* user time used by job */  | 
    ||
86  | 
    Proc *proc_list; /* process list */  | 
    ||
87  | 
    Proc *last_proc; /* last process in list */  | 
    ||
88  | 
    Coproc_id coproc_id; /* 0 or id of coprocess output pipe */  | 
    ||
89  | 
    #ifdef JOBS  | 
    ||
90  | 
    struct termios ttystate;/* saved tty state for stopped jobs */  | 
    ||
91  | 
    pid_t saved_ttypgrp; /* saved tty process group for stopped jobs */  | 
    ||
92  | 
    #endif /* JOBS */  | 
    ||
93  | 
    };  | 
    ||
94  | 
    |||
95  | 
    /* Flags for j_waitj() */  | 
    ||
96  | 
    #define JW_NONE 0x00  | 
    ||
97  | 
    #define JW_INTERRUPT 0x01 /* ^C will stop the wait */  | 
    ||
98  | 
    #define JW_ASYNCNOTIFY 0x02 /* asynchronous notification during wait ok */  | 
    ||
99  | 
    #define JW_STOPPEDWAIT 0x04 /* wait even if job stopped */  | 
    ||
100  | 
    |||
101  | 
    /* Error codes for j_lookup() */  | 
    ||
102  | 
    #define JL_OK 0  | 
    ||
103  | 
    #define JL_NOSUCH 1 /* no such job */  | 
    ||
104  | 
    #define JL_AMBIG 2 /* %foo or %?foo is ambiguous */  | 
    ||
105  | 
    #define JL_INVALID 3 /* non-pid, non-% job id */  | 
    ||
106  | 
    |||
107  | 
    static const char	*const lookup_msgs[] = { | 
    ||
108  | 
    null,  | 
    ||
109  | 
    "no such job",  | 
    ||
110  | 
    "ambiguous",  | 
    ||
111  | 
    "argument must be %job or process id",  | 
    ||
112  | 
    NULL  | 
    ||
113  | 
    };  | 
    ||
114  | 
    |||
115  | 
    struct timeval j_systime, j_usrtime; /* user and system time of last j_waitjed job */  | 
    ||
116  | 
    |||
117  | 
    static Job *job_list; /* job list */  | 
    ||
118  | 
    static Job *last_job;  | 
    ||
119  | 
    static Job *async_job;  | 
    ||
120  | 
    static pid_t async_pid;  | 
    ||
121  | 
    |||
122  | 
    static int nzombie; /* # of zombies owned by this process */  | 
    ||
123  | 
    int njobs; /* # of jobs started */  | 
    ||
124  | 
    static int child_max; /* CHILD_MAX */  | 
    ||
125  | 
    |||
126  | 
    |||
127  | 
    /* held_sigchld is set if sigchld occurs before a job is completely started */  | 
    ||
128  | 
    static volatile sig_atomic_t held_sigchld;  | 
    ||
129  | 
    |||
130  | 
    #ifdef JOBS  | 
    ||
131  | 
    static struct shf *shl_j;  | 
    ||
132  | 
    static int ttypgrp_ok; /* set if can use tty pgrps */  | 
    ||
133  | 
    static pid_t restore_ttypgrp = -1;  | 
    ||
134  | 
    static pid_t our_pgrp;  | 
    ||
135  | 
    static int const	tt_sigs[] = { SIGTSTP, SIGTTIN, SIGTTOU }; | 
    ||
136  | 
    #endif /* JOBS */  | 
    ||
137  | 
    |||
138  | 
    static void j_set_async(Job *);  | 
    ||
139  | 
    static void j_startjob(Job *);  | 
    ||
140  | 
    static int j_waitj(Job *, int, const char *);  | 
    ||
141  | 
    static void j_sigchld(int);  | 
    ||
142  | 
    static void j_print(Job *, int, struct shf *);  | 
    ||
143  | 
    static Job *j_lookup(const char *, int *);  | 
    ||
144  | 
    static Job *new_job(void);  | 
    ||
145  | 
    static Proc *new_proc(void);  | 
    ||
146  | 
    static void check_job(Job *);  | 
    ||
147  | 
    static void put_job(Job *, int);  | 
    ||
148  | 
    static void remove_job(Job *, const char *);  | 
    ||
149  | 
    static int kill_job(Job *, int);  | 
    ||
150  | 
    |||
151  | 
    /* initialize job control */  | 
    ||
152  | 
    void  | 
    ||
153  | 
    j_init(int mflagset)  | 
    ||
154  | 
    { | 
    ||
155  | 
    435536  | 
    child_max = CHILD_MAX; /* so syscon() isn't always being called */  | 
    |
156  | 
    |||
157  | 
    217768  | 
    sigemptyset(&sm_default);  | 
    |
158  | 
    217768  | 
    sigprocmask(SIG_SETMASK, &sm_default, NULL);  | 
    |
159  | 
    |||
160  | 
    217768  | 
    sigemptyset(&sm_sigchld);  | 
    |
161  | 
    217768  | 
    sigaddset(&sm_sigchld, SIGCHLD);  | 
    |
162  | 
    |||
163  | 
    217768  | 
    setsig(&sigtraps[SIGCHLD], j_sigchld,  | 
    |
164  | 
    SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);  | 
    ||
165  | 
    |||
166  | 
    #ifdef JOBS  | 
    ||
167  | 
    ✓✗✓✓ | 
    435536  | 
    if (!mflagset && Flag(FTALKING))  | 
    
168  | 
    3624  | 
    Flag(FMONITOR) = 1;  | 
    |
169  | 
    |||
170  | 
    /* shl_j is used to do asynchronous notification (used in  | 
    ||
171  | 
    * an interrupt handler, so need a distinct shf)  | 
    ||
172  | 
    */  | 
    ||
173  | 
    217768  | 
    shl_j = shf_fdopen(2, SHF_WR, NULL);  | 
    |
174  | 
    |||
175  | 
    ✓✓✗✓ | 
    431912  | 
    	if (Flag(FMONITOR) || Flag(FTALKING)) { | 
    
176  | 
    int i;  | 
    ||
177  | 
    |||
178  | 
    /* the TF_SHELL_USES test is a kludge that lets us know if  | 
    ||
179  | 
    * if the signals have been changed by the shell.  | 
    ||
180  | 
    */  | 
    ||
181  | 
    ✓✓ | 28992  | 
    		for (i = NELEM(tt_sigs); --i >= 0; ) { | 
    
182  | 
    10872  | 
    sigtraps[tt_sigs[i]].flags |= TF_SHELL_USES;  | 
    |
183  | 
    /* j_change() sets this to SS_RESTORE_DFL if FMONITOR */  | 
    ||
184  | 
    10872  | 
    setsig(&sigtraps[tt_sigs[i]], SIG_IGN,  | 
    |
185  | 
    SS_RESTORE_IGN|SS_FORCE);  | 
    ||
186  | 
    }  | 
    ||
187  | 
    3624  | 
    }  | 
    |
188  | 
    |||
189  | 
    /* j_change() calls tty_init() */  | 
    ||
190  | 
    ✓✓ | 217768  | 
    if (Flag(FMONITOR))  | 
    
191  | 
    3624  | 
    j_change();  | 
    |
192  | 
    else  | 
    ||
193  | 
    #endif /* JOBS */  | 
    ||
194  | 
    ✗✓ | 214144  | 
    if (Flag(FTALKING))  | 
    
195  | 
    tty_init(true);  | 
    ||
196  | 
    217768  | 
    }  | 
    |
197  | 
    |||
198  | 
    /* suspend the shell */  | 
    ||
199  | 
    void  | 
    ||
200  | 
    j_suspend(void)  | 
    ||
201  | 
    { | 
    ||
202  | 
    struct sigaction sa, osa;  | 
    ||
203  | 
    |||
204  | 
    /* Restore tty and pgrp. */  | 
    ||
205  | 
    	if (ttypgrp_ok) { | 
    ||
206  | 
    tcsetattr(tty_fd, TCSADRAIN, &tty_state);  | 
    ||
207  | 
    		if (restore_ttypgrp >= 0) { | 
    ||
208  | 
    			if (tcsetpgrp(tty_fd, restore_ttypgrp) < 0) { | 
    ||
209  | 
    warningf(false,  | 
    ||
210  | 
    "j_suspend: tcsetpgrp() failed: %s",  | 
    ||
211  | 
    strerror(errno));  | 
    ||
212  | 
    			} else { | 
    ||
213  | 
    				if (setpgid(0, restore_ttypgrp) < 0) { | 
    ||
214  | 
    warningf(false,  | 
    ||
215  | 
    "j_suspend: setpgid() failed: %s",  | 
    ||
216  | 
    strerror(errno));  | 
    ||
217  | 
    }  | 
    ||
218  | 
    }  | 
    ||
219  | 
    }  | 
    ||
220  | 
    }  | 
    ||
221  | 
    |||
222  | 
    /* Suspend the shell. */  | 
    ||
223  | 
    memset(&sa, 0, sizeof(sa));  | 
    ||
224  | 
    sigemptyset(&sa.sa_mask);  | 
    ||
225  | 
    sa.sa_handler = SIG_DFL;  | 
    ||
226  | 
    sigaction(SIGTSTP, &sa, &osa);  | 
    ||
227  | 
    kill(0, SIGTSTP);  | 
    ||
228  | 
    |||
229  | 
    /* Back from suspend, reset signals, pgrp and tty. */  | 
    ||
230  | 
    sigaction(SIGTSTP, &osa, NULL);  | 
    ||
231  | 
    	if (ttypgrp_ok) { | 
    ||
232  | 
    		if (restore_ttypgrp >= 0) { | 
    ||
233  | 
    			if (setpgid(0, kshpid) < 0) { | 
    ||
234  | 
    warningf(false,  | 
    ||
235  | 
    "j_suspend: setpgid() failed: %s",  | 
    ||
236  | 
    strerror(errno));  | 
    ||
237  | 
    ttypgrp_ok = 0;  | 
    ||
238  | 
    			} else { | 
    ||
239  | 
    				if (tcsetpgrp(tty_fd, kshpid) < 0) { | 
    ||
240  | 
    warningf(false,  | 
    ||
241  | 
    "j_suspend: tcsetpgrp() failed: %s",  | 
    ||
242  | 
    strerror(errno));  | 
    ||
243  | 
    ttypgrp_ok = 0;  | 
    ||
244  | 
    }  | 
    ||
245  | 
    }  | 
    ||
246  | 
    }  | 
    ||
247  | 
    tty_init(true);  | 
    ||
248  | 
    }  | 
    ||
249  | 
    }  | 
    ||
250  | 
    |||
251  | 
    /* job cleanup before shell exit */  | 
    ||
252  | 
    void  | 
    ||
253  | 
    j_exit(void)  | 
    ||
254  | 
    { | 
    ||
255  | 
    /* kill stopped, and possibly running, jobs */  | 
    ||
256  | 
    Job *j;  | 
    ||
257  | 
    int killed = 0;  | 
    ||
258  | 
    |||
259  | 
    ✓✓ | 425772  | 
    	for (j = job_list; j != NULL; j = j->next) { | 
    
260  | 
    ✓✗✗✗ | 
    2037  | 
    if (j->ppid == procpid &&  | 
    
261  | 
    ✓✗ | 2037  | 
    (j->state == PSTOPPED ||  | 
    
262  | 
    ✓✓ | 2037  | 
    (j->state == PRUNNING &&  | 
    
263  | 
    ✓✗ | 40  | 
    ((j->flags & JF_FG) ||  | 
    
264  | 
    ✗✓ | 40  | 
    		    (Flag(FLOGIN) && !Flag(FNOHUP) && procpid == kshpid))))) { | 
    
265  | 
    killed = 1;  | 
    ||
266  | 
    if (j->pgrp == 0)  | 
    ||
267  | 
    kill_job(j, SIGHUP);  | 
    ||
268  | 
    else  | 
    ||
269  | 
    killpg(j->pgrp, SIGHUP);  | 
    ||
270  | 
    #ifdef JOBS  | 
    ||
271  | 
    			if (j->state == PSTOPPED) { | 
    ||
272  | 
    if (j->pgrp == 0)  | 
    ||
273  | 
    kill_job(j, SIGCONT);  | 
    ||
274  | 
    else  | 
    ||
275  | 
    killpg(j->pgrp, SIGCONT);  | 
    ||
276  | 
    }  | 
    ||
277  | 
    #endif /* JOBS */  | 
    ||
278  | 
    }  | 
    ||
279  | 
    }  | 
    ||
280  | 
    ✗✓ | 140566  | 
    if (killed)  | 
    
281  | 
    sleep(1);  | 
    ||
282  | 
    140566  | 
    j_notify();  | 
    |
283  | 
    |||
284  | 
    #ifdef JOBS  | 
    ||
285  | 
    ✓✓ | 140566  | 
    	if (kshpid == procpid && restore_ttypgrp >= 0) { | 
    
286  | 
    /* Need to restore the tty pgrp to what it was when the  | 
    ||
287  | 
    * shell started up, so that the process that started us  | 
    ||
288  | 
    * will be able to access the tty when we are done.  | 
    ||
289  | 
    * Also need to restore our process group in case we are  | 
    ||
290  | 
    * about to do an exec so that both our parent and the  | 
    ||
291  | 
    * process we are to become will be able to access the tty.  | 
    ||
292  | 
    */  | 
    ||
293  | 
    775  | 
    tcsetpgrp(tty_fd, restore_ttypgrp);  | 
    |
294  | 
    775  | 
    setpgid(0, restore_ttypgrp);  | 
    |
295  | 
    775  | 
    }  | 
    |
296  | 
    ✓✓ | 140566  | 
    	if (Flag(FMONITOR)) { | 
    
297  | 
    3518  | 
    Flag(FMONITOR) = 0;  | 
    |
298  | 
    3518  | 
    j_change();  | 
    |
299  | 
    3518  | 
    }  | 
    |
300  | 
    #endif /* JOBS */  | 
    ||
301  | 
    140566  | 
    }  | 
    |
302  | 
    |||
303  | 
    #ifdef JOBS  | 
    ||
304  | 
    /* turn job control on or off according to Flag(FMONITOR) */  | 
    ||
305  | 
    void  | 
    ||
306  | 
    j_change(void)  | 
    ||
307  | 
    { | 
    ||
308  | 
    int i;  | 
    ||
309  | 
    |||
310  | 
    ✓✓ | 14284  | 
    	if (Flag(FMONITOR)) { | 
    
311  | 
    int use_tty;  | 
    ||
312  | 
    |||
313  | 
    ✓✗ | 3624  | 
    		if (Flag(FTALKING)) { | 
    
314  | 
    /* Don't call tcgetattr() 'til we own the tty process group */  | 
    ||
315  | 
    use_tty = 1;  | 
    ||
316  | 
    3624  | 
    tty_init(false);  | 
    |
317  | 
    3624  | 
    } else  | 
    |
318  | 
    use_tty = 0;  | 
    ||
319  | 
    |||
320  | 
    /* no controlling tty, no SIGT* */  | 
    ||
321  | 
    ✓✗ | 10872  | 
    ttypgrp_ok = use_tty && tty_fd >= 0 && tty_devtty;  | 
    
322  | 
    |||
323  | 
    ✓✗✗✓ | 
    7248  | 
    		if (ttypgrp_ok && (our_pgrp = getpgrp()) < 0) { | 
    
324  | 
    warningf(false, "j_init: getpgrp() failed: %s",  | 
    ||
325  | 
    strerror(errno));  | 
    ||
326  | 
    ttypgrp_ok = 0;  | 
    ||
327  | 
    }  | 
    ||
328  | 
    ✓✗ | 3624  | 
    		if (ttypgrp_ok) { | 
    
329  | 
    3624  | 
    setsig(&sigtraps[SIGTTIN], SIG_DFL,  | 
    |
330  | 
    SS_RESTORE_ORIG|SS_FORCE);  | 
    ||
331  | 
    /* wait to be given tty (POSIX.1, B.2, job control) */  | 
    ||
332  | 
    3624  | 
    			while (1) { | 
    |
333  | 
    pid_t ttypgrp;  | 
    ||
334  | 
    |||
335  | 
    ✗✓ | 3624  | 
    				if ((ttypgrp = tcgetpgrp(tty_fd)) < 0) { | 
    
336  | 
    warningf(false,  | 
    ||
337  | 
    "j_init: tcgetpgrp() failed: %s",  | 
    ||
338  | 
    strerror(errno));  | 
    ||
339  | 
    ttypgrp_ok = 0;  | 
    ||
340  | 
    break;  | 
    ||
341  | 
    }  | 
    ||
342  | 
    ✓✗ | 3624  | 
    if (ttypgrp == our_pgrp)  | 
    
343  | 
    3624  | 
    break;  | 
    |
344  | 
    kill(0, SIGTTIN);  | 
    ||
345  | 
    }  | 
    ||
346  | 
    }  | 
    ||
347  | 
    ✓✓ | 28992  | 
    for (i = NELEM(tt_sigs); --i >= 0; )  | 
    
348  | 
    10872  | 
    setsig(&sigtraps[tt_sigs[i]], SIG_IGN,  | 
    |
349  | 
    SS_RESTORE_DFL|SS_FORCE);  | 
    ||
350  | 
    ✓✗✓✓ | 
    7248  | 
    		if (ttypgrp_ok && our_pgrp != kshpid) { | 
    
351  | 
    ✗✓ | 875  | 
    			if (setpgid(0, kshpid) < 0) { | 
    
352  | 
    warningf(false,  | 
    ||
353  | 
    "j_init: setpgid() failed: %s",  | 
    ||
354  | 
    strerror(errno));  | 
    ||
355  | 
    ttypgrp_ok = 0;  | 
    ||
356  | 
    			} else { | 
    ||
357  | 
    ✗✓ | 875  | 
    				if (tcsetpgrp(tty_fd, kshpid) < 0) { | 
    
358  | 
    warningf(false,  | 
    ||
359  | 
    "j_init: tcsetpgrp() failed: %s",  | 
    ||
360  | 
    strerror(errno));  | 
    ||
361  | 
    ttypgrp_ok = 0;  | 
    ||
362  | 
    } else  | 
    ||
363  | 
    875  | 
    restore_ttypgrp = our_pgrp;  | 
    |
364  | 
    875  | 
    our_pgrp = kshpid;  | 
    |
365  | 
    }  | 
    ||
366  | 
    }  | 
    ||
367  | 
    ✗✓ | 7248  | 
    		if (use_tty) { | 
    
368  | 
    3624  | 
    if (!ttypgrp_ok)  | 
    |
369  | 
    warningf(false, "warning: won't have full job control");  | 
    ||
370  | 
    }  | 
    ||
371  | 
    ✓✗ | 3624  | 
    if (tty_fd >= 0)  | 
    
372  | 
    3624  | 
    tcgetattr(tty_fd, &tty_state);  | 
    |
373  | 
    3624  | 
    	} else { | 
    |
374  | 
    3518  | 
    ttypgrp_ok = 0;  | 
    |
375  | 
    ✓✓ | 3518  | 
    if (Flag(FTALKING))  | 
    
376  | 
    ✓✓ | 24612  | 
    for (i = NELEM(tt_sigs); --i >= 0; )  | 
    
377  | 
    10548  | 
    setsig(&sigtraps[tt_sigs[i]], SIG_IGN,  | 
    |
378  | 
    SS_RESTORE_IGN|SS_FORCE);  | 
    ||
379  | 
    else  | 
    ||
380  | 
    ✓✓ | 8  | 
    			for (i = NELEM(tt_sigs); --i >= 0; ) { | 
    
381  | 
    ✓✗ | 6  | 
    if (sigtraps[tt_sigs[i]].flags &  | 
    
382  | 
    (TF_ORIG_IGN | TF_ORIG_DFL))  | 
    ||
383  | 
    setsig(&sigtraps[tt_sigs[i]],  | 
    ||
384  | 
    (sigtraps[tt_sigs[i]].flags & TF_ORIG_IGN) ?  | 
    ||
385  | 
    SIG_IGN : SIG_DFL,  | 
    ||
386  | 
    SS_RESTORE_ORIG|SS_FORCE);  | 
    ||
387  | 
    }  | 
    ||
388  | 
    ✓✓ | 3518  | 
    if (!Flag(FTALKING))  | 
    
389  | 
    2  | 
    tty_close();  | 
    |
390  | 
    }  | 
    ||
391  | 
    7142  | 
    }  | 
    |
392  | 
    #endif /* JOBS */  | 
    ||
393  | 
    |||
394  | 
    /* execute tree in child subprocess */  | 
    ||
395  | 
    int  | 
    ||
396  | 
    exchild(struct op *t, int flags, volatile int *xerrok,  | 
    ||
397  | 
    int close_fd) /* used if XPCLOSE or XCCLOSE */  | 
    ||
398  | 
    { | 
    ||
399  | 
    static Proc *last_proc; /* for pipelines */  | 
    ||
400  | 
    |||
401  | 
    int i;  | 
    ||
402  | 
    14414766  | 
    sigset_t omask;  | 
    |
403  | 
    Proc *p;  | 
    ||
404  | 
    Job *j;  | 
    ||
405  | 
    int rv = 0;  | 
    ||
406  | 
    int forksleep;  | 
    ||
407  | 
    int ischild;  | 
    ||
408  | 
    |||
409  | 
    ✗✓ | 7207383  | 
    if (flags & XEXEC)  | 
    
410  | 
    /* Clear XFORK|XPCLOSE|XCCLOSE|XCOPROC|XPIPEO|XPIPEI|XXCOM|XBGND  | 
    ||
411  | 
    * (also done in another execute() below)  | 
    ||
412  | 
    */  | 
    ||
413  | 
    return execute(t, flags & (XEXEC | XERROK), xerrok);  | 
    ||
414  | 
    |||
415  | 
    /* no SIGCHLD's while messing with job and process lists */  | 
    ||
416  | 
    7207383  | 
    sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);  | 
    |
417  | 
    |||
418  | 
    7207383  | 
    p = new_proc();  | 
    |
419  | 
    7207383  | 
    p->next = NULL;  | 
    |
420  | 
    7207383  | 
    p->state = PRUNNING;  | 
    |
421  | 
    7207383  | 
    p->status = 0;  | 
    |
422  | 
    7207383  | 
    p->pid = 0;  | 
    |
423  | 
    |||
424  | 
    /* link process into jobs list */  | 
    ||
425  | 
    ✓✓ | 7207383  | 
    	if (flags&XPIPEI) {	/* continuing with a pipe */ | 
    
426  | 
    ✗✓ | 2663379  | 
    if (!last_job)  | 
    
427  | 
    internal_errorf(1,  | 
    ||
428  | 
    "exchild: XPIPEI and no last_job - pid %d",  | 
    ||
429  | 
    (int) procpid);  | 
    ||
430  | 
    2663379  | 
    j = last_job;  | 
    |
431  | 
    2663379  | 
    last_proc->next = p;  | 
    |
432  | 
    2663379  | 
    last_proc = p;  | 
    |
433  | 
    2663379  | 
    	} else { | 
    |
434  | 
    4544004  | 
    j = new_job(); /* fills in j->job */  | 
    |
435  | 
    /* we don't consider XXCOM's foreground since they don't get  | 
    ||
436  | 
    * tty process group and we don't save or restore tty modes.  | 
    ||
437  | 
    */  | 
    ||
438  | 
    ✓✓ | 10652462  | 
    j->flags = (flags & XXCOM) ? JF_XXCOM :  | 
    
439  | 
    1564454  | 
    ((flags & XBGND) ? 0 : (JF_FG|JF_USETTYMODE));  | 
    |
440  | 
    4544004  | 
    timerclear(&j->usrtime);  | 
    |
441  | 
    4544004  | 
    timerclear(&j->systime);  | 
    |
442  | 
    4544004  | 
    j->state = PRUNNING;  | 
    |
443  | 
    4544004  | 
    j->pgrp = 0;  | 
    |
444  | 
    4544004  | 
    j->ppid = procpid;  | 
    |
445  | 
    4544004  | 
    j->age = ++njobs;  | 
    |
446  | 
    4544004  | 
    j->proc_list = p;  | 
    |
447  | 
    4544004  | 
    j->coproc_id = 0;  | 
    |
448  | 
    4544004  | 
    last_job = j;  | 
    |
449  | 
    4544004  | 
    last_proc = p;  | 
    |
450  | 
    4544004  | 
    put_job(j, PJ_PAST_STOPPED);  | 
    |
451  | 
    }  | 
    ||
452  | 
    |||
453  | 
    7207383  | 
    snptreef(p->command, sizeof(p->command), "%T", t);  | 
    |
454  | 
    |||
455  | 
    /* create child process */  | 
    ||
456  | 
    forksleep = 1;  | 
    ||
457  | 
    ✗✓✗✗ | 
    14414766  | 
    	while ((i = fork()) < 0 && errno == EAGAIN && forksleep < 32) { | 
    
458  | 
    if (intrsig) /* allow user to ^C out... */  | 
    ||
459  | 
    break;  | 
    ||
460  | 
    sleep(forksleep);  | 
    ||
461  | 
    forksleep <<= 1;  | 
    ||
462  | 
    }  | 
    ||
463  | 
    ✗✓ | 7207383  | 
    	if (i < 0) { | 
    
464  | 
    kill_job(j, SIGKILL);  | 
    ||
465  | 
    remove_job(j, "fork failed");  | 
    ||
466  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
467  | 
    		errorf("cannot fork - try again"); | 
    ||
468  | 
    }  | 
    ||
469  | 
    7207383  | 
    ischild = i == 0;  | 
    |
470  | 
    ✓✓ | 7207383  | 
    if (ischild)  | 
    
471  | 
    81965  | 
    p->pid = procpid = getpid();  | 
    |
472  | 
    else  | 
    ||
473  | 
    p->pid = i;  | 
    ||
474  | 
    |||
475  | 
    #ifdef JOBS  | 
    ||
476  | 
    /* job control set up */  | 
    ||
477  | 
    ✓✓✓✓ | 
    7208193  | 
    	if (Flag(FMONITOR) && !(flags&XXCOM)) { | 
    
478  | 
    int dotty = 0;  | 
    ||
479  | 
    ✓✓ | 765  | 
    		if (j->pgrp == 0) {	/* First process */ | 
    
480  | 
    671  | 
    j->pgrp = p->pid;  | 
    |
481  | 
    dotty = 1;  | 
    ||
482  | 
    671  | 
    }  | 
    |
483  | 
    |||
484  | 
    /* set pgrp in both parent and child to deal with race  | 
    ||
485  | 
    * condition  | 
    ||
486  | 
    */  | 
    ||
487  | 
    765  | 
    setpgid(p->pid, j->pgrp);  | 
    |
488  | 
    /* YYY: should this be  | 
    ||
489  | 
    if (ttypgrp_ok && ischild && !(flags&XBGND))  | 
    ||
490  | 
    tcsetpgrp(tty_fd, j->pgrp);  | 
    ||
491  | 
    instead? (see also YYY below)  | 
    ||
492  | 
    */  | 
    ||
493  | 
    ✓✓✓✗ | 
    1436  | 
    if (ttypgrp_ok && dotty && !(flags & XBGND))  | 
    
494  | 
    671  | 
    tcsetpgrp(tty_fd, j->pgrp);  | 
    |
495  | 
    765  | 
    }  | 
    |
496  | 
    #endif /* JOBS */  | 
    ||
497  | 
    |||
498  | 
    /* used to close pipe input fd */  | 
    ||
499  | 
    ✓✓✓✓ | 
    11873717  | 
    if (close_fd >= 0 && (((flags & XPCLOSE) && !ischild) ||  | 
    
500  | 
    ✓✓ | 2724078  | 
    ((flags & XCCLOSE) && ischild)))  | 
    
501  | 
    2002484  | 
    close(close_fd);  | 
    |
502  | 
    ✓✓ | 7207383  | 
    	if (ischild) {		/* child */ | 
    
503  | 
    /* Do this before restoring signal */  | 
    ||
504  | 
    ✗✓ | 81965  | 
    if (flags & XCOPROC)  | 
    
505  | 
    coproc_cleanup(false);  | 
    ||
506  | 
    81965  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    |
507  | 
    81965  | 
    cleanup_parents_env();  | 
    |
508  | 
    #ifdef JOBS  | 
    ||
509  | 
    /* If FMONITOR or FTALKING is set, these signals are ignored,  | 
    ||
510  | 
    * if neither FMONITOR nor FTALKING are set, the signals have  | 
    ||
511  | 
    * their inherited values.  | 
    ||
512  | 
    */  | 
    ||
513  | 
    ✓✓✓✓ | 
    82073  | 
    		if (Flag(FMONITOR) && !(flags & XXCOM)) { | 
    
514  | 
    ✓✓ | 824  | 
    for (i = NELEM(tt_sigs); --i >= 0; )  | 
    
515  | 
    309  | 
    setsig(&sigtraps[tt_sigs[i]], SIG_DFL,  | 
    |
516  | 
    SS_RESTORE_DFL|SS_FORCE);  | 
    ||
517  | 
    }  | 
    ||
518  | 
    #endif /* JOBS */  | 
    ||
519  | 
    ✗✓✗✗ | 
    81965  | 
    if (Flag(FBGNICE) && (flags & XBGND))  | 
    
520  | 
    nice(4);  | 
    ||
521  | 
    ✓✓ | 81965  | 
    		if ((flags & XBGND) && !Flag(FMONITOR)) { | 
    
522  | 
    456  | 
    setsig(&sigtraps[SIGINT], SIG_IGN,  | 
    |
523  | 
    SS_RESTORE_IGN|SS_FORCE);  | 
    ||
524  | 
    456  | 
    setsig(&sigtraps[SIGQUIT], SIG_IGN,  | 
    |
525  | 
    SS_RESTORE_IGN|SS_FORCE);  | 
    ||
526  | 
    ✓✗ | 456  | 
    			if (!(flags & (XPIPEI | XCOPROC))) { | 
    
527  | 
    456  | 
    				int fd = open("/dev/null", O_RDONLY); | 
    |
528  | 
    ✓✗ | 456  | 
    				if (fd != 0) { | 
    
529  | 
    456  | 
    (void) ksh_dup2(fd, 0, true);  | 
    |
530  | 
    456  | 
    close(fd);  | 
    |
531  | 
    456  | 
    }  | 
    |
532  | 
    456  | 
    }  | 
    |
533  | 
    }  | 
    ||
534  | 
    remove_job(j, "child"); /* in case of `jobs` command */  | 
    ||
535  | 
    nzombie = 0;  | 
    ||
536  | 
    #ifdef JOBS  | 
    ||
537  | 
    ttypgrp_ok = 0;  | 
    ||
538  | 
    Flag(FMONITOR) = 0;  | 
    ||
539  | 
    #endif /* JOBS */  | 
    ||
540  | 
    Flag(FTALKING) = 0;  | 
    ||
541  | 
    tty_close();  | 
    ||
542  | 
    cleartraps();  | 
    ||
543  | 
    execute(t, (flags & XERROK) | XEXEC, NULL); /* no return */  | 
    ||
544  | 
    internal_errorf(0, "exchild: execute() returned");  | 
    ||
545  | 
    unwind(LLEAVE);  | 
    ||
546  | 
    /* NOTREACHED */  | 
    ||
547  | 
    }  | 
    ||
548  | 
    |||
549  | 
    /* shell (parent) stuff */  | 
    ||
550  | 
    /* Ensure next child gets a (slightly) different $RANDOM sequence */  | 
    ||
551  | 
    7125418  | 
    change_random();  | 
    |
552  | 
    ✓✓ | 7125418  | 
    	if (!(flags & XPIPEO)) {	/* last process in a job */ | 
    
553  | 
    #ifdef JOBS  | 
    ||
554  | 
    /* YYY: Is this needed? (see also YYY above)  | 
    ||
555  | 
    if (Flag(FMONITOR) && !(flags&(XXCOM|XBGND)))  | 
    ||
556  | 
    tcsetpgrp(tty_fd, j->pgrp);  | 
    ||
557  | 
    */  | 
    ||
558  | 
    #endif /* JOBS */  | 
    ||
559  | 
    1510257  | 
    j_startjob(j);  | 
    |
560  | 
    ✗✓ | 1510257  | 
    		if (flags & XCOPROC) { | 
    
561  | 
    j->coproc_id = coproc.id;  | 
    ||
562  | 
    coproc.njobs++; /* n jobs using co-process output */  | 
    ||
563  | 
    coproc.job = (void *) j; /* j using co-process input */  | 
    ||
564  | 
    }  | 
    ||
565  | 
    ✓✓ | 1510257  | 
    		if (flags & XBGND) { | 
    
566  | 
    2363  | 
    j_set_async(j);  | 
    |
567  | 
    ✗✓ | 2363  | 
    			if (Flag(FTALKING)) { | 
    
568  | 
    shf_fprintf(shl_out, "[%d]", j->job);  | 
    ||
569  | 
    for (p = j->proc_list; p; p = p->next)  | 
    ||
570  | 
    shf_fprintf(shl_out, " %d", p->pid);  | 
    ||
571  | 
    				shf_putchar('\n', shl_out); | 
    ||
572  | 
    shf_flush(shl_out);  | 
    ||
573  | 
    }  | 
    ||
574  | 
    } else  | 
    ||
575  | 
    1507894  | 
    rv = j_waitj(j, JW_NONE, "jw:last proc");  | 
    |
576  | 
    }  | 
    ||
577  | 
    |||
578  | 
    7125418  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    |
579  | 
    |||
580  | 
    7125418  | 
    return rv;  | 
    |
581  | 
    7125418  | 
    }  | 
    |
582  | 
    |||
583  | 
    /* start the last job: only used for `command` jobs */  | 
    ||
584  | 
    void  | 
    ||
585  | 
    startlast(void)  | 
    ||
586  | 
    { | 
    ||
587  | 
    5903564  | 
    sigset_t omask;  | 
    |
588  | 
    |||
589  | 
    2951782  | 
    sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);  | 
    |
590  | 
    |||
591  | 
    ✓✗ | 2951782  | 
    	if (last_job) { /* no need to report error - waitlast() will do it */ | 
    
592  | 
    /* ensure it isn't removed by check_job() */  | 
    ||
593  | 
    2951782  | 
    last_job->flags |= JF_WAITING;  | 
    |
594  | 
    2951782  | 
    j_startjob(last_job);  | 
    |
595  | 
    2951782  | 
    }  | 
    |
596  | 
    2951782  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    |
597  | 
    2951782  | 
    }  | 
    |
598  | 
    |||
599  | 
    /* wait for last job: only used for `command` jobs */  | 
    ||
600  | 
    int  | 
    ||
601  | 
    waitlast(void)  | 
    ||
602  | 
    { | 
    ||
603  | 
    int rv;  | 
    ||
604  | 
    Job *j;  | 
    ||
605  | 
    5903564  | 
    sigset_t omask;  | 
    |
606  | 
    |||
607  | 
    2951782  | 
    sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);  | 
    |
608  | 
    |||
609  | 
    2951782  | 
    j = last_job;  | 
    |
610  | 
    ✓✗✗✓ | 
    5903564  | 
    	if (!j || !(j->flags & JF_STARTED)) { | 
    
611  | 
    if (!j)  | 
    ||
612  | 
    warningf(true, "waitlast: no last job");  | 
    ||
613  | 
    else  | 
    ||
614  | 
    internal_errorf(0, "waitlast: not started");  | 
    ||
615  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
616  | 
    return 125; /* not so arbitrary, non-zero value */  | 
    ||
617  | 
    }  | 
    ||
618  | 
    |||
619  | 
    2951782  | 
    rv = j_waitj(j, JW_NONE, "jw:waitlast");  | 
    |
620  | 
    |||
621  | 
    2951782  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    |
622  | 
    |||
623  | 
    2951782  | 
    return rv;  | 
    |
624  | 
    2951782  | 
    }  | 
    |
625  | 
    |||
626  | 
    /* wait for child, interruptable. */  | 
    ||
627  | 
    int  | 
    ||
628  | 
    waitfor(const char *cp, int *sigp)  | 
    ||
629  | 
    { | 
    ||
630  | 
    int rv;  | 
    ||
631  | 
    Job *j;  | 
    ||
632  | 
    362  | 
    int ecode;  | 
    |
633  | 
    int flags = JW_INTERRUPT|JW_ASYNCNOTIFY;  | 
    ||
634  | 
    181  | 
    sigset_t omask;  | 
    |
635  | 
    |||
636  | 
    181  | 
    sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);  | 
    |
637  | 
    |||
638  | 
    181  | 
    *sigp = 0;  | 
    |
639  | 
    |||
640  | 
    ✓✓ | 181  | 
    	if (cp == NULL) { | 
    
641  | 
    /* wait for an unspecified job - always returns 0, so  | 
    ||
642  | 
    * don't have to worry about exited/signaled jobs  | 
    ||
643  | 
    */  | 
    ||
644  | 
    ✓✓ | 456  | 
    for (j = job_list; j; j = j->next)  | 
    
645  | 
    /* at&t ksh will wait for stopped jobs - we don't */  | 
    ||
646  | 
    ✓✗✓✓ | 
    228  | 
    if (j->ppid == procpid && j->state == PRUNNING)  | 
    
647  | 
    break;  | 
    ||
648  | 
    ✓✓ | 164  | 
    		if (!j) { | 
    
649  | 
    114  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    |
650  | 
    114  | 
    return -1;  | 
    |
651  | 
    }  | 
    ||
652  | 
    ✓✗ | 17  | 
    	} else if ((j = j_lookup(cp, &ecode))) { | 
    
653  | 
    /* don't report normal job completion */  | 
    ||
654  | 
    flags &= ~JW_ASYNCNOTIFY;  | 
    ||
655  | 
    ✗✓ | 17  | 
    		if (j->ppid != procpid) { | 
    
656  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
657  | 
    return -1;  | 
    ||
658  | 
    }  | 
    ||
659  | 
    	} else { | 
    ||
660  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
661  | 
    if (ecode != JL_NOSUCH)  | 
    ||
662  | 
    			bi_errorf("%s: %s", cp, lookup_msgs[ecode]); | 
    ||
663  | 
    return -1;  | 
    ||
664  | 
    }  | 
    ||
665  | 
    |||
666  | 
    /* at&t ksh will wait for stopped jobs - we don't */  | 
    ||
667  | 
    67  | 
    rv = j_waitj(j, flags, "jw:waitfor");  | 
    |
668  | 
    |||
669  | 
    67  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    |
670  | 
    |||
671  | 
    ✗✓ | 67  | 
    if (rv < 0) /* we were interrupted */  | 
    
672  | 
    *sigp = 128 + -rv;  | 
    ||
673  | 
    |||
674  | 
    67  | 
    return rv;  | 
    |
675  | 
    181  | 
    }  | 
    |
676  | 
    |||
677  | 
    /* kill (built-in) a job */  | 
    ||
678  | 
    int  | 
    ||
679  | 
    j_kill(const char *cp, int sig)  | 
    ||
680  | 
    { | 
    ||
681  | 
    Job *j;  | 
    ||
682  | 
    int rv = 0;  | 
    ||
683  | 
    int ecode;  | 
    ||
684  | 
    sigset_t omask;  | 
    ||
685  | 
    |||
686  | 
    sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);  | 
    ||
687  | 
    |||
688  | 
    	if ((j = j_lookup(cp, &ecode)) == NULL) { | 
    ||
689  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
690  | 
    		bi_errorf("%s: %s", cp, lookup_msgs[ecode]); | 
    ||
691  | 
    return 1;  | 
    ||
692  | 
    }  | 
    ||
693  | 
    |||
694  | 
    	if (j->pgrp == 0) {	/* started when !Flag(FMONITOR) */ | 
    ||
695  | 
    		if (kill_job(j, sig) < 0) { | 
    ||
696  | 
    			bi_errorf("%s: %s", cp, strerror(errno)); | 
    ||
697  | 
    rv = 1;  | 
    ||
698  | 
    }  | 
    ||
699  | 
    	} else { | 
    ||
700  | 
    #ifdef JOBS  | 
    ||
701  | 
    if (j->state == PSTOPPED && (sig == SIGTERM || sig == SIGHUP))  | 
    ||
702  | 
    (void) killpg(j->pgrp, SIGCONT);  | 
    ||
703  | 
    #endif /* JOBS */  | 
    ||
704  | 
    		if (killpg(j->pgrp, sig) < 0) { | 
    ||
705  | 
    			bi_errorf("%s: %s", cp, strerror(errno)); | 
    ||
706  | 
    rv = 1;  | 
    ||
707  | 
    }  | 
    ||
708  | 
    }  | 
    ||
709  | 
    |||
710  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
711  | 
    |||
712  | 
    return rv;  | 
    ||
713  | 
    }  | 
    ||
714  | 
    |||
715  | 
    #ifdef JOBS  | 
    ||
716  | 
    /* fg and bg built-ins: called only if Flag(FMONITOR) set */  | 
    ||
717  | 
    int  | 
    ||
718  | 
    j_resume(const char *cp, int bg)  | 
    ||
719  | 
    { | 
    ||
720  | 
    Job *j;  | 
    ||
721  | 
    Proc *p;  | 
    ||
722  | 
    int ecode;  | 
    ||
723  | 
    int running;  | 
    ||
724  | 
    int rv = 0;  | 
    ||
725  | 
    sigset_t omask;  | 
    ||
726  | 
    |||
727  | 
    sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);  | 
    ||
728  | 
    |||
729  | 
    	if ((j = j_lookup(cp, &ecode)) == NULL) { | 
    ||
730  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
731  | 
    		bi_errorf("%s: %s", cp, lookup_msgs[ecode]); | 
    ||
732  | 
    return 1;  | 
    ||
733  | 
    }  | 
    ||
734  | 
    |||
735  | 
    	if (j->pgrp == 0) { | 
    ||
736  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
737  | 
    		bi_errorf("job not job-controlled"); | 
    ||
738  | 
    return 1;  | 
    ||
739  | 
    }  | 
    ||
740  | 
    |||
741  | 
    if (bg)  | 
    ||
742  | 
    		shprintf("[%d] ", j->job); | 
    ||
743  | 
    |||
744  | 
    running = 0;  | 
    ||
745  | 
    	for (p = j->proc_list; p != NULL; p = p->next) { | 
    ||
746  | 
    		if (p->state == PSTOPPED) { | 
    ||
747  | 
    p->state = PRUNNING;  | 
    ||
748  | 
    p->status = 0;  | 
    ||
749  | 
    running = 1;  | 
    ||
750  | 
    }  | 
    ||
751  | 
    		shprintf("%s%s", p->command, p->next ? "| " : ""); | 
    ||
752  | 
    }  | 
    ||
753  | 
    	shprintf("\n"); | 
    ||
754  | 
    shf_flush(shl_stdout);  | 
    ||
755  | 
    if (running)  | 
    ||
756  | 
    j->state = PRUNNING;  | 
    ||
757  | 
    |||
758  | 
    put_job(j, PJ_PAST_STOPPED);  | 
    ||
759  | 
    if (bg)  | 
    ||
760  | 
    j_set_async(j);  | 
    ||
761  | 
    	else { | 
    ||
762  | 
    # ifdef JOBS  | 
    ||
763  | 
    /* attach tty to job */  | 
    ||
764  | 
    		if (j->state == PRUNNING) { | 
    ||
765  | 
    if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))  | 
    ||
766  | 
    tcsetattr(tty_fd, TCSADRAIN, &j->ttystate);  | 
    ||
767  | 
    /* See comment in j_waitj regarding saved_ttypgrp. */  | 
    ||
768  | 
    if (ttypgrp_ok &&  | 
    ||
769  | 
    tcsetpgrp(tty_fd, (j->flags & JF_SAVEDTTYPGRP) ?  | 
    ||
770  | 
    			    j->saved_ttypgrp : j->pgrp) < 0) { | 
    ||
771  | 
    if (j->flags & JF_SAVEDTTY)  | 
    ||
772  | 
    tcsetattr(tty_fd, TCSADRAIN, &tty_state);  | 
    ||
773  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
774  | 
    				bi_errorf("1st tcsetpgrp(%d, %d) failed: %s", | 
    ||
775  | 
    tty_fd,  | 
    ||
776  | 
    (int) ((j->flags & JF_SAVEDTTYPGRP) ?  | 
    ||
777  | 
    j->saved_ttypgrp : j->pgrp),  | 
    ||
778  | 
    strerror(errno));  | 
    ||
779  | 
    return 1;  | 
    ||
780  | 
    }  | 
    ||
781  | 
    }  | 
    ||
782  | 
    # endif /* JOBS */  | 
    ||
783  | 
    j->flags |= JF_FG;  | 
    ||
784  | 
    j->flags &= ~JF_KNOWN;  | 
    ||
785  | 
    if (j == async_job)  | 
    ||
786  | 
    async_job = NULL;  | 
    ||
787  | 
    }  | 
    ||
788  | 
    |||
789  | 
    	if (j->state == PRUNNING && killpg(j->pgrp, SIGCONT) < 0) { | 
    ||
790  | 
    int err = errno;  | 
    ||
791  | 
    |||
792  | 
    		if (!bg) { | 
    ||
793  | 
    j->flags &= ~JF_FG;  | 
    ||
794  | 
    # ifdef JOBS  | 
    ||
795  | 
    if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))  | 
    ||
796  | 
    tcsetattr(tty_fd, TCSADRAIN, &tty_state);  | 
    ||
797  | 
    			if (ttypgrp_ok && tcsetpgrp(tty_fd, our_pgrp) < 0) { | 
    ||
798  | 
    warningf(true,  | 
    ||
799  | 
    "fg: 2nd tcsetpgrp(%d, %d) failed: %s",  | 
    ||
800  | 
    tty_fd, (int) our_pgrp,  | 
    ||
801  | 
    strerror(errno));  | 
    ||
802  | 
    }  | 
    ||
803  | 
    # endif /* JOBS */  | 
    ||
804  | 
    }  | 
    ||
805  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
806  | 
    		bi_errorf("cannot continue job %s: %s", | 
    ||
807  | 
    cp, strerror(err));  | 
    ||
808  | 
    return 1;  | 
    ||
809  | 
    }  | 
    ||
810  | 
    	if (!bg) { | 
    ||
811  | 
    # ifdef JOBS  | 
    ||
812  | 
    		if (ttypgrp_ok) { | 
    ||
813  | 
    j->flags &= ~(JF_SAVEDTTY | JF_SAVEDTTYPGRP);  | 
    ||
814  | 
    }  | 
    ||
815  | 
    # endif /* JOBS */  | 
    ||
816  | 
    rv = j_waitj(j, JW_NONE, "jw:resume");  | 
    ||
817  | 
    }  | 
    ||
818  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
819  | 
    return rv;  | 
    ||
820  | 
    }  | 
    ||
821  | 
    #endif /* JOBS */  | 
    ||
822  | 
    |||
823  | 
    /* are there any running or stopped jobs ? */  | 
    ||
824  | 
    int  | 
    ||
825  | 
    j_stopped_running(void)  | 
    ||
826  | 
    { | 
    ||
827  | 
    Job *j;  | 
    ||
828  | 
    int which = 0;  | 
    ||
829  | 
    |||
830  | 
    ✓✓ | 10333  | 
    	for (j = job_list; j != NULL; j = j->next) { | 
    
831  | 
    #ifdef JOBS  | 
    ||
832  | 
    ✓✗✗✓ | 
    3508  | 
    if (j->ppid == procpid && j->state == PSTOPPED)  | 
    
833  | 
    which |= 1;  | 
    ||
834  | 
    #endif /* JOBS */  | 
    ||
835  | 
    ✗✓✗✗ ✗✗  | 
    1754  | 
    if (Flag(FLOGIN) && !Flag(FNOHUP) && procpid == kshpid &&  | 
    
836  | 
    j->ppid == procpid && j->state == PRUNNING)  | 
    ||
837  | 
    which |= 2;  | 
    ||
838  | 
    }  | 
    ||
839  | 
    ✗✓ | 2275  | 
    	if (which) { | 
    
840  | 
    		shellf("You have %s%s%s jobs\n", | 
    ||
841  | 
    which & 1 ? "stopped" : "",  | 
    ||
842  | 
    which == 3 ? " and " : "",  | 
    ||
843  | 
    which & 2 ? "running" : "");  | 
    ||
844  | 
    return 1;  | 
    ||
845  | 
    }  | 
    ||
846  | 
    |||
847  | 
    2275  | 
    return 0;  | 
    |
848  | 
    2275  | 
    }  | 
    |
849  | 
    |||
850  | 
    int  | 
    ||
851  | 
    j_njobs(void)  | 
    ||
852  | 
    { | 
    ||
853  | 
    Job *j;  | 
    ||
854  | 
    int nj = 0;  | 
    ||
855  | 
    sigset_t omask;  | 
    ||
856  | 
    |||
857  | 
    sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);  | 
    ||
858  | 
    for (j = job_list; j; j = j->next)  | 
    ||
859  | 
    nj++;  | 
    ||
860  | 
    |||
861  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
862  | 
    return nj;  | 
    ||
863  | 
    }  | 
    ||
864  | 
    |||
865  | 
    |||
866  | 
    /* list jobs for jobs built-in */  | 
    ||
867  | 
    int  | 
    ||
868  | 
    j_jobs(const char *cp, int slp,  | 
    ||
869  | 
    int nflag) /* 0: short, 1: long, 2: pgrp */  | 
    ||
870  | 
    { | 
    ||
871  | 
    Job *j, *tmp;  | 
    ||
872  | 
    int how;  | 
    ||
873  | 
    int zflag = 0;  | 
    ||
874  | 
    sigset_t omask;  | 
    ||
875  | 
    |||
876  | 
    sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);  | 
    ||
877  | 
    |||
878  | 
    	if (nflag < 0) { /* kludge: print zombies */ | 
    ||
879  | 
    nflag = 0;  | 
    ||
880  | 
    zflag = 1;  | 
    ||
881  | 
    }  | 
    ||
882  | 
    	if (cp) { | 
    ||
883  | 
    int ecode;  | 
    ||
884  | 
    |||
885  | 
    		if ((j = j_lookup(cp, &ecode)) == NULL) { | 
    ||
886  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
887  | 
    			bi_errorf("%s: %s", cp, lookup_msgs[ecode]); | 
    ||
888  | 
    return 1;  | 
    ||
889  | 
    }  | 
    ||
890  | 
    } else  | 
    ||
891  | 
    j = job_list;  | 
    ||
892  | 
    how = slp == 0 ? JP_MEDIUM : (slp == 1 ? JP_LONG : JP_PGRP);  | 
    ||
893  | 
    	for (; j; j = j->next) { | 
    ||
894  | 
    if ((!(j->flags & JF_ZOMBIE) || zflag) &&  | 
    ||
895  | 
    		    (!nflag || (j->flags & JF_CHANGED))) { | 
    ||
896  | 
    j_print(j, how, shl_stdout);  | 
    ||
897  | 
    if (j->state == PEXITED || j->state == PSIGNALLED)  | 
    ||
898  | 
    j->flags |= JF_REMOVE;  | 
    ||
899  | 
    }  | 
    ||
900  | 
    if (cp)  | 
    ||
901  | 
    break;  | 
    ||
902  | 
    }  | 
    ||
903  | 
    /* Remove jobs after printing so there won't be multiple + or - jobs */  | 
    ||
904  | 
    	for (j = job_list; j; j = tmp) { | 
    ||
905  | 
    tmp = j->next;  | 
    ||
906  | 
    if (j->flags & JF_REMOVE)  | 
    ||
907  | 
    remove_job(j, "jobs");  | 
    ||
908  | 
    }  | 
    ||
909  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
910  | 
    return 0;  | 
    ||
911  | 
    }  | 
    ||
912  | 
    |||
913  | 
    /* list jobs for top-level notification */  | 
    ||
914  | 
    void  | 
    ||
915  | 
    j_notify(void)  | 
    ||
916  | 
    { | 
    ||
917  | 
    Job *j, *tmp;  | 
    ||
918  | 
    296208  | 
    sigset_t omask;  | 
    |
919  | 
    |||
920  | 
    148104  | 
    sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);  | 
    |
921  | 
    ✓✓ | 300282  | 
    	for (j = job_list; j; j = j->next) { | 
    
922  | 
    #ifdef JOBS  | 
    ||
923  | 
    ✗✓✗✗ | 
    2037  | 
    if (Flag(FMONITOR) && (j->flags & JF_CHANGED))  | 
    
924  | 
    j_print(j, JP_MEDIUM, shl_out);  | 
    ||
925  | 
    #endif /* JOBS */  | 
    ||
926  | 
    /* Remove job after doing reports so there aren't  | 
    ||
927  | 
    * multiple +/- jobs.  | 
    ||
928  | 
    */  | 
    ||
929  | 
    ✓✓✓✓ | 
    3800  | 
    if (j->state == PEXITED || j->state == PSIGNALLED)  | 
    
930  | 
    1997  | 
    j->flags |= JF_REMOVE;  | 
    |
931  | 
    }  | 
    ||
932  | 
    ✓✓ | 300282  | 
    	for (j = job_list; j; j = tmp) { | 
    
933  | 
    2037  | 
    tmp = j->next;  | 
    |
934  | 
    ✓✓ | 2037  | 
    if (j->flags & JF_REMOVE)  | 
    
935  | 
    1997  | 
    remove_job(j, "notify");  | 
    |
936  | 
    }  | 
    ||
937  | 
    148104  | 
    shf_flush(shl_out);  | 
    |
938  | 
    148104  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    |
939  | 
    148104  | 
    }  | 
    |
940  | 
    |||
941  | 
    /* Return pid of last process in last asynchronous job */  | 
    ||
942  | 
    pid_t  | 
    ||
943  | 
    j_async(void)  | 
    ||
944  | 
    { | 
    ||
945  | 
    3534  | 
    sigset_t omask;  | 
    |
946  | 
    |||
947  | 
    1767  | 
    sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);  | 
    |
948  | 
    |||
949  | 
    ✓✗ | 1767  | 
    if (async_job)  | 
    
950  | 
    1767  | 
    async_job->flags |= JF_KNOWN;  | 
    |
951  | 
    |||
952  | 
    1767  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    |
953  | 
    |||
954  | 
    3534  | 
    return async_pid;  | 
    |
955  | 
    1767  | 
    }  | 
    |
956  | 
    |||
957  | 
    /* Make j the last async process  | 
    ||
958  | 
    *  | 
    ||
959  | 
    * If jobs are compiled in then this routine expects sigchld to be blocked.  | 
    ||
960  | 
    */  | 
    ||
961  | 
    static void  | 
    ||
962  | 
    j_set_async(Job *j)  | 
    ||
963  | 
    { | 
    ||
964  | 
    Job *jl, *oldest;  | 
    ||
965  | 
    |||
966  | 
    ✓✓✓✓ | 
    6470  | 
    if (async_job && (async_job->flags & (JF_KNOWN|JF_ZOMBIE)) == JF_ZOMBIE)  | 
    
967  | 
    56  | 
    remove_job(async_job, "async");  | 
    |
968  | 
    ✗✓ | 2363  | 
    	if (!(j->flags & JF_STARTED)) { | 
    
969  | 
    internal_errorf(0, "j_async: job not started");  | 
    ||
970  | 
    return;  | 
    ||
971  | 
    }  | 
    ||
972  | 
    2363  | 
    async_job = j;  | 
    |
973  | 
    2363  | 
    async_pid = j->last_proc->pid;  | 
    |
974  | 
    ✗✓ | 4726  | 
    	while (nzombie > child_max) { | 
    
975  | 
    oldest = NULL;  | 
    ||
976  | 
    for (jl = job_list; jl; jl = jl->next)  | 
    ||
977  | 
    if (jl != async_job && (jl->flags & JF_ZOMBIE) &&  | 
    ||
978  | 
    (!oldest || jl->age < oldest->age))  | 
    ||
979  | 
    oldest = jl;  | 
    ||
980  | 
    		if (!oldest) { | 
    ||
981  | 
    /* XXX debugging */  | 
    ||
982  | 
    			if (!(async_job->flags & JF_ZOMBIE) || nzombie != 1) { | 
    ||
983  | 
    internal_errorf(0,  | 
    ||
984  | 
    "j_async: bad nzombie (%d)", nzombie);  | 
    ||
985  | 
    nzombie = 0;  | 
    ||
986  | 
    }  | 
    ||
987  | 
    break;  | 
    ||
988  | 
    }  | 
    ||
989  | 
    remove_job(oldest, "zombie");  | 
    ||
990  | 
    }  | 
    ||
991  | 
    4726  | 
    }  | 
    |
992  | 
    |||
993  | 
    /* Start a job: set STARTED, check for held signals and set j->last_proc  | 
    ||
994  | 
    *  | 
    ||
995  | 
    * If jobs are compiled in then this routine expects sigchld to be blocked.  | 
    ||
996  | 
    */  | 
    ||
997  | 
    static void  | 
    ||
998  | 
    j_startjob(Job *j)  | 
    ||
999  | 
    { | 
    ||
1000  | 
    Proc *p;  | 
    ||
1001  | 
    |||
1002  | 
    8924078  | 
    j->flags |= JF_STARTED;  | 
    |
1003  | 
    ✓✓ | 14249718  | 
    for (p = j->proc_list; p->next; p = p->next)  | 
    
1004  | 
    ;  | 
    ||
1005  | 
    4462039  | 
    j->last_proc = p;  | 
    |
1006  | 
    |||
1007  | 
    ✓✓ | 4462039  | 
    	if (held_sigchld) { | 
    
1008  | 
    13056  | 
    held_sigchld = 0;  | 
    |
1009  | 
    /* Don't call j_sigchld() as it may remove job... */  | 
    ||
1010  | 
    13056  | 
    kill(procpid, SIGCHLD);  | 
    |
1011  | 
    13056  | 
    }  | 
    |
1012  | 
    4462039  | 
    }  | 
    |
1013  | 
    |||
1014  | 
    /*  | 
    ||
1015  | 
    * wait for job to complete or change state  | 
    ||
1016  | 
    *  | 
    ||
1017  | 
    * If jobs are compiled in then this routine expects sigchld to be blocked.  | 
    ||
1018  | 
    */  | 
    ||
1019  | 
    static int  | 
    ||
1020  | 
    j_waitj(Job *j,  | 
    ||
1021  | 
    int flags, /* see JW_* */  | 
    ||
1022  | 
    const char *where)  | 
    ||
1023  | 
    { | 
    ||
1024  | 
    int rv;  | 
    ||
1025  | 
    |||
1026  | 
    /*  | 
    ||
1027  | 
    * No auto-notify on the job we are waiting on.  | 
    ||
1028  | 
    */  | 
    ||
1029  | 
    8919486  | 
    j->flags |= JF_WAITING;  | 
    |
1030  | 
    ✓✓ | 4459743  | 
    if (flags & JW_ASYNCNOTIFY)  | 
    
1031  | 
    50  | 
    j->flags |= JF_W_ASYNCNOTIFY;  | 
    |
1032  | 
    |||
1033  | 
    ✓✓ | 4459743  | 
    if (!Flag(FMONITOR))  | 
    
1034  | 
    4459135  | 
    flags |= JW_STOPPEDWAIT;  | 
    |
1035  | 
    |||
1036  | 
    ✓✓✗✓ | 
    10671162  | 
    while ((volatile int) j->state == PRUNNING ||  | 
    
1037  | 
    ✓✓ | 8918878  | 
    	    ((flags & JW_STOPPEDWAIT) && (volatile int) j->state == PSTOPPED)) { | 
    
1038  | 
    1752284  | 
    sigsuspend(&sm_default);  | 
    |
1039  | 
    ✗✓ | 1752284  | 
    		if (fatal_trap) { | 
    
1040  | 
    int oldf = j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY);  | 
    ||
1041  | 
    j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);  | 
    ||
1042  | 
    runtraps(TF_FATAL);  | 
    ||
1043  | 
    j->flags |= oldf; /* not reached... */  | 
    ||
1044  | 
    }  | 
    ||
1045  | 
    ✓✓✓✗ | 
    1752343  | 
    		if ((flags & JW_INTERRUPT) && (rv = trap_pending())) { | 
    
1046  | 
    j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);  | 
    ||
1047  | 
    return -rv;  | 
    ||
1048  | 
    }  | 
    ||
1049  | 
    }  | 
    ||
1050  | 
    4459743  | 
    j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);  | 
    |
1051  | 
    |||
1052  | 
    ✓✓ | 4459743  | 
    	if (j->flags & JF_FG) { | 
    
1053  | 
    int status;  | 
    ||
1054  | 
    |||
1055  | 
    1507894  | 
    j->flags &= ~JF_FG;  | 
    |
1056  | 
    #ifdef JOBS  | 
    ||
1057  | 
    ✓✓✓✗ | 
    1508462  | 
    		if (Flag(FMONITOR) && ttypgrp_ok && j->pgrp) { | 
    
1058  | 
    /*  | 
    ||
1059  | 
    * Save the tty's current pgrp so it can be restored  | 
    ||
1060  | 
    * when the job is foregrounded. This is to  | 
    ||
1061  | 
    * deal with things like the GNU su which does  | 
    ||
1062  | 
    * a fork/exec instead of an exec (the fork means  | 
    ||
1063  | 
    * the execed shell gets a different pid from its  | 
    ||
1064  | 
    * pgrp, so naturally it sets its pgrp and gets hosed  | 
    ||
1065  | 
    * when it gets foregrounded by the parent shell, which  | 
    ||
1066  | 
    * has restored the tty's pgrp to that of the su  | 
    ||
1067  | 
    * process).  | 
    ||
1068  | 
    */  | 
    ||
1069  | 
    ✗✓✗✗ | 
    568  | 
    if (j->state == PSTOPPED &&  | 
    
1070  | 
    (j->saved_ttypgrp = tcgetpgrp(tty_fd)) >= 0)  | 
    ||
1071  | 
    j->flags |= JF_SAVEDTTYPGRP;  | 
    ||
1072  | 
    ✗✓ | 568  | 
    			if (tcsetpgrp(tty_fd, our_pgrp) < 0) { | 
    
1073  | 
    warningf(true,  | 
    ||
1074  | 
    "j_waitj: tcsetpgrp(%d, %d) failed: %s",  | 
    ||
1075  | 
    tty_fd, (int) our_pgrp,  | 
    ||
1076  | 
    strerror(errno));  | 
    ||
1077  | 
    }  | 
    ||
1078  | 
    ✗✓ | 568  | 
    			if (j->state == PSTOPPED) { | 
    
1079  | 
    j->flags |= JF_SAVEDTTY;  | 
    ||
1080  | 
    tcgetattr(tty_fd, &j->ttystate);  | 
    ||
1081  | 
    }  | 
    ||
1082  | 
    }  | 
    ||
1083  | 
    #endif /* JOBS */  | 
    ||
1084  | 
    ✓✓ | 1507894  | 
    		if (tty_fd >= 0) { | 
    
1085  | 
    /* Only restore tty settings if job was originally  | 
    ||
1086  | 
    * started in the foreground. Problems can be  | 
    ||
1087  | 
    * caused by things like `more foobar &' which will  | 
    ||
1088  | 
    * typically get and save the shell's vi/emacs tty  | 
    ||
1089  | 
    * settings before setting up the tty for itself;  | 
    ||
1090  | 
    * when more exits, it restores the `original'  | 
    ||
1091  | 
    * settings, and things go down hill from there...  | 
    ||
1092  | 
    */  | 
    ||
1093  | 
    ✓✓✓✓ ✓✗  | 
    1524  | 
    if (j->state == PEXITED && j->status == 0 &&  | 
    
1094  | 
    408  | 
    			    (j->flags & JF_USETTYMODE)) { | 
    |
1095  | 
    408  | 
    tcgetattr(tty_fd, &tty_state);  | 
    |
1096  | 
    408  | 
    			} else { | 
    |
1097  | 
    160  | 
    tcsetattr(tty_fd, TCSADRAIN, &tty_state);  | 
    |
1098  | 
    /* Don't use tty mode if job is stopped and  | 
    ||
1099  | 
    * later restarted and exits. Consider  | 
    ||
1100  | 
    * the sequence:  | 
    ||
1101  | 
    * vi foo (stopped)  | 
    ||
1102  | 
    * ...  | 
    ||
1103  | 
    * stty something  | 
    ||
1104  | 
    * ...  | 
    ||
1105  | 
    * fg (vi; ZZ)  | 
    ||
1106  | 
    * mode should be that of the stty, not what  | 
    ||
1107  | 
    * was before the vi started.  | 
    ||
1108  | 
    */  | 
    ||
1109  | 
    ✗✓ | 160  | 
    if (j->state == PSTOPPED)  | 
    
1110  | 
    j->flags &= ~JF_USETTYMODE;  | 
    ||
1111  | 
    }  | 
    ||
1112  | 
    }  | 
    ||
1113  | 
    #ifdef JOBS  | 
    ||
1114  | 
    /* If it looks like user hit ^C to kill a job, pretend we got  | 
    ||
1115  | 
    * one too to break out of for loops, etc. (at&t ksh does this  | 
    ||
1116  | 
    * even when not monitoring, but this doesn't make sense since  | 
    ||
1117  | 
    * a tty generated ^C goes to the whole process group)  | 
    ||
1118  | 
    */  | 
    ||
1119  | 
    1507894  | 
    status = j->last_proc->status;  | 
    |
1120  | 
    ✓✓✓✓ ✓✓  | 
    1508475  | 
    if (Flag(FMONITOR) && j->state == PSIGNALLED &&  | 
    
1121  | 
    ✓✗✓✓ | 
    40  | 
    WIFSIGNALED(status) &&  | 
    
1122  | 
    13  | 
    (sigtraps[WTERMSIG(status)].flags & TF_TTY_INTR))  | 
    |
1123  | 
    8  | 
    trapsig(WTERMSIG(status));  | 
    |
1124  | 
    #endif /* JOBS */  | 
    ||
1125  | 
    1507894  | 
    }  | 
    |
1126  | 
    |||
1127  | 
    4459743  | 
    j_usrtime = j->usrtime;  | 
    |
1128  | 
    4459743  | 
    j_systime = j->systime;  | 
    |
1129  | 
    4459743  | 
    rv = j->status;  | 
    |
1130  | 
    |||
1131  | 
    ✓✓✓✗ | 
    4460351  | 
    if (!(flags & JW_ASYNCNOTIFY) &&  | 
    
1132  | 
    ✓✓ | 4460301  | 
    	    (!Flag(FMONITOR) || j->state != PSTOPPED)) { | 
    
1133  | 
    4459693  | 
    j_print(j, JP_SHORT, shl_out);  | 
    |
1134  | 
    4459693  | 
    shf_flush(shl_out);  | 
    |
1135  | 
    4459693  | 
    }  | 
    |
1136  | 
    ✓✗✓✗ | 
    4460351  | 
    if (j->state != PSTOPPED &&  | 
    
1137  | 
    ✓✓ | 4459743  | 
    (!Flag(FMONITOR) || !(flags & JW_ASYNCNOTIFY)))  | 
    
1138  | 
    4459743  | 
    remove_job(j, where);  | 
    |
1139  | 
    |||
1140  | 
    4459743  | 
    return rv;  | 
    |
1141  | 
    4459743  | 
    }  | 
    |
1142  | 
    |||
1143  | 
    /* SIGCHLD handler to reap children and update job states  | 
    ||
1144  | 
    *  | 
    ||
1145  | 
    * If jobs are compiled in then this routine expects sigchld to be blocked.  | 
    ||
1146  | 
    */  | 
    ||
1147  | 
    static void  | 
    ||
1148  | 
    j_sigchld(int sig)  | 
    ||
1149  | 
    { | 
    ||
1150  | 
    13917090  | 
    int errno_ = errno;  | 
    |
1151  | 
    Job *j;  | 
    ||
1152  | 
    Proc *p = NULL;  | 
    ||
1153  | 
    int pid;  | 
    ||
1154  | 
    6958545  | 
    int status;  | 
    |
1155  | 
    6958545  | 
    struct rusage ru0, ru1;  | 
    |
1156  | 
    |||
1157  | 
    /* Don't wait for any processes if a job is partially started.  | 
    ||
1158  | 
    * This is so we don't do away with the process group leader  | 
    ||
1159  | 
    * before all the processes in a pipe line are started (so the  | 
    ||
1160  | 
    * setpgid() won't fail)  | 
    ||
1161  | 
    */  | 
    ||
1162  | 
    ✓✓ | 28416394  | 
    for (j = job_list; j; j = j->next)  | 
    
1163  | 
    ✓✓✓✓ | 
    14525311  | 
    		if (j->ppid == procpid && !(j->flags & JF_STARTED)) { | 
    
1164  | 
    13056  | 
    held_sigchld = 1;  | 
    |
1165  | 
    13056  | 
    goto finished;  | 
    |
1166  | 
    }  | 
    ||
1167  | 
    |||
1168  | 
    6945489  | 
    getrusage(RUSAGE_CHILDREN, &ru0);  | 
    |
1169  | 
    6945489  | 
    	do { | 
    |
1170  | 
    14070091  | 
    pid = waitpid(-1, &status, (WNOHANG|WUNTRACED));  | 
    |
1171  | 
    |||
1172  | 
    ✓✓ | 14070091  | 
    if (pid <= 0) /* return if would block (0) ... */  | 
    
1173  | 
    break; /* ... or no children or interrupted (-1) */  | 
    ||
1174  | 
    |||
1175  | 
    7124602  | 
    getrusage(RUSAGE_CHILDREN, &ru1);  | 
    |
1176  | 
    |||
1177  | 
    /* find job and process structures for this pid */  | 
    ||
1178  | 
    ✓✗ | 14249792  | 
    for (j = job_list; j != NULL; j = j->next)  | 
    
1179  | 
    ✓✓ | 21028756  | 
    for (p = j->proc_list; p != NULL; p = p->next)  | 
    
1180  | 
    ✓✓ | 10514084  | 
    if (p->pid == pid)  | 
    
1181  | 
    goto found;  | 
    ||
1182  | 
    found:  | 
    ||
1183  | 
    ✗✓ | 7124602  | 
    		if (j == NULL) { | 
    
1184  | 
    /* Can occur if process has kids, then execs shell  | 
    ||
1185  | 
    warningf(true, "bad process waited for (pid = %d)",  | 
    ||
1186  | 
    pid);  | 
    ||
1187  | 
    */  | 
    ||
1188  | 
    ru0 = ru1;  | 
    ||
1189  | 
    continue;  | 
    ||
1190  | 
    }  | 
    ||
1191  | 
    |||
1192  | 
    ✓✓ | 7124617  | 
    timeradd(&j->usrtime, &ru1.ru_utime, &j->usrtime);  | 
    
1193  | 
    ✓✓ | 7127178  | 
    timersub(&j->usrtime, &ru0.ru_utime, &j->usrtime);  | 
    
1194  | 
    ✓✓ | 7127156  | 
    timeradd(&j->systime, &ru1.ru_stime, &j->systime);  | 
    
1195  | 
    ✓✓ | 7133112  | 
    timersub(&j->systime, &ru0.ru_stime, &j->systime);  | 
    
1196  | 
    7124602  | 
    ru0 = ru1;  | 
    |
1197  | 
    7124602  | 
    p->status = status;  | 
    |
1198  | 
    #ifdef JOBS  | 
    ||
1199  | 
    ✗✓ | 7124602  | 
    if (WIFSTOPPED(status))  | 
    
1200  | 
    p->state = PSTOPPED;  | 
    ||
1201  | 
    else  | 
    ||
1202  | 
    #endif /* JOBS */  | 
    ||
1203  | 
    ✓✗✓✓ | 
    14249204  | 
    if (WIFSIGNALED(status))  | 
    
1204  | 
    2163  | 
    p->state = PSIGNALLED;  | 
    |
1205  | 
    else  | 
    ||
1206  | 
    p->state = PEXITED;  | 
    ||
1207  | 
    |||
1208  | 
    7124602  | 
    check_job(j); /* check to see if entire job is done */  | 
    |
1209  | 
    7124602  | 
    } while (1);  | 
    |
1210  | 
    |||
1211  | 
    finished:  | 
    ||
1212  | 
    6958545  | 
    errno = errno_;  | 
    |
1213  | 
    6958545  | 
    }  | 
    |
1214  | 
    |||
1215  | 
    /*  | 
    ||
1216  | 
    * Called only when a process in j has exited/stopped (ie, called only  | 
    ||
1217  | 
    * from j_sigchld()). If no processes are running, the job status  | 
    ||
1218  | 
    * and state are updated, asynchronous job notification is done and,  | 
    ||
1219  | 
    * if unneeded, the job is removed.  | 
    ||
1220  | 
    *  | 
    ||
1221  | 
    * If jobs are compiled in then this routine expects sigchld to be blocked.  | 
    ||
1222  | 
    */  | 
    ||
1223  | 
    static void  | 
    ||
1224  | 
    check_job(Job *j)  | 
    ||
1225  | 
    { | 
    ||
1226  | 
    int jstate;  | 
    ||
1227  | 
    Proc *p;  | 
    ||
1228  | 
    |||
1229  | 
    /* XXX debugging (nasty - interrupt routine using shl_out) */  | 
    ||
1230  | 
    ✗✓ | 14249204  | 
    	if (!(j->flags & JF_STARTED)) { | 
    
1231  | 
    internal_errorf(0, "check_job: job started (flags 0x%x)",  | 
    ||
1232  | 
    j->flags);  | 
    ||
1233  | 
    return;  | 
    ||
1234  | 
    }  | 
    ||
1235  | 
    |||
1236  | 
    jstate = PRUNNING;  | 
    ||
1237  | 
    ✓✓ | 34843332  | 
    	for (p=j->proc_list; p != NULL; p = p->next) { | 
    
1238  | 
    ✓✓ | 12959861  | 
    if (p->state == PRUNNING)  | 
    
1239  | 
    2662797  | 
    return; /* some processes still running */  | 
    |
1240  | 
    ✓✓ | 10297064  | 
    if (p->state > jstate)  | 
    
1241  | 
    6938610  | 
    jstate = p->state;  | 
    |
1242  | 
    }  | 
    ||
1243  | 
    4461805  | 
    j->state = jstate;  | 
    |
1244  | 
    |||
1245  | 
    ✓✓✗ | 4461805  | 
    	switch (j->last_proc->state) { | 
    
1246  | 
    case PEXITED:  | 
    ||
1247  | 
    4459799  | 
    j->status = WEXITSTATUS(j->last_proc->status);  | 
    |
1248  | 
    4459799  | 
    break;  | 
    |
1249  | 
    case PSIGNALLED:  | 
    ||
1250  | 
    2006  | 
    j->status = 128 + WTERMSIG(j->last_proc->status);  | 
    |
1251  | 
    2006  | 
    break;  | 
    |
1252  | 
    default:  | 
    ||
1253  | 
    j->status = 0;  | 
    ||
1254  | 
    break;  | 
    ||
1255  | 
    }  | 
    ||
1256  | 
    |||
1257  | 
    /* Note when co-process dies: can't be done in j_wait() nor  | 
    ||
1258  | 
    * remove_job() since neither may be called for non-interactive  | 
    ||
1259  | 
    * shells.  | 
    ||
1260  | 
    */  | 
    ||
1261  | 
    ✓✓✓✗ | 
    4463962  | 
    	if (j->state == PEXITED || j->state == PSIGNALLED) { | 
    
1262  | 
    /* No need to keep co-process input any more  | 
    ||
1263  | 
    * (at least, this is what ksh93d thinks)  | 
    ||
1264  | 
    */  | 
    ||
1265  | 
    ✗✓ | 4461805  | 
    		if (coproc.job == j) { | 
    
1266  | 
    coproc.job = NULL;  | 
    ||
1267  | 
    /* XXX would be nice to get the closes out of here  | 
    ||
1268  | 
    * so they aren't done in the signal handler.  | 
    ||
1269  | 
    * Would mean a check in coproc_getfd() to  | 
    ||
1270  | 
    * do "if job == 0 && write >= 0, close write".  | 
    ||
1271  | 
    */  | 
    ||
1272  | 
    coproc_write_close(coproc.write);  | 
    ||
1273  | 
    }  | 
    ||
1274  | 
    /* Do we need to keep the output? */  | 
    ||
1275  | 
    ✗✓✗✗ ✗✗  | 
    4461805  | 
    if (j->coproc_id && j->coproc_id == coproc.id &&  | 
    
1276  | 
    --coproc.njobs == 0)  | 
    ||
1277  | 
    coproc_readw_close(coproc.read);  | 
    ||
1278  | 
    }  | 
    ||
1279  | 
    |||
1280  | 
    4461805  | 
    j->flags |= JF_CHANGED;  | 
    |
1281  | 
    #ifdef JOBS  | 
    ||
1282  | 
    ✓✓✓✓ | 
    4462413  | 
    	if (Flag(FMONITOR) && !(j->flags & JF_XXCOM)) { | 
    
1283  | 
    /* Only put stopped jobs at the front to avoid confusing  | 
    ||
1284  | 
    * the user (don't want finished jobs effecting %+ or %-)  | 
    ||
1285  | 
    */  | 
    ||
1286  | 
    ✗✓ | 568  | 
    if (j->state == PSTOPPED)  | 
    
1287  | 
    put_job(j, PJ_ON_FRONT);  | 
    ||
1288  | 
    ✗✓✗✗ | 
    568  | 
    if (Flag(FNOTIFY) &&  | 
    
1289  | 
    		    (j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY)) != JF_WAITING) { | 
    ||
1290  | 
    /* Look for the real file descriptor 2 */  | 
    ||
1291  | 
    			{ | 
    ||
1292  | 
    struct env *ep;  | 
    ||
1293  | 
    int fd = 2;  | 
    ||
1294  | 
    |||
1295  | 
    for (ep = genv; ep; ep = ep->oenv)  | 
    ||
1296  | 
    if (ep->savefd && ep->savefd[2])  | 
    ||
1297  | 
    fd = ep->savefd[2];  | 
    ||
1298  | 
    shf_reopen(fd, SHF_WR, shl_j);  | 
    ||
1299  | 
    }  | 
    ||
1300  | 
    /* Can't call j_notify() as it removes jobs. The job  | 
    ||
1301  | 
    * must stay in the job list as j_waitj() may be  | 
    ||
1302  | 
    * running with this job.  | 
    ||
1303  | 
    */  | 
    ||
1304  | 
    j_print(j, JP_MEDIUM, shl_j);  | 
    ||
1305  | 
    shf_flush(shl_j);  | 
    ||
1306  | 
    if (!(j->flags & JF_WAITING) && j->state != PSTOPPED)  | 
    ||
1307  | 
    remove_job(j, "notify");  | 
    ||
1308  | 
    }  | 
    ||
1309  | 
    }  | 
    ||
1310  | 
    #endif /* JOBS */  | 
    ||
1311  | 
    ✓✓✓✓ ✓✗  | 
    8925072  | 
    if (!Flag(FMONITOR) && !(j->flags & (JF_WAITING|JF_FG)) &&  | 
    
1312  | 
    2070  | 
    	    j->state != PSTOPPED) { | 
    |
1313  | 
    ✗✓✗✗ | 
    2070  | 
    		if (j == async_job || (j->flags & JF_KNOWN)) { | 
    
1314  | 
    2070  | 
    j->flags |= JF_ZOMBIE;  | 
    |
1315  | 
    2070  | 
    j->job = -1;  | 
    |
1316  | 
    2070  | 
    nzombie++;  | 
    |
1317  | 
    2070  | 
    } else  | 
    |
1318  | 
    remove_job(j, "checkjob");  | 
    ||
1319  | 
    }  | 
    ||
1320  | 
    11586407  | 
    }  | 
    |
1321  | 
    |||
1322  | 
    /*  | 
    ||
1323  | 
    * Print job status in either short, medium or long format.  | 
    ||
1324  | 
    *  | 
    ||
1325  | 
    * If jobs are compiled in then this routine expects sigchld to be blocked.  | 
    ||
1326  | 
    */  | 
    ||
1327  | 
    static void  | 
    ||
1328  | 
    j_print(Job *j, int how, struct shf *shf)  | 
    ||
1329  | 
    { | 
    ||
1330  | 
    Proc *p;  | 
    ||
1331  | 
    int state;  | 
    ||
1332  | 
    int status;  | 
    ||
1333  | 
    int coredumped;  | 
    ||
1334  | 
    char jobchar = ' ';  | 
    ||
1335  | 
    8919386  | 
    char buf[64];  | 
    |
1336  | 
    const char *filler;  | 
    ||
1337  | 
    int output = 0;  | 
    ||
1338  | 
    |||
1339  | 
    ✗✓ | 4459693  | 
    	if (how == JP_PGRP) { | 
    
1340  | 
    /* POSIX doesn't say what to do it there is no process  | 
    ||
1341  | 
    * group leader (ie, !FMONITOR). We arbitrarily return  | 
    ||
1342  | 
    * last pid (which is what $! returns).  | 
    ||
1343  | 
    */  | 
    ||
1344  | 
    shf_fprintf(shf, "%d\n", j->pgrp ? j->pgrp :  | 
    ||
1345  | 
    (j->last_proc ? j->last_proc->pid : 0));  | 
    ||
1346  | 
    return;  | 
    ||
1347  | 
    }  | 
    ||
1348  | 
    4459693  | 
    j->flags &= ~JF_CHANGED;  | 
    |
1349  | 
    4459693  | 
    filler = j->job > 10 ? "\n " : "\n ";  | 
    |
1350  | 
    ✓✗ | 4459693  | 
    if (j == job_list)  | 
    
1351  | 
    4459693  | 
    jobchar = '+';  | 
    |
1352  | 
    else if (j == job_list->next)  | 
    ||
1353  | 
    jobchar = '-';  | 
    ||
1354  | 
    |||
1355  | 
    ✓✓ | 13400308  | 
    	for (p = j->proc_list; p != NULL;) { | 
    
1356  | 
    coredumped = 0;  | 
    ||
1357  | 
    ✗✗✓✓ ✓  | 
    8961844  | 
    		switch (p->state) { | 
    
1358  | 
    case PRUNNING:  | 
    ||
1359  | 
    strlcpy(buf, "Running", sizeof buf);  | 
    ||
1360  | 
    break;  | 
    ||
1361  | 
    case PSTOPPED:  | 
    ||
1362  | 
    strlcpy(buf, sigtraps[WSTOPSIG(p->status)].mess,  | 
    ||
1363  | 
    sizeof buf);  | 
    ||
1364  | 
    break;  | 
    ||
1365  | 
    case PEXITED:  | 
    ||
1366  | 
    ✓✗ | 4480506  | 
    if (how == JP_SHORT)  | 
    
1367  | 
    4480506  | 
    buf[0] = '\0';  | 
    |
1368  | 
    else if (WEXITSTATUS(p->status) == 0)  | 
    ||
1369  | 
    strlcpy(buf, "Done", sizeof buf);  | 
    ||
1370  | 
    else  | 
    ||
1371  | 
    shf_snprintf(buf, sizeof(buf), "Done (%d)",  | 
    ||
1372  | 
    WEXITSTATUS(p->status));  | 
    ||
1373  | 
    break;  | 
    ||
1374  | 
    case PSIGNALLED:  | 
    ||
1375  | 
    ✓✓ | 416  | 
    if (WCOREDUMP(p->status))  | 
    
1376  | 
    155  | 
    coredumped = 1;  | 
    |
1377  | 
    /* kludge for not reporting `normal termination signals'  | 
    ||
1378  | 
    * (ie, SIGINT, SIGPIPE)  | 
    ||
1379  | 
    */  | 
    ||
1380  | 
    ✓✓✓✓ | 
    606  | 
    if (how == JP_SHORT && !coredumped &&  | 
    
1381  | 
    ✓✓ | 261  | 
    (WTERMSIG(p->status) == SIGINT ||  | 
    
1382  | 
    190  | 
    			    WTERMSIG(p->status) == SIGPIPE)) { | 
    |
1383  | 
    205  | 
    buf[0] = '\0';  | 
    |
1384  | 
    205  | 
    } else  | 
    |
1385  | 
    211  | 
    strlcpy(buf, sigtraps[WTERMSIG(p->status)].mess,  | 
    |
1386  | 
    sizeof buf);  | 
    ||
1387  | 
    break;  | 
    ||
1388  | 
    }  | 
    ||
1389  | 
    |||
1390  | 
    ✗✓ | 4480922  | 
    		if (how != JP_SHORT) { | 
    
1391  | 
    if (p == j->proc_list)  | 
    ||
1392  | 
    shf_fprintf(shf, "[%d] %c ", j->job, jobchar);  | 
    ||
1393  | 
    else  | 
    ||
1394  | 
    shf_fprintf(shf, "%s", filler);  | 
    ||
1395  | 
    }  | 
    ||
1396  | 
    |||
1397  | 
    ✗✓ | 4480922  | 
    if (how == JP_LONG)  | 
    
1398  | 
    shf_fprintf(shf, "%5d ", p->pid);  | 
    ||
1399  | 
    |||
1400  | 
    ✓✗ | 4480922  | 
    		if (how == JP_SHORT) { | 
    
1401  | 
    ✓✓ | 4480922  | 
    			if (buf[0]) { | 
    
1402  | 
    output = 1;  | 
    ||
1403  | 
    211  | 
    shf_fprintf(shf, "%s%s ",  | 
    |
1404  | 
    211  | 
    buf, coredumped ? " (core dumped)" : "");  | 
    |
1405  | 
    211  | 
    }  | 
    |
1406  | 
    		} else { | 
    ||
1407  | 
    output = 1;  | 
    ||
1408  | 
    shf_fprintf(shf, "%-20s %s%s%s", buf, p->command,  | 
    ||
1409  | 
    p->next ? "|" : "",  | 
    ||
1410  | 
    coredumped ? " (core dumped)" : "");  | 
    ||
1411  | 
    }  | 
    ||
1412  | 
    |||
1413  | 
    4480922  | 
    state = p->state;  | 
    |
1414  | 
    4480922  | 
    status = p->status;  | 
    |
1415  | 
    4480922  | 
    p = p->next;  | 
    |
1416  | 
    ✓✓✓✓ ✓✓  | 
    19570198  | 
    		while (p && p->state == state && p->status == status) { | 
    
1417  | 
    ✗✓ | 2641521  | 
    if (how == JP_LONG)  | 
    
1418  | 
    shf_fprintf(shf, "%s%5d %-20s %s%s", filler, p->pid,  | 
    ||
1419  | 
    " ", p->command, p->next ? "|" : "");  | 
    ||
1420  | 
    ✗✓ | 2641521  | 
    else if (how == JP_MEDIUM)  | 
    
1421  | 
    shf_fprintf(shf, " %s%s", p->command,  | 
    ||
1422  | 
    p->next ? "|" : "");  | 
    ||
1423  | 
    2641521  | 
    p = p->next;  | 
    |
1424  | 
    }  | 
    ||
1425  | 
    }  | 
    ||
1426  | 
    ✓✓ | 4459693  | 
    if (output)  | 
    
1427  | 
    211  | 
    shf_fprintf(shf, "\n");  | 
    |
1428  | 
    8919386  | 
    }  | 
    |
1429  | 
    |||
1430  | 
    /* Convert % sequence to job  | 
    ||
1431  | 
    *  | 
    ||
1432  | 
    * If jobs are compiled in then this routine expects sigchld to be blocked.  | 
    ||
1433  | 
    */  | 
    ||
1434  | 
    static Job *  | 
    ||
1435  | 
    j_lookup(const char *cp, int *ecodep)  | 
    ||
1436  | 
    { | 
    ||
1437  | 
    Job *j, *last_match;  | 
    ||
1438  | 
    34  | 
    const char *errstr;  | 
    |
1439  | 
    Proc *p;  | 
    ||
1440  | 
    int len, job = 0;  | 
    ||
1441  | 
    |||
1442  | 
    ✓✗ | 17  | 
    	if (digit(*cp)) { | 
    
1443  | 
    17  | 
    job = strtonum(cp, 1, INT_MAX, &errstr);  | 
    |
1444  | 
    ✗✓ | 17  | 
    		if (errstr) { | 
    
1445  | 
    if (ecodep)  | 
    ||
1446  | 
    *ecodep = JL_NOSUCH;  | 
    ||
1447  | 
    return NULL;  | 
    ||
1448  | 
    }  | 
    ||
1449  | 
    /* Look for last_proc->pid (what $! returns) first... */  | 
    ||
1450  | 
    ✓✗ | 34  | 
    for (j = job_list; j != NULL; j = j->next)  | 
    
1451  | 
    ✓✗✓✗ | 
    34  | 
    if (j->last_proc && j->last_proc->pid == job)  | 
    
1452  | 
    17  | 
    return j;  | 
    |
1453  | 
    /* ...then look for process group (this is non-POSIX),  | 
    ||
1454  | 
    * but should not break anything (so FPOSIX isn't used).  | 
    ||
1455  | 
    */  | 
    ||
1456  | 
    for (j = job_list; j != NULL; j = j->next)  | 
    ||
1457  | 
    if (j->pgrp && j->pgrp == job)  | 
    ||
1458  | 
    return j;  | 
    ||
1459  | 
    if (ecodep)  | 
    ||
1460  | 
    *ecodep = JL_NOSUCH;  | 
    ||
1461  | 
    return NULL;  | 
    ||
1462  | 
    }  | 
    ||
1463  | 
    	if (*cp != '%') { | 
    ||
1464  | 
    if (ecodep)  | 
    ||
1465  | 
    *ecodep = JL_INVALID;  | 
    ||
1466  | 
    return NULL;  | 
    ||
1467  | 
    }  | 
    ||
1468  | 
    	switch (*++cp) { | 
    ||
1469  | 
    case '\0': /* non-standard */  | 
    ||
1470  | 
    case '+':  | 
    ||
1471  | 
    case '%':  | 
    ||
1472  | 
    if (job_list != NULL)  | 
    ||
1473  | 
    return job_list;  | 
    ||
1474  | 
    break;  | 
    ||
1475  | 
    |||
1476  | 
    case '-':  | 
    ||
1477  | 
    if (job_list != NULL && job_list->next)  | 
    ||
1478  | 
    return job_list->next;  | 
    ||
1479  | 
    break;  | 
    ||
1480  | 
    |||
1481  | 
    case '0': case '1': case '2': case '3': case '4':  | 
    ||
1482  | 
    case '5': case '6': case '7': case '8': case '9':  | 
    ||
1483  | 
    job = strtonum(cp, 1, INT_MAX, &errstr);  | 
    ||
1484  | 
    if (errstr)  | 
    ||
1485  | 
    break;  | 
    ||
1486  | 
    for (j = job_list; j != NULL; j = j->next)  | 
    ||
1487  | 
    if (j->job == job)  | 
    ||
1488  | 
    return j;  | 
    ||
1489  | 
    break;  | 
    ||
1490  | 
    |||
1491  | 
    case '?': /* %?string */  | 
    ||
1492  | 
    last_match = NULL;  | 
    ||
1493  | 
    for (j = job_list; j != NULL; j = j->next)  | 
    ||
1494  | 
    for (p = j->proc_list; p != NULL; p = p->next)  | 
    ||
1495  | 
    				if (strstr(p->command, cp+1) != NULL) { | 
    ||
1496  | 
    					if (last_match) { | 
    ||
1497  | 
    if (ecodep)  | 
    ||
1498  | 
    *ecodep = JL_AMBIG;  | 
    ||
1499  | 
    return NULL;  | 
    ||
1500  | 
    }  | 
    ||
1501  | 
    last_match = j;  | 
    ||
1502  | 
    }  | 
    ||
1503  | 
    if (last_match)  | 
    ||
1504  | 
    return last_match;  | 
    ||
1505  | 
    break;  | 
    ||
1506  | 
    |||
1507  | 
    default: /* %string */  | 
    ||
1508  | 
    len = strlen(cp);  | 
    ||
1509  | 
    last_match = NULL;  | 
    ||
1510  | 
    for (j = job_list; j != NULL; j = j->next)  | 
    ||
1511  | 
    			if (strncmp(cp, j->proc_list->command, len) == 0) { | 
    ||
1512  | 
    				if (last_match) { | 
    ||
1513  | 
    if (ecodep)  | 
    ||
1514  | 
    *ecodep = JL_AMBIG;  | 
    ||
1515  | 
    return NULL;  | 
    ||
1516  | 
    }  | 
    ||
1517  | 
    last_match = j;  | 
    ||
1518  | 
    }  | 
    ||
1519  | 
    if (last_match)  | 
    ||
1520  | 
    return last_match;  | 
    ||
1521  | 
    break;  | 
    ||
1522  | 
    }  | 
    ||
1523  | 
    if (ecodep)  | 
    ||
1524  | 
    *ecodep = JL_NOSUCH;  | 
    ||
1525  | 
    return NULL;  | 
    ||
1526  | 
    17  | 
    }  | 
    |
1527  | 
    |||
1528  | 
    static Job *free_jobs;  | 
    ||
1529  | 
    static Proc *free_procs;  | 
    ||
1530  | 
    |||
1531  | 
    /* allocate a new job and fill in the job number.  | 
    ||
1532  | 
    *  | 
    ||
1533  | 
    * If jobs are compiled in then this routine expects sigchld to be blocked.  | 
    ||
1534  | 
    */  | 
    ||
1535  | 
    static Job *  | 
    ||
1536  | 
    new_job(void)  | 
    ||
1537  | 
    { | 
    ||
1538  | 
    int i;  | 
    ||
1539  | 
    Job *newj, *j;  | 
    ||
1540  | 
    |||
1541  | 
    ✓✓ | 9088008  | 
    	if (free_jobs != NULL) { | 
    
1542  | 
    newj = free_jobs;  | 
    ||
1543  | 
    4343711  | 
    free_jobs = free_jobs->next;  | 
    |
1544  | 
    4343711  | 
    } else  | 
    |
1545  | 
    200293  | 
    newj = alloc(sizeof(Job), APERM);  | 
    |
1546  | 
    |||
1547  | 
    /* brute force method */  | 
    ||
1548  | 
    4550100  | 
    	for (i = 1; ; i++) { | 
    |
1549  | 
    ✓✓✓✓ | 
    9670488  | 
    for (j = job_list; j && j->job != i; j = j->next)  | 
    
1550  | 
    ;  | 
    ||
1551  | 
    ✓✓ | 4550100  | 
    if (j == NULL)  | 
    
1552  | 
    break;  | 
    ||
1553  | 
    }  | 
    ||
1554  | 
    4544004  | 
    newj->job = i;  | 
    |
1555  | 
    |||
1556  | 
    4544004  | 
    return newj;  | 
    |
1557  | 
    }  | 
    ||
1558  | 
    |||
1559  | 
    /* Allocate new process struct  | 
    ||
1560  | 
    *  | 
    ||
1561  | 
    * If jobs are compiled in then this routine expects sigchld to be blocked.  | 
    ||
1562  | 
    */  | 
    ||
1563  | 
    static Proc *  | 
    ||
1564  | 
    new_proc(void)  | 
    ||
1565  | 
    { | 
    ||
1566  | 
    Proc *p;  | 
    ||
1567  | 
    |||
1568  | 
    ✓✓ | 14414766  | 
    	if (free_procs != NULL) { | 
    
1569  | 
    p = free_procs;  | 
    ||
1570  | 
    6918236  | 
    free_procs = free_procs->next;  | 
    |
1571  | 
    6918236  | 
    } else  | 
    |
1572  | 
    289147  | 
    p = alloc(sizeof(Proc), APERM);  | 
    |
1573  | 
    |||
1574  | 
    7207383  | 
    return p;  | 
    |
1575  | 
    }  | 
    ||
1576  | 
    |||
1577  | 
    /* Take job out of job_list and put old structures into free list.  | 
    ||
1578  | 
    * Keeps nzombies, last_job and async_job up to date.  | 
    ||
1579  | 
    *  | 
    ||
1580  | 
    * If jobs are compiled in then this routine expects sigchld to be blocked.  | 
    ||
1581  | 
    */  | 
    ||
1582  | 
    static void  | 
    ||
1583  | 
    remove_job(Job *j, const char *where)  | 
    ||
1584  | 
    { | 
    ||
1585  | 
    Proc *p, *tmp;  | 
    ||
1586  | 
    Job **prev, *curr;  | 
    ||
1587  | 
    |||
1588  | 
    prev = &job_list;  | 
    ||
1589  | 
    9087522  | 
    curr = *prev;  | 
    |
1590  | 
    ✓✗✓✓ | 
    13631451  | 
    for (; curr != NULL && curr != j; prev = &curr->next, curr = *prev)  | 
    
1591  | 
    ;  | 
    ||
1592  | 
    ✗✓ | 4543761  | 
    	if (curr != j) { | 
    
1593  | 
    internal_errorf(0, "remove_job: job not found (%s)", where);  | 
    ||
1594  | 
    return;  | 
    ||
1595  | 
    }  | 
    ||
1596  | 
    4543761  | 
    *prev = curr->next;  | 
    |
1597  | 
    |||
1598  | 
    /* free up proc structures */  | 
    ||
1599  | 
    ✓✓ | 23501750  | 
    	for (p = j->proc_list; p != NULL; ) { | 
    
1600  | 
    tmp = p;  | 
    ||
1601  | 
    7207114  | 
    p = p->next;  | 
    |
1602  | 
    7207114  | 
    tmp->next = free_procs;  | 
    |
1603  | 
    7207114  | 
    free_procs = tmp;  | 
    |
1604  | 
    }  | 
    ||
1605  | 
    |||
1606  | 
    ✓✓✓✗ | 
    4545822  | 
    if ((j->flags & JF_ZOMBIE) && j->ppid == procpid)  | 
    
1607  | 
    2061  | 
    --nzombie;  | 
    |
1608  | 
    4543761  | 
    j->next = free_jobs;  | 
    |
1609  | 
    4543761  | 
    free_jobs = j;  | 
    |
1610  | 
    |||
1611  | 
    ✓✓ | 4543761  | 
    if (j == last_job)  | 
    
1612  | 
    4541659  | 
    last_job = NULL;  | 
    |
1613  | 
    ✓✓ | 4543761  | 
    if (j == async_job)  | 
    
1614  | 
    457  | 
    async_job = NULL;  | 
    |
1615  | 
    9087522  | 
    }  | 
    |
1616  | 
    |||
1617  | 
    /* put j in a particular location (taking it out job_list if it is there  | 
    ||
1618  | 
    * already)  | 
    ||
1619  | 
    *  | 
    ||
1620  | 
    * If jobs are compiled in then this routine expects sigchld to be blocked.  | 
    ||
1621  | 
    */  | 
    ||
1622  | 
    static void  | 
    ||
1623  | 
    put_job(Job *j, int where)  | 
    ||
1624  | 
    { | 
    ||
1625  | 
    Job **prev, *curr;  | 
    ||
1626  | 
    |||
1627  | 
    /* Remove job from list (if there) */  | 
    ||
1628  | 
    prev = &job_list;  | 
    ||
1629  | 
    9088008  | 
    curr = job_list;  | 
    |
1630  | 
    ✓✓✓✗ | 
    9652125  | 
    for (; curr && curr != j; prev = &curr->next, curr = *prev)  | 
    
1631  | 
    ;  | 
    ||
1632  | 
    ✗✓ | 4544004  | 
    if (curr == j)  | 
    
1633  | 
    *prev = curr->next;  | 
    ||
1634  | 
    |||
1635  | 
    ✗✓✓ | 9088008  | 
    	switch (where) { | 
    
1636  | 
    case PJ_ON_FRONT:  | 
    ||
1637  | 
    j->next = job_list;  | 
    ||
1638  | 
    job_list = j;  | 
    ||
1639  | 
    break;  | 
    ||
1640  | 
    |||
1641  | 
    case PJ_PAST_STOPPED:  | 
    ||
1642  | 
    prev = &job_list;  | 
    ||
1643  | 
    4544004  | 
    curr = job_list;  | 
    |
1644  | 
    ✓✓✗✓ | 
    9096207  | 
    for (; curr && curr->state == PSTOPPED; prev = &curr->next,  | 
    
1645  | 
    curr = *prev)  | 
    ||
1646  | 
    ;  | 
    ||
1647  | 
    4544004  | 
    j->next = curr;  | 
    |
1648  | 
    4544004  | 
    *prev = j;  | 
    |
1649  | 
    4544004  | 
    break;  | 
    |
1650  | 
    }  | 
    ||
1651  | 
    4544004  | 
    }  | 
    |
1652  | 
    |||
1653  | 
    /* nuke a job (called when unable to start full job).  | 
    ||
1654  | 
    *  | 
    ||
1655  | 
    * If jobs are compiled in then this routine expects sigchld to be blocked.  | 
    ||
1656  | 
    */  | 
    ||
1657  | 
    static int  | 
    ||
1658  | 
    kill_job(Job *j, int sig)  | 
    ||
1659  | 
    { | 
    ||
1660  | 
    Proc *p;  | 
    ||
1661  | 
    int rval = 0;  | 
    ||
1662  | 
    |||
1663  | 
    for (p = j->proc_list; p != NULL; p = p->next)  | 
    ||
1664  | 
    if (p->pid != 0)  | 
    ||
1665  | 
    if (kill(p->pid, sig) < 0)  | 
    ||
1666  | 
    rval = -1;  | 
    ||
1667  | 
    return rval;  | 
    ||
1668  | 
    }  | 
    
| Generated by: GCOVR (Version 3.3) |