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