xref: /original-bsd/bin/csh/sem.c (revision 1344f446)
1 /*-
2  * Copyright (c) 1980, 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)sem.c	5.20 (Berkeley) 09/04/91";
10 #endif /* not lint */
11 
12 #include <sys/param.h>
13 #include <sys/ioctl.h>
14 #include <sys/stat.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20 #if __STDC__
21 # include <stdarg.h>
22 #else
23 # include <varargs.h>
24 #endif
25 
26 #include "csh.h"
27 #include "proc.h"
28 #include "extern.h"
29 
30 static void	vffree __P((int));
31 static void	doio __P((struct command *t, int *, int *));
32 static void	chkclob __P((char *));
33 
34 void
35 execute(t, wanttty, pipein, pipeout)
36     register struct command *t;
37     int     wanttty, *pipein, *pipeout;
38 {
39     bool    forked = 0;
40     struct biltins *bifunc;
41     int     pid = 0;
42     int     pv[2];
43 
44     static sigset_t csigmask;
45 
46     static sigset_t ocsigmask;
47     static int onosigchld = 0;
48     static int nosigchld = 0;
49 
50     if (t == 0)
51 	return;
52 
53     if (t->t_dflg & F_AMPERSAND)
54 	wanttty = 0;
55     switch (t->t_dtyp) {
56 
57     case NODE_COMMAND:
58 	if ((t->t_dcom[0][0] & (QUOTE | TRIM)) == QUOTE)
59 	    (void) Strcpy(t->t_dcom[0], t->t_dcom[0] + 1);
60 	if ((t->t_dflg & F_REPEAT) == 0)
61 	    Dfix(t);		/* $ " ' \ */
62 	if (t->t_dcom[0] == 0)
63 	    return;
64 	/* fall into... */
65 
66     case NODE_PAREN:
67 	if (t->t_dflg & F_PIPEOUT)
68 	    mypipe(pipeout);
69 	/*
70 	 * Must do << early so parent will know where input pointer should be.
71 	 * If noexec then this is all we do.
72 	 */
73 	if (t->t_dflg & F_READ) {
74 	    (void) close(0);
75 	    heredoc(t->t_dlef);
76 	    if (noexec)
77 		(void) close(0);
78 	}
79 
80 	set(STRstatus, Strsave(STR0));
81 
82 	/*
83 	 * This mess is the necessary kludge to handle the prefix builtins:
84 	 * nice, nohup, time.  These commands can also be used by themselves,
85 	 * and this is not handled here. This will also work when loops are
86 	 * parsed.
87 	 */
88 	while (t->t_dtyp == NODE_COMMAND)
89 	    if (eq(t->t_dcom[0], STRnice))
90 		if (t->t_dcom[1])
91 		    if (strchr("+-", t->t_dcom[1][0]))
92 			if (t->t_dcom[2]) {
93 			    setname("nice");
94 			    t->t_nice =
95 				getn(t->t_dcom[1]);
96 			    lshift(t->t_dcom, 2);
97 			    t->t_dflg |= F_NICE;
98 			}
99 			else
100 			    break;
101 		    else {
102 			t->t_nice = 4;
103 			lshift(t->t_dcom, 1);
104 			t->t_dflg |= F_NICE;
105 		    }
106 		else
107 		    break;
108 	    else if (eq(t->t_dcom[0], STRnohup))
109 		if (t->t_dcom[1]) {
110 		    t->t_dflg |= F_NOHUP;
111 		    lshift(t->t_dcom, 1);
112 		}
113 		else
114 		    break;
115 	    else if (eq(t->t_dcom[0], STRtime))
116 		if (t->t_dcom[1]) {
117 		    t->t_dflg |= F_TIME;
118 		    lshift(t->t_dcom, 1);
119 		}
120 		else
121 		    break;
122 	    else
123 		break;
124 
125 	/* is it a command */
126 	if (t->t_dtyp == NODE_COMMAND) {
127 	    /*
128 	     * Check if we have a builtin function and remember which one.
129 	     */
130 	    bifunc = isbfunc(t);
131  	    if (noexec) {
132 		/*
133 		 * Continue for builtins that are part of the scripting language
134 		 */
135 		if (bifunc->bfunct != dobreak   && bifunc->bfunct != docontin &&
136 		    bifunc->bfunct != doelse    && bifunc->bfunct != doend    &&
137 		    bifunc->bfunct != doforeach && bifunc->bfunct != dogoto   &&
138 		    bifunc->bfunct != doif      && bifunc->bfunct != dorepeat &&
139 		    bifunc->bfunct != doswbrk   && bifunc->bfunct != doswitch &&
140 		    bifunc->bfunct != dowhile   && bifunc->bfunct != dozip)
141 		    break;
142 	    }
143 	}
144 	else {			/* not a command */
145 	    bifunc = NULL;
146 	    if (noexec)
147 		break;
148 	}
149 
150 	/*
151 	 * We fork only if we are timed, or are not the end of a parenthesized
152 	 * list and not a simple builtin function. Simple meaning one that is
153 	 * not pipedout, niced, nohupped, or &'d. It would be nice(?) to not
154 	 * fork in some of these cases.
155 	 */
156 	/*
157 	 * Prevent forking cd, pushd, popd, chdir cause this will cause the
158 	 * shell not to change dir!
159 	 */
160 	if (bifunc && (bifunc->bfunct == dochngd ||
161 		       bifunc->bfunct == dopushd ||
162 		       bifunc->bfunct == dopopd))
163 	    t->t_dflg &= ~(F_NICE);
164 	if (((t->t_dflg & F_TIME) || (t->t_dflg & F_NOFORK) == 0 &&
165 	     (!bifunc || t->t_dflg &
166 	      (F_PIPEOUT | F_AMPERSAND | F_NICE | F_NOHUP))) ||
167 	/*
168 	 * We have to fork for eval too.
169 	 */
170 	    (bifunc && (t->t_dflg & (F_PIPEIN | F_PIPEOUT)) != 0 &&
171 	     bifunc->bfunct == doeval))
172 	    if (t->t_dtyp == NODE_PAREN ||
173 		t->t_dflg & (F_REPEAT | F_AMPERSAND) || bifunc) {
174 		forked++;
175 		/*
176 		 * We need to block SIGCHLD here, so that if the process does
177 		 * not die before we can set the process group
178 		 */
179 		if (wanttty >= 0 && !nosigchld) {
180 		    csigmask = sigblock(sigmask(SIGCHLD));
181 		    nosigchld = 1;
182 		}
183 
184 		pid = pfork(t, wanttty);
185 		if (pid == 0 && nosigchld) {
186 		    (void) sigsetmask(csigmask);
187 		    nosigchld = 0;
188 		}
189 	    }
190 	    else {
191 		int     ochild, osetintr, ohaderr, odidfds;
192 		int     oSHIN, oSHOUT, oSHERR, oOLDSTD, otpgrp;
193 		sigset_t omask;
194 
195 		/*
196 		 * Prepare for the vfork by saving everything that the child
197 		 * corrupts before it exec's. Note that in some signal
198 		 * implementations which keep the signal info in user space
199 		 * (e.g. Sun's) it will also be necessary to save and restore
200 		 * the current sigvec's for the signals the child touches
201 		 * before it exec's.
202 		 */
203 		if (wanttty >= 0 && !nosigchld && !noexec) {
204 		    csigmask = sigblock(sigmask(SIGCHLD));
205 		    nosigchld = 1;
206 		}
207 		omask = sigblock(sigmask(SIGCHLD) | sigmask(SIGINT));
208 		ochild = child;
209 		osetintr = setintr;
210 		ohaderr = haderr;
211 		odidfds = didfds;
212 		oSHIN = SHIN;
213 		oSHOUT = SHOUT;
214 		oSHERR = SHERR;
215 		oOLDSTD = OLDSTD;
216 		otpgrp = tpgrp;
217 		ocsigmask = csigmask;
218 		onosigchld = nosigchld;
219 		Vsav = Vdp = 0;
220 		Vexpath = 0;
221 		Vt = 0;
222 		pid = vfork();
223 
224 		if (pid < 0) {
225 		    (void) sigsetmask(omask);
226 		    stderror(ERR_NOPROC);
227 		}
228 		forked++;
229 		if (pid) {	/* parent */
230 		    child = ochild;
231 		    setintr = osetintr;
232 		    haderr = ohaderr;
233 		    didfds = odidfds;
234 		    SHIN = oSHIN;
235 		    SHOUT = oSHOUT;
236 		    SHERR = oSHERR;
237 		    OLDSTD = oOLDSTD;
238 		    tpgrp = otpgrp;
239 		    csigmask = ocsigmask;
240 		    nosigchld = onosigchld;
241 
242 		    xfree((ptr_t) Vsav);
243 		    Vsav = 0;
244 		    xfree((ptr_t) Vdp);
245 		    Vdp = 0;
246 		    xfree((ptr_t) Vexpath);
247 		    Vexpath = 0;
248 		    blkfree((Char **) Vt);
249 		    Vt = 0;
250 		    /* this is from pfork() */
251 		    palloc(pid, t);
252 		    (void) sigsetmask(omask);
253 		}
254 		else {		/* child */
255 		    /* this is from pfork() */
256 		    int     pgrp;
257 		    bool    ignint = 0;
258 
259 		    if (nosigchld) {
260 			(void) sigsetmask(csigmask);
261 			nosigchld = 0;
262 		    }
263 
264 		    if (setintr)
265 			ignint =
266 			    (tpgrp == -1 &&
267 			     (t->t_dflg & F_NOINTERRUPT))
268 			    || gointr && eq(gointr, STRminus);
269 		    pgrp = pcurrjob ? pcurrjob->p_jobid : getpid();
270 		    child++;
271 		    if (setintr) {
272 			setintr = 0;
273 			if (ignint) {
274 			    (void) signal(SIGINT, SIG_IGN);
275 			    (void) signal(SIGQUIT, SIG_IGN);
276 			}
277 			else {
278 			    (void) signal(SIGINT, vffree);
279 			    (void) signal(SIGQUIT, SIG_DFL);
280 			}
281 
282 			if (wanttty >= 0) {
283 			    (void) signal(SIGTSTP, SIG_DFL);
284 			    (void) signal(SIGTTIN, SIG_DFL);
285 			    (void) signal(SIGTTOU, SIG_DFL);
286 			}
287 
288 			(void) signal(SIGTERM, parterm);
289 		    }
290 		    else if (tpgrp == -1 &&
291 			     (t->t_dflg & F_NOINTERRUPT)) {
292 			(void) signal(SIGINT, SIG_IGN);
293 			(void) signal(SIGQUIT, SIG_IGN);
294 		    }
295 
296 		    pgetty(wanttty, pgrp);
297 		    if (t->t_dflg & F_NOHUP)
298 			(void) signal(SIGHUP, SIG_IGN);
299 		    if (t->t_dflg & F_NICE)
300 			(void) setpriority(PRIO_PROCESS, 0, t->t_nice);
301 		}
302 
303 	    }
304 	if (pid != 0) {
305 	    /*
306 	     * It would be better if we could wait for the whole job when we
307 	     * knew the last process had been started.  Pwait, in fact, does
308 	     * wait for the whole job anyway, but this test doesn't really
309 	     * express our intentions.
310 	     */
311 	    if (didfds == 0 && t->t_dflg & F_PIPEIN) {
312 		(void) close(pipein[0]);
313 		(void) close(pipein[1]);
314 	    }
315 	    if ((t->t_dflg & F_PIPEOUT) == 0) {
316 		if (nosigchld) {
317 		    (void) sigsetmask(csigmask);
318 		    nosigchld = 0;
319 		}
320 		if ((t->t_dflg & F_AMPERSAND) == 0)
321 		    pwait();
322 	    }
323 	    break;
324 	}
325 	doio(t, pipein, pipeout);
326 	if (t->t_dflg & F_PIPEOUT) {
327 	    (void) close(pipeout[0]);
328 	    (void) close(pipeout[1]);
329 	}
330 	/*
331 	 * Perform a builtin function. If we are not forked, arrange for
332 	 * possible stopping
333 	 */
334 	if (bifunc) {
335 	    func(t, bifunc);
336 	    if (forked)
337 		exitstat();
338 	    break;
339 	}
340 	if (t->t_dtyp != NODE_PAREN) {
341 	    doexec(NULL, t);
342 	    /* NOTREACHED */
343 	}
344 	/*
345 	 * For () commands must put new 0,1,2 in FSH* and recurse
346 	 */
347 	OLDSTD = dcopy(0, FOLDSTD);
348 	SHOUT = dcopy(1, FSHOUT);
349 	SHERR = dcopy(2, FSHERR);
350 	(void) close(SHIN);
351 	SHIN = -1;
352 	didfds = 0;
353 	wanttty = -1;
354 	t->t_dspr->t_dflg |= t->t_dflg & F_NOINTERRUPT;
355 	execute(t->t_dspr, wanttty, NULL, NULL);
356 	exitstat();
357 
358     case NODE_PIPE:
359 	t->t_dcar->t_dflg |= F_PIPEOUT |
360 	    (t->t_dflg & (F_PIPEIN | F_AMPERSAND | F_STDERR | F_NOINTERRUPT));
361 	execute(t->t_dcar, wanttty, pipein, pv);
362 	t->t_dcdr->t_dflg |= F_PIPEIN | (t->t_dflg &
363 			(F_PIPEOUT | F_AMPERSAND | F_NOFORK | F_NOINTERRUPT));
364 	if (wanttty > 0)
365 	    wanttty = 0;	/* got tty already */
366 	execute(t->t_dcdr, wanttty, pv, pipeout);
367 	break;
368 
369     case NODE_LIST:
370 	if (t->t_dcar) {
371 	    t->t_dcar->t_dflg |= t->t_dflg & F_NOINTERRUPT;
372 	    execute(t->t_dcar, wanttty, NULL, NULL);
373 	    /*
374 	     * In strange case of A&B make a new job after A
375 	     */
376 	    if (t->t_dcar->t_dflg & F_AMPERSAND && t->t_dcdr &&
377 		(t->t_dcdr->t_dflg & F_AMPERSAND) == 0)
378 		pendjob();
379 	}
380 	if (t->t_dcdr) {
381 	    t->t_dcdr->t_dflg |= t->t_dflg &
382 		(F_NOFORK | F_NOINTERRUPT);
383 	    execute(t->t_dcdr, wanttty, NULL, NULL);
384 	}
385 	break;
386 
387     case NODE_OR:
388     case NODE_AND:
389 	if (t->t_dcar) {
390 	    t->t_dcar->t_dflg |= t->t_dflg & F_NOINTERRUPT;
391 	    execute(t->t_dcar, wanttty, NULL, NULL);
392 	    if ((getn(value(STRstatus)) == 0) !=
393 		(t->t_dtyp == NODE_AND))
394 		return;
395 	}
396 	if (t->t_dcdr) {
397 	    t->t_dcdr->t_dflg |= t->t_dflg &
398 		(F_NOFORK | F_NOINTERRUPT);
399 	    execute(t->t_dcdr, wanttty, NULL, NULL);
400 	}
401 	break;
402     }
403     /*
404      * Fall through for all breaks from switch
405      *
406      * If there will be no more executions of this command, flush all file
407      * descriptors. Places that turn on the F_REPEAT bit are responsible for
408      * doing donefds after the last re-execution
409      */
410     if (didfds && !(t->t_dflg & F_REPEAT))
411 	donefds();
412 }
413 
414 static void
415 vffree(i)
416 int i;
417 {
418     register Char **v;
419 
420     if (v = gargv) {
421 	gargv = 0;
422 	xfree((ptr_t) v);
423     }
424     if (v = pargv) {
425 	pargv = 0;
426 	xfree((ptr_t) v);
427     }
428     _exit(i);
429 }
430 
431 /*
432  * Perform io redirection.
433  * We may or maynot be forked here.
434  */
435 static void
436 doio(t, pipein, pipeout)
437     register struct command *t;
438     int    *pipein, *pipeout;
439 {
440     register int fd;
441     register Char *cp, *dp;
442     register int flags = t->t_dflg;
443 
444     if (didfds || (flags & F_REPEAT))
445 	return;
446     if ((flags & F_READ) == 0) {/* F_READ already done */
447 	if (cp = t->t_dlef) {
448 	    char    tmp[MAXPATHLEN+1];
449 
450 	    /*
451 	     * so < /dev/std{in,out,err} work
452 	     */
453 	    (void) dcopy(SHIN, 0);
454 	    (void) dcopy(SHOUT, 1);
455 	    (void) dcopy(SHERR, 2);
456 	    cp = globone(dp = Dfix1(cp), G_IGNORE);
457 	    (void) strncpy(tmp, short2str(cp), MAXPATHLEN);
458 	    tmp[MAXPATHLEN] = '\0';
459 	    xfree((ptr_t) cp);
460 	    xfree((ptr_t) dp);
461 	    if ((fd = open(tmp, O_RDONLY)) < 0)
462 		stderror(ERR_SYSTEM, tmp, strerror(errno));
463 	    (void) dmove(fd, 0);
464 	}
465 	else if (flags & F_PIPEIN) {
466 	    (void) close(0);
467 	    (void) dup(pipein[0]);
468 	    (void) close(pipein[0]);
469 	    (void) close(pipein[1]);
470 	}
471 	else if ((flags & F_NOINTERRUPT) && tpgrp == -1) {
472 	    (void) close(0);
473 	    (void) open(_PATH_DEVNULL, O_RDONLY);
474 	}
475 	else {
476 	    (void) close(0);
477 	    (void) dup(OLDSTD);
478 	    (void) ioctl(0, FIONCLEX, NULL);
479 	}
480     }
481     if (cp = t->t_drit) {
482 	char    tmp[MAXPATHLEN+1];
483 
484 	cp = globone(dp = Dfix1(cp), G_IGNORE);
485 	(void) strncpy(tmp, short2str(cp), MAXPATHLEN);
486 	tmp[MAXPATHLEN] = '\0';
487 	xfree((ptr_t) cp);
488 	xfree((ptr_t) dp);
489 	/*
490 	 * so > /dev/std{out,err} work
491 	 */
492 	(void) dcopy(SHOUT, 1);
493 	(void) dcopy(SHERR, 2);
494 	if ((flags & F_APPEND) &&
495 #ifdef O_APPEND
496 	    (fd = open(tmp, O_WRONLY | O_APPEND)) >= 0);
497 #else
498 	    (fd = open(tmp, O_WRONLY)) >= 0)
499 	    (void) lseek(1, (off_t) 0, L_XTND);
500 #endif
501 	else {
502 	    if (!(flags & F_OVERWRITE) && adrof(STRnoclobber)) {
503 		if (flags & F_APPEND)
504 		    stderror(ERR_SYSTEM, tmp, strerror(errno));
505 		chkclob(tmp);
506 	    }
507 	    if ((fd = creat(tmp, 0666)) < 0)
508 		stderror(ERR_SYSTEM, tmp, strerror(errno));
509 	}
510 	(void) dmove(fd, 1);
511     }
512     else if (flags & F_PIPEOUT) {
513 	(void) close(1);
514 	(void) dup(pipeout[1]);
515     }
516     else {
517 	(void) close(1);
518 	(void) dup(SHOUT);
519 	(void) ioctl(1, FIONCLEX, NULL);
520     }
521 
522     (void) close(2);
523     if (flags & F_STDERR) {
524 	(void) dup(1);
525     }
526     else {
527 	(void) dup(SHERR);
528 	(void) ioctl(2, FIONCLEX, NULL);
529     }
530     didfds = 1;
531 }
532 
533 void
534 mypipe(pv)
535     register int *pv;
536 {
537 
538     if (pipe(pv) < 0)
539 	goto oops;
540     pv[0] = dmove(pv[0], -1);
541     pv[1] = dmove(pv[1], -1);
542     if (pv[0] >= 0 && pv[1] >= 0)
543 	return;
544 oops:
545     stderror(ERR_PIPE);
546 }
547 
548 static void
549 chkclob(cp)
550     register char *cp;
551 {
552     struct stat stb;
553 
554     if (stat(cp, &stb) < 0)
555 	return;
556     if ((stb.st_mode & S_IFMT) == S_IFCHR)
557 	return;
558     stderror(ERR_EXISTS, cp);
559 }
560