xref: /illumos-gate/usr/src/cmd/csh/sh.sem.c (revision 6c02b4a4)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved  	*/
8 
9 /*
10  * Copyright (c) 1980 Regents of the University of California.
11  * All rights reserved. The Berkeley Software License Agreement
12  * specifies the terms and conditions for redistribution.
13  */
14 
15 #pragma ident	"%Z%%M%	%I%	%E% SMI"
16 
17 #include <unistd.h>
18 #include <fcntl.h>
19 #include "sh.h"
20 #include "sh.proc.h"
21 #include "sh.tconst.h"
22 
23 /*
24  * C shell
25  */
26 
27 void	doio(struct command *, int *, int *);
28 void	mypipe(int *);
29 void	chkclob(tchar *);
30 
31 /*
32  * Return true if there is a back-quote (`) anywhere in the argument list.
33  * Its presence would cause glob() to be invoked in the child process
34  * and this would cause chaos if the child is created with vfork().
35  */
36 static bool
37 AnyBquote(struct command *t)
38 {
39 	tchar **pp;
40 	tchar *p;
41 
42 	if (noexec)
43 		return (0);
44 	for (pp = t->t_dcom; p = *pp++;) {
45 		if (any('`', p))
46 			return (1);
47 	}
48 	return (0);
49 }
50 
51 /*VARARGS 1*/
52 void
53 execute(t, wanttty, pipein, pipeout)
54 	struct command *t;
55 	int wanttty, *pipein, *pipeout;
56 {
57 	bool forked = 0;
58 	struct biltins *bifunc;
59 	int pid = 0;
60 	int pv[2];
61 	extern int globcnt;
62 #ifdef TRACE
63 	tprintf("TRACE- execute()\n");
64 #endif
65 
66 	if (t == 0)
67 		return;
68 	if ((t->t_dflg & FAND) && wanttty > 0)
69 		wanttty = 0;
70 	switch (t->t_dtyp) {
71 
72 	case TCOM:
73 		if (t->t_dcom[0][0] == (tchar)S_TOPBIT[0])
74 			(void) strcpy_(t->t_dcom[0], t->t_dcom[0] + 1);
75 		if ((t->t_dflg & FREDO) == 0)
76 			Dfix(t);		/* $ " ' \ */
77 		if (t->t_dcom[0] == 0)
78 			return;
79 		/* fall into... */
80 
81 	case TPAR:
82 		if (t->t_dflg & FPOU)
83 			mypipe(pipeout);
84 		/*
85 		 * Must do << early so parent will know
86 		 * where input pointer should be.
87 		 * If noexec then this is all we do.
88 		 */
89 		if (t->t_dflg & FHERE) {
90 			(void) close(0);
91 			unsetfd(0);
92 			heredoc(t->t_dlef);
93 			if (noexec) {
94 				(void) close(0);
95 				unsetfd(0);
96 			}
97 		}
98 		if (noexec)
99 			break;
100 
101 		set(S_status, S_0);
102 
103 		/*
104 		 * This mess is the necessary kludge to handle the prefix
105 		 * builtins: nice, nohup, time.  These commands can also
106 		 * be used by themselves, and this is not handled here.
107 		 * This will also work when loops are parsed.
108 		 */
109 		while (t->t_dtyp == TCOM)
110 			if (eq(t->t_dcom[0], S_nice /*"nice"*/))
111 				if (t->t_dcom[1])
112 					/*if (any(t->t_dcom[1][0], "+-"))*/
113 					if (t->t_dcom[1][0] == '+' ||
114 					    t->t_dcom[1][0] == '-')
115 						if (t->t_dcom[2]) {
116 							setname(S_nice /*"nice"*/);
117 							t->t_nice = getn(t->t_dcom[1]);
118 							lshift(t->t_dcom, 2);
119 							t->t_dflg |= FNICE;
120 						} else
121 							break;
122 					else {
123 						t->t_nice = 4;
124 						lshift(t->t_dcom, 1);
125 						t->t_dflg |= FNICE;
126 					}
127 				else
128 					break;
129 			else if (eq(t->t_dcom[0], S_nohup /*"nohup"*/))
130 				if (t->t_dcom[1]) {
131 					t->t_dflg |= FNOHUP;
132 					lshift(t->t_dcom, 1);
133 				} else
134 					break;
135 			else if (eq(t->t_dcom[0], S_time /*"time"*/))
136 				if (t->t_dcom[1]) {
137 					t->t_dflg |= FTIME;
138 					lshift(t->t_dcom, 1);
139 				} else
140 					break;
141 			else
142 				break;
143 		/*
144 		 * Check if we have a builtin function and remember which one.
145 		 */
146 		bifunc = t->t_dtyp == TCOM ? isbfunc(t) : (struct biltins *) 0;
147 
148 		/*
149 		 * We fork only if we are timed, or are not the end of
150 		 * a parenthesized list and not a simple builtin function.
151 		 * Simple meaning one that is not pipedout, niced, nohupped,
152 		 * or &'d.
153 		 * It would be nice(?) to not fork in some of these cases.
154 		 */
155 		if (((t->t_dflg & FTIME) || (t->t_dflg & FPAR) == 0 &&
156 		     (!bifunc || t->t_dflg & (FPOU|FAND|FNICE|FNOHUP))))
157 #ifdef VFORK
158 		    if (t->t_dtyp == TPAR || t->t_dflg&(FREDO|FAND) ||
159 			bifunc || AnyBquote(t))
160 #endif
161 			{ forked++; pid = pfork(t, wanttty); }
162 #ifdef VFORK
163 		    else {
164 			void vffree();
165 			struct sv {
166 				int mask, child, setintr, haderr, didfds;
167 				int SHIN, SHOUT, SHDIAG, OLDSTD, tpgrp;
168 				struct sigvec sigv;
169 			} sv;
170 
171 			/*
172 			 * Prepare for the vfork by saving everything
173 			 * that the child corrupts before it exec's.
174 			 * Note that in some signal implementations
175 			 * which keep the signal info in user space
176 			 * (e.g. Sun's) it will also be necessary to
177  			 * save and restore the current sigvec's for
178 			 * the signals the child touches before it
179 			 * exec's.
180 			 */
181 			sv.mask = sigblock(sigmask(SIGCHLD));
182 			sv.child = child; sv.setintr = setintr;
183 			sv.haderr = haderr; sv.didfds = didfds;
184 			sv.SHIN = SHIN; sv.SHOUT = SHOUT;
185 			sv.SHDIAG = SHDIAG; sv.OLDSTD = OLDSTD;
186 			sv.tpgrp = tpgrp;
187 			Vsav = Vdp = 0; Vav = 0;
188 			(void) sigvec(SIGINT, (struct sigvec *)0, &sv.sigv);
189 			pid = vfork();
190 			if (pid < 0) {
191 				(void) sigsetmask(sv.mask);
192 				error("Vfork failed");
193 			}
194 			forked++;
195 			if (pid) {	/* parent */
196 				int ppid;
197 				closelog();
198 				child = sv.child; setintr = sv.setintr;
199 				haderr = sv.haderr; didfds = sv.didfds;
200 				SHIN = sv.SHIN;
201 				SHOUT = sv.SHOUT; SHDIAG = sv.SHDIAG;
202 				OLDSTD = sv.OLDSTD; tpgrp = sv.tpgrp;
203 				xfree(Vsav); Vsav = 0;
204 				xfree(Vdp); Vdp = 0;
205 				xfree( (tchar *)Vav); Vav = 0;
206 				/* this is from pfork() */
207 				ppid = pcurrjob ? pcurrjob->p_jobid : pid;
208 				if (wanttty >= 0 && tpgrp >= 0)
209 					setpgid (ppid, ppid);
210 				palloc(pid, t);
211 				/*
212 				 * Restore SIGINT handler.
213 				 */
214 				(void) sigvec(SIGINT, &sv.sigv, (struct sigvec *)0);
215 				(void) sigsetmask(sv.mask);
216 			} else {	/* child */
217 				/* this is from pfork() */
218 				int pgrp;
219 				bool ignint = 0;
220 				int sigttou;
221 				if (setintr)
222 					ignint =
223 					    (tpgrp == -1 && (t->t_dflg&FINT))
224 					    || gointr
225 						&& eq(gointr, S_MINUS/*"-"*/);
226 				pgrp = pcurrjob ? pcurrjob->p_jobid : getpid();
227 				child++;
228 				if (setintr) {
229 					setintr = 0;
230 #ifdef notdef
231 					(void) signal(SIGCHLD, SIG_DFL);
232 #endif
233 					(void) signal(SIGINT, ignint ?
234 						SIG_IGN : vffree);
235 					(void) signal(SIGQUIT, ignint ?
236 						SIG_IGN : SIG_DFL);
237 					if (wanttty >= 0) {
238 						(void) signal(SIGTSTP, SIG_DFL);
239 						(void) signal(SIGTTIN, SIG_DFL);
240 						(void) signal(SIGTTOU, SIG_DFL);
241 					}
242 					(void) signal(SIGTERM, parterm);
243 				} else if (tpgrp == -1 && (t->t_dflg&FINT)) {
244 					(void) signal(SIGINT, SIG_IGN);
245 					(void) signal(SIGQUIT, SIG_IGN);
246 				}
247 				if (wanttty >= 0 && tpgrp >= 0)
248 					(void) setpgid(0, pgrp);
249 				if (wanttty > 0) {
250 					sigttou = sigblock (
251 						sigmask(SIGTTOU) |
252 						sigmask(SIGTTIN) |
253 						sigmask(SIGTSTP));
254 					(void) ioctl(FSHTTY, TIOCSPGRP,
255 						 (tchar *)&pgrp);
256 					sigsetmask (sigttou);
257 				}
258 				if (tpgrp > 0)
259 					tpgrp = 0;
260 				if (t->t_dflg & FNOHUP)
261 					(void) signal(SIGHUP, SIG_IGN);
262 				if (t->t_dflg & FNICE)
263 					(void) setpriority(PRIO_PROCESS,
264 						0, t->t_nice);
265 			}
266 
267 		    }
268 #endif
269 		if (pid != 0) {
270 			/*
271 			 * It would be better if we could wait for the
272 			 * whole job when we knew the last process
273 			 * had been started.  Pwait, in fact, does
274 			 * wait for the whole job anyway, but this test
275 			 * doesn't really express our intentions.
276 			 */
277 			if (didfds==0 && t->t_dflg&FPIN) {
278 				(void) close(pipein[0]);
279 				unsetfd(pipein[0]);
280 				(void) close(pipein[1]);
281 				unsetfd(pipein[1]);
282 			}
283 			if ((t->t_dflg & (FPOU|FAND)) == 0)
284 				pwait();
285 			break;
286 		}
287 		doio(t, pipein, pipeout);
288 		if (t->t_dflg & FPOU) {
289 			(void) close(pipeout[0]);
290 			(void) unsetfd(pipeout[0]);
291 			(void) close(pipeout[1]);
292 			(void) unsetfd(pipeout[1]);
293 		}
294 
295 		/*
296 		 * Perform a builtin function.
297 		 * If we are not forked, arrange for possible stopping
298 		 */
299 		if (bifunc) {
300 			func(t, bifunc);
301 			if (forked)
302 				exitstat();
303 			break;
304 		}
305 		if (t->t_dtyp != TPAR) {
306 			doexec(t);
307 			/*NOTREACHED*/
308 		}
309 		/*
310 		 * For () commands must put new 0,1,2 in FSH* and recurse
311 		 */
312 		OLDSTD = dcopy(0, FOLDSTD);
313 		SHOUT = dcopy(1, FSHOUT);
314 		SHDIAG = dcopy(2, FSHDIAG);
315 		(void) close(SHIN);
316 		(void) unsetfd(SHIN);
317 		SHIN = -1;
318 		didfds = 0;
319 		wanttty = -1;
320 		t->t_dspr->t_dflg |= t->t_dflg & FINT;
321 		execute(t->t_dspr, wanttty);
322 		exitstat();
323 
324 	case TFIL:
325 		t->t_dcar->t_dflg |= FPOU |
326 		    (t->t_dflg & (FPIN|FAND|FDIAG|FINT));
327 		execute(t->t_dcar, wanttty, pipein, pv);
328 		t->t_dcdr->t_dflg |= FPIN |
329 		    (t->t_dflg & (FPOU|FAND|FPAR|FINT));
330 		if (wanttty > 0)
331 			wanttty = 0;		/* got tty already */
332 		execute(t->t_dcdr, wanttty, pv, pipeout);
333 		break;
334 
335 	case TLST:
336 		if (t->t_dcar) {
337 			t->t_dcar->t_dflg |= t->t_dflg & FINT;
338 			execute(t->t_dcar, wanttty);
339 			/*
340 			 * In strange case of A&B make a new job after A
341 			 */
342 			if (t->t_dcar->t_dflg&FAND && t->t_dcdr &&
343 			    (t->t_dcdr->t_dflg&FAND) == 0)
344 				pendjob();
345 		}
346 		if (t->t_dcdr) {
347 			t->t_dcdr->t_dflg |= t->t_dflg & (FPAR|FINT);
348 			execute(t->t_dcdr, wanttty);
349 		}
350 		break;
351 
352 	case TOR:
353 	case TAND:
354 		if (t->t_dcar) {
355 			t->t_dcar->t_dflg |= t->t_dflg & FINT;
356 			execute(t->t_dcar, wanttty);
357 			if ((getn(value(S_status/*"status"*/)) == 0) != (t->t_dtyp == TAND))
358 				return;
359 		}
360 		if (t->t_dcdr) {
361 			t->t_dcdr->t_dflg |= t->t_dflg & (FPAR|FINT);
362 			execute(t->t_dcdr, wanttty);
363 		}
364 		break;
365 	}
366 	/*
367 	 * Fall through for all breaks from switch
368 	 *
369 	 * If there will be no more executions of this
370 	 * command, flush all file descriptors.
371 	 * Places that turn on the FREDO bit are responsible
372 	 * for doing donefds after the last re-execution
373 	 */
374 	if (didfds && !(t->t_dflg & FREDO))
375 		donefds();
376 
377 	/*
378 	 * If glob() was called and arguments list is not yet
379 	 * free'ed, free them here.
380 	 */
381 	if (gargv) {
382 		blkfree(gargv);
383 		gargv = 0;
384 		globcnt = 0;
385 	}
386 }
387 
388 #ifdef VFORK
389 void
390 vffree(void)
391 {
392 	tchar **v;
393 
394 #ifdef TRACE
395 	tprintf("TRACE- vffree()\n");
396 #endif
397 	if (v = gargv)
398 		gargv = 0, xfree( (tchar *)v);
399 	if (v = pargv)
400 		pargv = 0, xfree( (tchar *)v);
401 	_exit(1);
402 }
403 #endif
404 
405 /*
406  * Perform io redirection.
407  * We may or maynot be forked here.
408  */
409 void
410 doio(struct command *t, int *pipein, int *pipeout)
411 {
412 	tchar *cp, *dp;
413 	int flags = t->t_dflg;
414 	int fd;
415 
416 #ifdef TRACE
417 	tprintf("TRACE- doio()\n");
418 #endif
419 	if (didfds || (flags & FREDO))
420 		return;
421 	if ((flags & FHERE) == 0) {	/* FHERE already done */
422 		(void) close(0);
423 		(void) unsetfd(0);
424 		if (cp = t->t_dlef) {
425 			dp = Dfix1(cp);
426 			cp = globone(dp);
427 			xfree(dp);
428 			xfree(cp);
429 			if (open_(cp, 0) < 0)
430 				Perror(cp);
431 		} else if (flags & FPIN) {
432 			fd = dup(pipein[0]);
433 			if (fd != -1)
434 				setfd(fd);
435 			(void) close(pipein[0]);
436 			(void) unsetfd(pipein[0]);
437 			(void) close(pipein[1]);
438 			(void) unsetfd(pipein[1]);
439 		} else if ((flags & FINT) && tpgrp == -1) {
440 			(void) close(0);	/* no need for unsetfd */
441 			(void) open("/dev/null", 0); /* no need for setfd */
442 		} else {
443 			fd = dup(OLDSTD);
444 			if (fd != -1)
445 				setfd(fd);
446 		}
447 	}
448 	(void) close(1);
449 	(void) unsetfd(1);
450 	if (cp = t->t_drit) {
451 		dp = Dfix1(cp);
452 		cp = globone(dp);
453 		xfree(dp);
454 		if ((flags & FCAT) && open_(cp, 1) >= 0)
455 			(void) lseek(1, (off_t)0, 2);
456 		else {
457 			if (!(flags & FANY) && adrof(S_noclobber/*"noclobber"*/)) {
458 				if (flags & FCAT)
459 					Perror(cp);
460 				chkclob(cp);
461 			}
462 			if (creat_(cp, 0666) < 0)
463 				Perror(cp);
464 		}
465 		xfree(cp);
466 	} else if (flags & FPOU) {
467 		fd = dup(pipeout[1]);
468 		if (fd != -1)
469 			setfd (fd);
470 	}
471 	else {
472 		fd = dup(SHOUT);
473 		if (fd != -1)
474 			setfd(fd);
475 	}
476 
477 	(void) close(2);
478 	(void) unsetfd(2);
479 	if (flags & FDIAG) {
480 		fd = dup(1);
481 		if (fd != -1)
482 			setfd(fd);
483 	}
484 	else {
485 		fd = dup(SHDIAG);
486 		if (fd != -1)
487 			setfd(fd);
488 	}
489 	didfds = 1;
490 }
491 
492 void
493 mypipe(int *pv)
494 {
495 
496 #ifdef TRACE
497 	tprintf("TRACE- mypipe()\n");
498 #endif
499 	if (pipe(pv) < 0)
500 		goto oops;
501 	setfd(pv[0]);
502 	setfd(pv[1]);
503 
504 	pv[0] = dmove(pv[0], -1);
505 	pv[1] = dmove(pv[1], -1);
506 	if (pv[0] >= 0 && pv[1] >= 0)
507 		return;
508 oops:
509 	error("Can't make pipe");
510 }
511 
512 void
513 chkclob(tchar *cp)
514 {
515 	struct stat stb;
516 	unsigned short	type;
517 
518 #ifdef TRACE
519 	tprintf("TRACE- chkclob()\n");
520 #endif
521 	if (stat_(cp, &stb) < 0)
522 		return;
523 	type = stb.st_mode & S_IFMT;
524 	if (type == S_IFCHR || type == S_IFIFO)
525 		return;
526 	error("%t: File exists", cp);
527 }
528