xref: /netbsd/bin/sh/redir.c (revision bec2ec26)
1*bec2ec26Skre /*	$NetBSD: redir.c,v 1.72 2021/11/22 05:17:43 kre Exp $	*/
249f0ad86Scgd 
361f28255Scgd /*-
437ed7877Sjtc  * Copyright (c) 1991, 1993
537ed7877Sjtc  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * This code is derived from software contributed to Berkeley by
861f28255Scgd  * Kenneth Almquist.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
18b5b29542Sagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
35cd799663Schristos #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
3749f0ad86Scgd #if 0
3807bae7edSchristos static char sccsid[] = "@(#)redir.c	8.2 (Berkeley) 5/4/95";
3949f0ad86Scgd #else
40*bec2ec26Skre __RCSID("$NetBSD: redir.c,v 1.72 2021/11/22 05:17:43 kre Exp $");
4149f0ad86Scgd #endif
4261f28255Scgd #endif /* not lint */
4361f28255Scgd 
4407bae7edSchristos #include <sys/types.h>
45d06f1504Schristos #include <sys/param.h>	/* PIPE_BUF */
46b2deebf3Schristos #include <sys/stat.h>
4707bae7edSchristos #include <signal.h>
4807bae7edSchristos #include <string.h>
4907bae7edSchristos #include <fcntl.h>
5007bae7edSchristos #include <errno.h>
5107bae7edSchristos #include <unistd.h>
5207bae7edSchristos #include <stdlib.h>
5307bae7edSchristos 
5461f28255Scgd /*
5561f28255Scgd  * Code for dealing with input/output redirection.
5661f28255Scgd  */
5761f28255Scgd 
586f482334Schristos #include "main.h"
59949a12ecSchristos #include "builtins.h"
6061f28255Scgd #include "shell.h"
6161f28255Scgd #include "nodes.h"
6261f28255Scgd #include "jobs.h"
63f629aa28Schristos #include "options.h"
6461f28255Scgd #include "expand.h"
6561f28255Scgd #include "redir.h"
6661f28255Scgd #include "output.h"
6761f28255Scgd #include "memalloc.h"
6870946d25Skre #include "mystring.h"
6961f28255Scgd #include "error.h"
70df7ac856Skre #include "show.h"
7161f28255Scgd 
7261f28255Scgd 
7361f28255Scgd #define EMPTY -2		/* marks an unused slot in redirtab */
74f06749faSyamt #define CLOSED -1		/* fd was not open before redir */
75d06f1504Schristos #ifndef PIPE_BUF
7661f28255Scgd # define PIPESIZE 4096		/* amount of buffering in a pipe */
77d06f1504Schristos #else
78d06f1504Schristos # define PIPESIZE PIPE_BUF
79d06f1504Schristos #endif
8061f28255Scgd 
81dbb7acccSkre #ifndef FD_CLOEXEC
82dbb7acccSkre # define FD_CLOEXEC	1	/* well known from before there was a name */
83dbb7acccSkre #endif
84dbb7acccSkre 
85dbb7acccSkre #ifndef F_DUPFD_CLOEXEC
86dbb7acccSkre #define F_DUPFD_CLOEXEC	F_DUPFD
87dbb7acccSkre #define CLOEXEC(fd)	(fcntl((fd), F_SETFD, fcntl((fd),F_GETFD) | FD_CLOEXEC))
88dbb7acccSkre #else
89dbb7acccSkre #define CLOEXEC(fd)
90dbb7acccSkre #endif
91dbb7acccSkre 
9261f28255Scgd 
9361f28255Scgd MKINIT
942f19bf49Schristos struct renamelist {
952f19bf49Schristos 	struct renamelist *next;
962f19bf49Schristos 	int orig;
972f19bf49Schristos 	int into;
982f19bf49Schristos };
992f19bf49Schristos 
1002f19bf49Schristos MKINIT
10161f28255Scgd struct redirtab {
10261f28255Scgd 	struct redirtab *next;
1032f19bf49Schristos 	struct renamelist *renamed;
10461f28255Scgd };
10561f28255Scgd 
10661f28255Scgd 
10761f28255Scgd MKINIT struct redirtab *redirlist;
10861f28255Scgd 
10937ed7877Sjtc /*
11037ed7877Sjtc  * We keep track of whether or not fd0 has been redirected.  This is for
11137ed7877Sjtc  * background commands, where we want to redirect fd0 to /dev/null only
11237ed7877Sjtc  * if it hasn't already been redirected.
11337ed7877Sjtc  */
1142f19bf49Schristos STATIC int fd0_redirected = 0;
11561f28255Scgd 
1162f19bf49Schristos /*
1172f19bf49Schristos  * And also where to put internal use fds that should be out of the
1182f19bf49Schristos  * way of user defined fds (normally)
1192f19bf49Schristos  */
1202f19bf49Schristos STATIC int big_sh_fd = 0;
1212f19bf49Schristos 
1222f19bf49Schristos STATIC const struct renamelist *is_renamed(const struct renamelist *, int);
1232f19bf49Schristos STATIC void fd_rename(struct redirtab *, int, int);
124852ae738Skre STATIC int * saved_redirected_fd(int);
1252f19bf49Schristos STATIC void free_rl(struct redirtab *, int);
1265dd90992Schristos STATIC void openredirect(union node *, char[10], int);
127366c0bc2Syamt STATIC int openhere(const union node *);
1286183d0e0Skre STATIC int copyfd(int, int, int);
1292f19bf49Schristos STATIC void find_big_fd(void);
13061f28255Scgd 
1316bc33f84Skre 
1326bc33f84Skre struct shell_fds {		/* keep track of internal shell fds */
1336bc33f84Skre 	struct shell_fds *nxt;
1346bc33f84Skre 	void (*cb)(int, int);
1356bc33f84Skre 	int fd;
1366bc33f84Skre };
1376bc33f84Skre 
1386bc33f84Skre STATIC struct shell_fds *sh_fd_list;
1396bc33f84Skre 
140852ae738Skre STATIC int pick_new_fd(int);
1416bc33f84Skre STATIC void renumber_sh_fd(struct shell_fds *);
1426bc33f84Skre STATIC struct shell_fds *sh_fd(int);
1436bc33f84Skre 
1442f19bf49Schristos STATIC const struct renamelist *
is_renamed(const struct renamelist * rl,int fd)1452f19bf49Schristos is_renamed(const struct renamelist *rl, int fd)
1462f19bf49Schristos {
1472f19bf49Schristos 	while (rl != NULL) {
1482f19bf49Schristos 		if (rl->orig == fd)
1492f19bf49Schristos 			return rl;
1502f19bf49Schristos 		rl = rl->next;
1512f19bf49Schristos 	}
1522f19bf49Schristos 	return NULL;
1532f19bf49Schristos }
1542f19bf49Schristos 
155852ae738Skre STATIC int *
saved_redirected_fd(int fd)156852ae738Skre saved_redirected_fd(int fd)
157852ae738Skre {
158852ae738Skre 	struct redirtab *rt;
159852ae738Skre 	struct renamelist *rl;
160852ae738Skre 
161852ae738Skre 	for (rt = redirlist; rt != NULL; rt = rt->next) {
162852ae738Skre 		for (rl =  rt->renamed; rl != NULL; rl = rl->next) {
163852ae738Skre 			if (rl->into == fd)
164852ae738Skre 				return &rl->into;
165852ae738Skre 		}
166852ae738Skre 	}
167852ae738Skre 	return NULL;
168852ae738Skre }
169852ae738Skre 
1702f19bf49Schristos STATIC void
free_rl(struct redirtab * rt,int reset)1712f19bf49Schristos free_rl(struct redirtab *rt, int reset)
1722f19bf49Schristos {
1732f19bf49Schristos 	struct renamelist *rl, *rn = rt->renamed;
1742f19bf49Schristos 
1752f19bf49Schristos 	while ((rl = rn) != NULL) {
1762f19bf49Schristos 		rn = rl->next;
1772f19bf49Schristos 		if (rl->orig == 0)
1782f19bf49Schristos 			fd0_redirected--;
1799caa685bSkre 		VTRACE(DBG_REDIR, ("popredir %d%s: %s",
1809caa685bSkre 		    rl->orig, rl->orig==0 ? " (STDIN)" : "",
1819caa685bSkre 		    reset ? "" : "no reset\n"));
1822f19bf49Schristos 		if (reset) {
1839caa685bSkre 			if (rl->into < 0) {
1849caa685bSkre 				VTRACE(DBG_REDIR, ("closed\n"));
1852f19bf49Schristos 				close(rl->orig);
1869caa685bSkre 			} else {
1879caa685bSkre 				VTRACE(DBG_REDIR, ("from %d\n", rl->into));
1882f19bf49Schristos 				movefd(rl->into, rl->orig);
1892f19bf49Schristos 			}
1909caa685bSkre 		}
1912f19bf49Schristos 		ckfree(rl);
1922f19bf49Schristos 	}
1932f19bf49Schristos 	rt->renamed = NULL;
1942f19bf49Schristos }
1952f19bf49Schristos 
1962f19bf49Schristos STATIC void
fd_rename(struct redirtab * rt,int from,int to)1972f19bf49Schristos fd_rename(struct redirtab *rt, int from, int to)
1982f19bf49Schristos {
1999caa685bSkre 	/* XXX someday keep a short list (8..10) of freed renamelists XXX */
2002f19bf49Schristos 	struct renamelist *rl = ckmalloc(sizeof(struct renamelist));
2012f19bf49Schristos 
2022f19bf49Schristos 	rl->next = rt->renamed;
2032f19bf49Schristos 	rt->renamed = rl;
2042f19bf49Schristos 
2052f19bf49Schristos 	rl->orig = from;
2062f19bf49Schristos 	rl->into = to;
2072f19bf49Schristos }
20861f28255Scgd 
20961f28255Scgd /*
21061f28255Scgd  * Process a list of redirection commands.  If the REDIR_PUSH flag is set,
21161f28255Scgd  * old file descriptors are stashed away so that the redirection can be
21261f28255Scgd  * undone by calling popredir.  If the REDIR_BACKQ flag is set, then the
21361f28255Scgd  * standard output, and the standard error if it becomes a duplicate of
21461f28255Scgd  * stdout, is saved in memory.
21561f28255Scgd  */
21661f28255Scgd 
21761f28255Scgd void
redirect(union node * redir,int flags)218c02b3bbdSchristos redirect(union node *redir, int flags)
21961f28255Scgd {
22061f28255Scgd 	union node *n;
221cd799663Schristos 	struct redirtab *sv = NULL;
22261f28255Scgd 	int i;
22361f28255Scgd 	int fd;
22461f28255Scgd 	char memory[10];	/* file descriptors to write to memory */
22561f28255Scgd 
2269caa685bSkre 	CTRACE(DBG_REDIR, ("redirect(F=0x%x):%s\n", flags, redir?"":" NONE"));
22761f28255Scgd 	for (i = 10 ; --i >= 0 ; )
22861f28255Scgd 		memory[i] = 0;
22961f28255Scgd 	memory[1] = flags & REDIR_BACKQ;
23061f28255Scgd 	if (flags & REDIR_PUSH) {
2319caa685bSkre 		/*
2329caa685bSkre 		 * We don't have to worry about REDIR_VFORK here, as
233edcb4544Schristos 		 * flags & REDIR_PUSH is never true if REDIR_VFORK is set.
234edcb4544Schristos 		 */
23561f28255Scgd 		sv = ckmalloc(sizeof (struct redirtab));
2362f19bf49Schristos 		sv->renamed = NULL;
23761f28255Scgd 		sv->next = redirlist;
23861f28255Scgd 		redirlist = sv;
23961f28255Scgd 	}
24061f28255Scgd 	for (n = redir ; n ; n = n->nfile.next) {
241852ae738Skre 		int *renamed;
242852ae738Skre 
24361f28255Scgd 		fd = n->nfile.fd;
244852ae738Skre 		VTRACE(DBG_REDIR, ("redir %d (max=%d limit=%ld) ",
245852ae738Skre 		    fd, max_user_fd, user_fd_limit));
246852ae738Skre 		if (fd < user_fd_limit && fd > max_user_fd)
247eb5ab741Skre 			max_user_fd = fd;
2480c0cad49Skre 		if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
2490c0cad49Skre 		    n->ndup.dupfd == fd) {
2500c0cad49Skre 			VTRACE(DBG_REDIR, ("!cloexec\n"));
2510c0cad49Skre 			if (sh_fd(fd) != NULL ||
2520c0cad49Skre 			    saved_redirected_fd(fd) != NULL)
2530c0cad49Skre 				error("fd %d: %s", fd, strerror(EBADF));
2540c0cad49Skre 			/* redirect from/to same file descriptor */
2550c0cad49Skre 			/* make sure it stays open */
2560c0cad49Skre 			if (fcntl(fd, F_SETFD, 0) < 0)
2570c0cad49Skre 				error("fd %d: %s", fd, strerror(errno));
2580c0cad49Skre 			continue;
2590c0cad49Skre 		}
260852ae738Skre 		if ((renamed = saved_redirected_fd(fd)) != NULL) {
261852ae738Skre 			int to = pick_new_fd(fd);
262852ae738Skre 
263852ae738Skre 			VTRACE(DBG_REDIR,
264852ae738Skre 			    ("redirect: moved holding fd %d to %d\n", fd, to));
265852ae738Skre 			*renamed = to;
266852ae738Skre 			if (to != fd)	/* always... */
267852ae738Skre 				(void)close(fd);
268852ae738Skre 		}
2696bc33f84Skre 		renumber_sh_fd(sh_fd(fd));
270fb758fd8Schristos 
2712f19bf49Schristos 		if ((flags & REDIR_PUSH) && !is_renamed(sv->renamed, fd)) {
272285af6fdSkre 			int bigfd;
273285af6fdSkre 
27461f28255Scgd 			INTOFF;
2752f19bf49Schristos 			if (big_sh_fd < 10)
2762f19bf49Schristos 				find_big_fd();
277285af6fdSkre 			if ((bigfd = big_sh_fd) < max_user_fd)
278285af6fdSkre 				bigfd = max_user_fd;
279285af6fdSkre 			if ((i = fcntl(fd, F_DUPFD, bigfd + 1)) == -1) {
280fb758fd8Schristos 				switch (errno) {
281fb758fd8Schristos 				case EBADF:
282f06749faSyamt 					i = CLOSED;
283f06749faSyamt 					break;
2842f19bf49Schristos 				case EMFILE:
2852f19bf49Schristos 				case EINVAL:
2862f19bf49Schristos 					find_big_fd();
2872f19bf49Schristos 					i = fcntl(fd, F_DUPFD, big_sh_fd);
2882f19bf49Schristos 					if (i >= 0)
2892f19bf49Schristos 						break;
290852ae738Skre 					if (errno == EMFILE || errno == EINVAL)
291852ae738Skre 						i = fcntl(fd, F_DUPFD, 3);
292852ae738Skre 					if (i >= 0)
293852ae738Skre 						break;
2942f19bf49Schristos 					/* FALLTHRU */
295fb758fd8Schristos 				default:
296285af6fdSkre 					error("%d: %s", fd, strerror(errno));
297ee9e50eaSmycroft 					/* NOTREACHED */
298fb758fd8Schristos 				}
2992f19bf49Schristos 			}
3002f19bf49Schristos 			if (i >= 0)
301f06749faSyamt 				(void)fcntl(i, F_SETFD, FD_CLOEXEC);
3022f19bf49Schristos 			fd_rename(sv, fd, i);
303852ae738Skre 			VTRACE(DBG_REDIR, ("fd %d saved as %d ", fd, i));
30461f28255Scgd 			INTON;
30561f28255Scgd 		}
3069caa685bSkre 		VTRACE(DBG_REDIR, ("%s\n", fd == 0 ? "STDIN" : ""));
3075916a085Ssef 		if (fd == 0)
3085916a085Ssef 			fd0_redirected++;
3095dd90992Schristos 		openredirect(n, memory, flags);
31061f28255Scgd 	}
31161f28255Scgd 	if (memory[1])
31261f28255Scgd 		out1 = &memout;
31361f28255Scgd 	if (memory[2])
31461f28255Scgd 		out2 = &memout;
31561f28255Scgd }
31661f28255Scgd 
31761f28255Scgd 
31861f28255Scgd STATIC void
openredirect(union node * redir,char memory[10],int flags)3195dd90992Schristos openredirect(union node *redir, char memory[10], int flags)
32061f28255Scgd {
32199ddda90Schristos 	struct stat sb;
32261f28255Scgd 	int fd = redir->nfile.fd;
32361f28255Scgd 	char *fname;
32461f28255Scgd 	int f;
325efdc69a2Schristos 	int eflags, cloexec;
32661f28255Scgd 
32761f28255Scgd 	/*
32861f28255Scgd 	 * We suppress interrupts so that we won't leave open file
32961f28255Scgd 	 * descriptors around.  This may not be such a good idea because
33061f28255Scgd 	 * an open of a device or a fifo can block indefinitely.
33161f28255Scgd 	 */
33261f28255Scgd 	INTOFF;
3332f19bf49Schristos 	if (fd < 10)
33461f28255Scgd 		memory[fd] = 0;
33561f28255Scgd 	switch (redir->nfile.type) {
33661f28255Scgd 	case NFROM:
33761f28255Scgd 		fname = redir->nfile.expfname;
3385dd90992Schristos 		if (flags & REDIR_VFORK)
3395dd90992Schristos 			eflags = O_NONBLOCK;
3405dd90992Schristos 		else
3415dd90992Schristos 			eflags = 0;
3425dd90992Schristos 		if ((f = open(fname, O_RDONLY|eflags)) < 0)
3436e50d7a8Schristos 			goto eopen;
344df7ac856Skre 		VTRACE(DBG_REDIR, ("openredirect(< '%s') -> %d [%#x]",
345df7ac856Skre 		    fname, f, eflags));
3465dd90992Schristos 		if (eflags)
3475dd90992Schristos 			(void)fcntl(f, F_SETFL, fcntl(f, F_GETFL, 0) & ~eflags);
3486e50d7a8Schristos 		break;
3496e50d7a8Schristos 	case NFROMTO:
3506e50d7a8Schristos 		fname = redir->nfile.expfname;
351e338cf0cSkre 		if ((f = open(fname, O_RDWR|O_CREAT, 0666)) < 0)
3526e50d7a8Schristos 			goto ecreate;
353df7ac856Skre 		VTRACE(DBG_REDIR, ("openredirect(<> '%s') -> %d", fname, f));
35461f28255Scgd 		break;
35561f28255Scgd 	case NTO:
35699ddda90Schristos 		if (Cflag) {
35799ddda90Schristos 			fname = redir->nfile.expfname;
3588110a864Schristos 			if ((f = open(fname, O_WRONLY)) == -1) {
35999ddda90Schristos 				if ((f = open(fname, O_WRONLY|O_CREAT|O_EXCL,
36099ddda90Schristos 				    0666)) < 0)
36199ddda90Schristos 					goto ecreate;
3628110a864Schristos 			} else if (fstat(f, &sb) == -1) {
3638110a864Schristos 				int serrno = errno;
36499ddda90Schristos 				close(f);
3658110a864Schristos 				errno = serrno;
36699ddda90Schristos 				goto ecreate;
3678110a864Schristos 			} else if (S_ISREG(sb.st_mode)) {
3688110a864Schristos 				close(f);
36999ddda90Schristos 				errno = EEXIST;
37099ddda90Schristos 				goto ecreate;
37199ddda90Schristos 			}
3729caa685bSkre 			VTRACE(DBG_REDIR, ("openredirect(>| '%s') -> %d",
3739caa685bSkre 			    fname, f));
37499ddda90Schristos 			break;
37599ddda90Schristos 		}
376f629aa28Schristos 		/* FALLTHROUGH */
377f629aa28Schristos 	case NCLOBBER:
37861f28255Scgd 		fname = redir->nfile.expfname;
37999ddda90Schristos 		if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
3806e50d7a8Schristos 			goto ecreate;
381df7ac856Skre 		VTRACE(DBG_REDIR, ("openredirect(> '%s') -> %d", fname, f));
3826e50d7a8Schristos 		break;
38361f28255Scgd 	case NAPPEND:
38461f28255Scgd 		fname = redir->nfile.expfname;
38561f28255Scgd 		if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
3866e50d7a8Schristos 			goto ecreate;
387df7ac856Skre 		VTRACE(DBG_REDIR, ("openredirect(>> '%s') -> %d", fname, f));
3886e50d7a8Schristos 		break;
38961f28255Scgd 	case NTOFD:
39061f28255Scgd 	case NFROMFD:
39161f28255Scgd 		if (redir->ndup.dupfd >= 0) {	/* if not ">&-" */
392852ae738Skre 			if (sh_fd(redir->ndup.dupfd) != NULL ||
393852ae738Skre 			    saved_redirected_fd(redir->ndup.dupfd) != NULL)
394285af6fdSkre 				error("Redirect (from %d to %d) failed: %s",
395285af6fdSkre 				    redir->ndup.dupfd, fd, strerror(EBADF));
3962f19bf49Schristos 			if (fd < 10 && redir->ndup.dupfd < 10 &&
3972f19bf49Schristos 			    memory[redir->ndup.dupfd])
39861f28255Scgd 				memory[fd] = 1;
3996183d0e0Skre 			else if (copyfd(redir->ndup.dupfd, fd,
400194ba917Schristos 			    (flags & REDIR_KEEP) == 0) < 0)
4016183d0e0Skre 				error("Redirect (from %d to %d) failed: %s",
4026183d0e0Skre 				    redir->ndup.dupfd, fd, strerror(errno));
403df7ac856Skre 			VTRACE(DBG_REDIR, ("openredirect: %d%c&%d\n", fd,
404df7ac856Skre 			    "<>"[redir->nfile.type==NTOFD], redir->ndup.dupfd));
405df7ac856Skre 		} else {
4066183d0e0Skre 			(void) close(fd);
407df7ac856Skre 			VTRACE(DBG_REDIR, ("openredirect: %d%c&-\n", fd,
408df7ac856Skre 			    "<>"[redir->nfile.type==NTOFD]));
409df7ac856Skre 		}
4106e50d7a8Schristos 		INTON;
4116e50d7a8Schristos 		return;
41261f28255Scgd 	case NHERE:
41361f28255Scgd 	case NXHERE:
414df7ac856Skre 		VTRACE(DBG_REDIR, ("openredirect: %d<<...", fd));
4159caa685bSkre 		f = openhere(redir);
4166e50d7a8Schristos 		break;
41761f28255Scgd 	default:
41861f28255Scgd 		abort();
41961f28255Scgd 	}
4206e50d7a8Schristos 
4210cfd6d3dSkre 	cloexec = fd > 2 && (flags & REDIR_KEEP) == 0 && !posix;
4226e50d7a8Schristos 	if (f != fd) {
423df7ac856Skre 		VTRACE(DBG_REDIR, (" -> %d", fd));
4246183d0e0Skre 		if (copyfd(f, fd, cloexec) < 0) {
4256183d0e0Skre 			int e = errno;
4266183d0e0Skre 
4270c0cad49Skre 			VTRACE(DBG_REDIR, (" failed: %s\n", strerror(e)));
4286183d0e0Skre 			close(f);
4296183d0e0Skre 			error("redirect reassignment (fd %d) failed: %s", fd,
4306183d0e0Skre 			    strerror(e));
4316183d0e0Skre 		}
4326e50d7a8Schristos 		close(f);
433efdc69a2Schristos 	} else if (cloexec)
4344db07438Schristos 		(void)fcntl(f, F_SETFD, FD_CLOEXEC);
435df7ac856Skre 	VTRACE(DBG_REDIR, ("%s\n", cloexec ? " cloexec" : ""));
4364db07438Schristos 
43761f28255Scgd 	INTON;
4386e50d7a8Schristos 	return;
4396e50d7a8Schristos  ecreate:
440899c734bSmsaitoh 	exerrno = 1;
4416e50d7a8Schristos 	error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
4426e50d7a8Schristos  eopen:
443899c734bSmsaitoh 	exerrno = 1;
4446e50d7a8Schristos 	error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
44561f28255Scgd }
44661f28255Scgd 
44761f28255Scgd 
44861f28255Scgd /*
44961f28255Scgd  * Handle here documents.  Normally we fork off a process to write the
45061f28255Scgd  * data to a pipe.  If the document is short, we can stuff the data in
45161f28255Scgd  * the pipe without forking.
45261f28255Scgd  */
45361f28255Scgd 
45461f28255Scgd STATIC int
openhere(const union node * redir)455366c0bc2Syamt openhere(const union node *redir)
45661f28255Scgd {
45761f28255Scgd 	int pip[2];
45807bae7edSchristos 	int len = 0;
45961f28255Scgd 
46061f28255Scgd 	if (pipe(pip) < 0)
46161f28255Scgd 		error("Pipe call failed");
462*bec2ec26Skre 	len = strlen(redir->nhere.text);
463*bec2ec26Skre 	VTRACE(DBG_REDIR, ("openhere(%p) [%d] \"%.*s\"%s\n", redir, len,
464*bec2ec26Skre 	    (len < 40 ? len : 40), redir->nhere.text, (len < 40 ? "" : "...")));
465*bec2ec26Skre 	if (len <= PIPESIZE) {		/* XXX eventually copy FreeBSD method */
466*bec2ec26Skre 		xwrite(pip[1], redir->nhere.text, len);
46761f28255Scgd 		goto out;
46861f28255Scgd 	}
4699caa685bSkre 	VTRACE(DBG_REDIR, (" forking [%d,%d]\n", pip[0], pip[1]));
4704a1b3429Splunky 	if (forkshell(NULL, NULL, FORK_NOJOB) == 0) {
47161f28255Scgd 		close(pip[0]);
47261f28255Scgd 		signal(SIGINT, SIG_IGN);
47361f28255Scgd 		signal(SIGQUIT, SIG_IGN);
47461f28255Scgd 		signal(SIGHUP, SIG_IGN);
47561f28255Scgd #ifdef SIGTSTP
47661f28255Scgd 		signal(SIGTSTP, SIG_IGN);
47761f28255Scgd #endif
47861f28255Scgd 		signal(SIGPIPE, SIG_DFL);
479*bec2ec26Skre 		xwrite(pip[1], redir->nhere.text, len);
480eef776aaSkre 		VTRACE(DBG_PROCS|DBG_REDIR, ("wrote here doc.  exiting(0)\n"));
48161f28255Scgd 		_exit(0);
48261f28255Scgd 	}
4839caa685bSkre 	VTRACE(DBG_REDIR, ("openhere (closing %d)", pip[1]));
48461f28255Scgd  out:
48561f28255Scgd 	close(pip[1]);
4869caa685bSkre 	VTRACE(DBG_REDIR, (" (pipe fd=%d)", pip[0]));
48761f28255Scgd 	return pip[0];
48861f28255Scgd }
48961f28255Scgd 
49061f28255Scgd 
49161f28255Scgd 
49261f28255Scgd /*
49361f28255Scgd  * Undo the effects of the last redirection.
49461f28255Scgd  */
49561f28255Scgd 
49661f28255Scgd void
popredir(void)497c02b3bbdSchristos popredir(void)
498c02b3bbdSchristos {
49948250187Stls 	struct redirtab *rp = redirlist;
50061f28255Scgd 
50161f28255Scgd 	INTOFF;
5022f19bf49Schristos 	free_rl(rp, 1);
50361f28255Scgd 	redirlist = rp->next;
50461f28255Scgd 	ckfree(rp);
50561f28255Scgd 	INTON;
50661f28255Scgd }
50761f28255Scgd 
50861f28255Scgd /*
50961f28255Scgd  * Undo all redirections.  Called on error or interrupt.
51061f28255Scgd  */
51161f28255Scgd 
51261f28255Scgd #ifdef mkinit
51361f28255Scgd 
51461f28255Scgd INCLUDE "redir.h"
51561f28255Scgd 
51661f28255Scgd RESET {
51761f28255Scgd 	while (redirlist)
51861f28255Scgd 		popredir();
51961f28255Scgd }
52061f28255Scgd 
52161f28255Scgd SHELLPROC {
522edcb4544Schristos 	clearredir(0);
52361f28255Scgd }
52461f28255Scgd 
52561f28255Scgd #endif
52661f28255Scgd 
52737ed7877Sjtc /* Return true if fd 0 has already been redirected at least once.  */
52837ed7877Sjtc int
fd0_redirected_p(void)5292f19bf49Schristos fd0_redirected_p(void)
5302f19bf49Schristos {
53137ed7877Sjtc 	return fd0_redirected != 0;
53237ed7877Sjtc }
53361f28255Scgd 
53461f28255Scgd /*
53561f28255Scgd  * Discard all saved file descriptors.
53661f28255Scgd  */
53761f28255Scgd 
53861f28255Scgd void
clearredir(int vforked)53927323380Smatt clearredir(int vforked)
540edcb4544Schristos {
54148250187Stls 	struct redirtab *rp;
5422f19bf49Schristos 	struct renamelist *rl;
54361f28255Scgd 
54461f28255Scgd 	for (rp = redirlist ; rp ; rp = rp->next) {
545edcb4544Schristos 		if (!vforked)
5462f19bf49Schristos 			free_rl(rp, 0);
5472f19bf49Schristos 		else for (rl = rp->renamed; rl; rl = rl->next)
5482f19bf49Schristos 			if (rl->into >= 0)
5492f19bf49Schristos 				close(rl->into);
55061f28255Scgd 	}
55161f28255Scgd }
55261f28255Scgd 
55361f28255Scgd 
55461f28255Scgd 
55561f28255Scgd /*
5566183d0e0Skre  * Copy a file descriptor to be == to.
5576183d0e0Skre  * cloexec indicates if we want close-on-exec or not.
5586183d0e0Skre  * Returns -1 if any error occurs.
55961f28255Scgd  */
56061f28255Scgd 
5616183d0e0Skre STATIC int
copyfd(int from,int to,int cloexec)5626183d0e0Skre copyfd(int from, int to, int cloexec)
5634ce0d34aScgd {
56461f28255Scgd 	int newfd;
56561f28255Scgd 
566dbb7acccSkre 	if (cloexec && to > 2) {
567dbb7acccSkre #ifdef O_CLOEXEC
5684db07438Schristos 		newfd = dup3(from, to, O_CLOEXEC);
569dbb7acccSkre #else
570dbb7acccSkre 		newfd = dup2(from, to);
571dbb7acccSkre 		fcntl(newfd, F_SETFD, fcntl(newfd,F_GETFD) | FD_CLOEXEC);
572dbb7acccSkre #endif
573dbb7acccSkre 	} else
574b77fb462Spooka 		newfd = dup2(from, to);
5756183d0e0Skre 
57661f28255Scgd 	return newfd;
5775916a085Ssef }
5782f19bf49Schristos 
5796183d0e0Skre /*
5806183d0e0Skre  * rename fd from to be fd to (closing from).
5816183d0e0Skre  * close-on-exec is never set on 'to' (unless
5826183d0e0Skre  * from==to and it was set on from) - ie: a no-op
5836183d0e0Skre  * returns to (or errors() if an error occurs).
5846183d0e0Skre  *
5856183d0e0Skre  * This is mostly used for rearranging the
5866183d0e0Skre  * results from pipe().
5876183d0e0Skre  */
5882f19bf49Schristos int
movefd(int from,int to)5892f19bf49Schristos movefd(int from, int to)
5902f19bf49Schristos {
5912f19bf49Schristos 	if (from == to)
5922f19bf49Schristos 		return to;
5932f19bf49Schristos 
5942f19bf49Schristos 	(void) close(to);
5956183d0e0Skre 	if (copyfd(from, to, 0) != to) {
5966183d0e0Skre 		int e = errno;
5976183d0e0Skre 
5986183d0e0Skre 		(void) close(from);
5996183d0e0Skre 		error("Unable to make fd %d: %s", to, strerror(e));
6006183d0e0Skre 	}
6012f19bf49Schristos 	(void) close(from);
6022f19bf49Schristos 
6032f19bf49Schristos 	return to;
6042f19bf49Schristos }
6052f19bf49Schristos 
6062f19bf49Schristos STATIC void
find_big_fd(void)6072f19bf49Schristos find_big_fd(void)
6082f19bf49Schristos {
6092f19bf49Schristos 	int i, fd;
610d87ccf13Skre 	static int last_start = 3; /* aim to keep sh fd's under 20 */
6112f19bf49Schristos 
6126bc33f84Skre 	if (last_start < 10)
6136bc33f84Skre 		last_start++;
6146bc33f84Skre 
6156bc33f84Skre 	for (i = (1 << last_start); i >= 10; i >>= 1) {
6162f19bf49Schristos 		if ((fd = fcntl(0, F_DUPFD, i - 1)) >= 0) {
6172f19bf49Schristos 			close(fd);
6182f19bf49Schristos 			break;
6192f19bf49Schristos 		}
6202f19bf49Schristos 	}
6212f19bf49Schristos 
6222f19bf49Schristos 	fd = (i / 5) * 4;
6236bc33f84Skre 	if (fd < 10)
6242f19bf49Schristos 		fd = 10;
6252f19bf49Schristos 
6262f19bf49Schristos 	big_sh_fd = fd;
6272f19bf49Schristos }
6282f19bf49Schristos 
6296183d0e0Skre /*
6306183d0e0Skre  * If possible, move file descriptor fd out of the way
6316183d0e0Skre  * of expected user fd values.   Returns the new fd
6326183d0e0Skre  * (which may be the input fd if things do not go well.)
6336183d0e0Skre  * Always set close-on-exec on the result, and close
6346183d0e0Skre  * the input fd unless it is to be our result.
6356183d0e0Skre  */
6362f19bf49Schristos int
to_upper_fd(int fd)6372f19bf49Schristos to_upper_fd(int fd)
6382f19bf49Schristos {
6392f19bf49Schristos 	int i;
6402f19bf49Schristos 
641df7ac856Skre 	VTRACE(DBG_REDIR|DBG_OUTPUT, ("to_upper_fd(%d)", fd));
642852ae738Skre 	if (big_sh_fd < 10 || big_sh_fd >= user_fd_limit)
6432f19bf49Schristos 		find_big_fd();
6442f19bf49Schristos 	do {
6453a874859Skre 		i = fcntl(fd, F_DUPFD_CLOEXEC, big_sh_fd);
6462f19bf49Schristos 		if (i >= 0) {
6472f19bf49Schristos 			if (fd != i)
6482f19bf49Schristos 				close(fd);
649df7ac856Skre 			VTRACE(DBG_REDIR|DBG_OUTPUT, ("-> %d\n", i));
6502f19bf49Schristos 			return i;
6512f19bf49Schristos 		}
6526bc33f84Skre 		if (errno != EMFILE && errno != EINVAL)
6532f19bf49Schristos 			break;
6542f19bf49Schristos 		find_big_fd();
6552f19bf49Schristos 	} while (big_sh_fd > 10);
6562f19bf49Schristos 
6573a874859Skre 	/*
6586183d0e0Skre 	 * If we wanted to move this fd to some random high number
6593a874859Skre 	 * we certainly do not intend to pass it through exec, even
6603a874859Skre 	 * if the reassignment failed.
6613a874859Skre 	 */
6623a874859Skre 	(void)fcntl(fd, F_SETFD, FD_CLOEXEC);
663df7ac856Skre 	VTRACE(DBG_REDIR|DBG_OUTPUT, (" fails ->%d\n", fd));
6642f19bf49Schristos 	return fd;
6652f19bf49Schristos }
666949a12ecSchristos 
6676bc33f84Skre void
register_sh_fd(int fd,void (* cb)(int,int))6686bc33f84Skre register_sh_fd(int fd, void (*cb)(int, int))
6696bc33f84Skre {
6706bc33f84Skre 	struct shell_fds *fp;
6716bc33f84Skre 
6726bc33f84Skre 	fp = ckmalloc(sizeof (struct shell_fds));
6736bc33f84Skre 	if (fp != NULL) {
6746bc33f84Skre 		fp->nxt = sh_fd_list;
6756bc33f84Skre 		sh_fd_list = fp;
6766bc33f84Skre 
6776bc33f84Skre 		fp->fd = fd;
6786bc33f84Skre 		fp->cb = cb;
6796bc33f84Skre 	}
6806bc33f84Skre }
6816bc33f84Skre 
6826bc33f84Skre void
sh_close(int fd)6836bc33f84Skre sh_close(int fd)
6846bc33f84Skre {
6856bc33f84Skre 	struct shell_fds **fpp, *fp;
6866bc33f84Skre 
6876bc33f84Skre 	fpp = &sh_fd_list;
6886bc33f84Skre 	while ((fp = *fpp) != NULL) {
6896bc33f84Skre 		if (fp->fd == fd) {
6906bc33f84Skre 			*fpp = fp->nxt;
6916bc33f84Skre 			ckfree(fp);
6926bc33f84Skre 			break;
6936bc33f84Skre 		}
6946bc33f84Skre 		fpp = &fp->nxt;
6956bc33f84Skre 	}
6966bc33f84Skre 	(void)close(fd);
6976bc33f84Skre }
6986bc33f84Skre 
6996bc33f84Skre STATIC struct shell_fds *
sh_fd(int fd)7006bc33f84Skre sh_fd(int fd)
7016bc33f84Skre {
7026bc33f84Skre 	struct shell_fds *fp;
7036bc33f84Skre 
7046bc33f84Skre 	for (fp = sh_fd_list; fp != NULL; fp = fp->nxt)
7056bc33f84Skre 		if (fp->fd == fd)
7066bc33f84Skre 			return fp;
7076bc33f84Skre 	return NULL;
7086bc33f84Skre }
7096bc33f84Skre 
710852ae738Skre STATIC int
pick_new_fd(int fd)711852ae738Skre pick_new_fd(int fd)
712852ae738Skre {
713852ae738Skre 	int to;
714852ae738Skre 
715852ae738Skre 	to = fcntl(fd, F_DUPFD_CLOEXEC, big_sh_fd);
716852ae738Skre 	if (to == -1 && big_sh_fd >= 22)
717852ae738Skre 		to = fcntl(fd, F_DUPFD_CLOEXEC, big_sh_fd/2);
718852ae738Skre 	if (to == -1)
719852ae738Skre 		to = fcntl(fd, F_DUPFD_CLOEXEC, fd + 1);
720852ae738Skre 	if (to == -1)
721852ae738Skre 		to = fcntl(fd, F_DUPFD_CLOEXEC, 10);
722852ae738Skre 	if (to == -1)
723852ae738Skre 		to = fcntl(fd, F_DUPFD_CLOEXEC, 3);
724852ae738Skre 	if (to == -1)
725852ae738Skre 		error("insufficient file descriptors available");
726852ae738Skre 	CLOEXEC(to);
727852ae738Skre 	return to;
728852ae738Skre }
729852ae738Skre 
7306bc33f84Skre STATIC void
renumber_sh_fd(struct shell_fds * fp)7316bc33f84Skre renumber_sh_fd(struct shell_fds *fp)
7326bc33f84Skre {
7336bc33f84Skre 	int to;
7346bc33f84Skre 
7356bc33f84Skre 	if (fp == NULL)
7366bc33f84Skre 		return;
7376bc33f84Skre 
738d87ccf13Skre 	/*
739d87ccf13Skre 	 * if we have had a collision, and the sh fd was a "big" one
740d87ccf13Skre 	 * try moving the sh fd base to a higher number (if possible)
741d87ccf13Skre 	 * so future sh fds are less likely to be in the user's sights
742d87ccf13Skre 	 * (incl this one when moved)
743d87ccf13Skre 	 */
744d87ccf13Skre 	if (fp->fd >= big_sh_fd)
745d87ccf13Skre 		find_big_fd();
746d87ccf13Skre 
747852ae738Skre 	to = pick_new_fd(fp->fd);
7486bc33f84Skre 
7496bc33f84Skre 	if (fp->fd == to)	/* impossible? */
7506bc33f84Skre 		return;
7516bc33f84Skre 
752852ae738Skre 	VTRACE(DBG_REDIR, ("renumber_sh_fd: moved shell fd %d to %d\n",
753852ae738Skre 	    fp->fd, to));
754852ae738Skre 
7556bc33f84Skre 	(*fp->cb)(fp->fd, to);
7566bc33f84Skre 	(void)close(fp->fd);
7576bc33f84Skre 	fp->fd = to;
7586bc33f84Skre }
7596bc33f84Skre 
76070946d25Skre static const struct flgnames {
761949a12ecSchristos 	const char *name;
7623aa6ba17Skre 	uint16_t minch;
763949a12ecSchristos 	uint32_t value;
764949a12ecSchristos } nv[] = {
765949a12ecSchristos #ifdef O_APPEND
7663aa6ba17Skre 	{ "append",	2,	O_APPEND 	},
767dbb7acccSkre #else
768dbb7acccSkre # define O_APPEND 0
769949a12ecSchristos #endif
770949a12ecSchristos #ifdef O_ASYNC
7713aa6ba17Skre 	{ "async",	2,	O_ASYNC		},
772dbb7acccSkre #else
773dbb7acccSkre # define O_ASYNC 0
774949a12ecSchristos #endif
775949a12ecSchristos #ifdef O_SYNC
7763aa6ba17Skre 	{ "sync",	2,	O_SYNC		},
777dbb7acccSkre #else
778dbb7acccSkre # define O_SYNC 0
779949a12ecSchristos #endif
780949a12ecSchristos #ifdef O_NONBLOCK
7813aa6ba17Skre 	{ "nonblock",	3,	O_NONBLOCK	},
782dbb7acccSkre #else
783dbb7acccSkre # define O_NONBLOCK 0
784949a12ecSchristos #endif
785949a12ecSchristos #ifdef O_FSYNC
7863aa6ba17Skre 	{ "fsync",	2,	O_FSYNC		},
787dbb7acccSkre #else
788dbb7acccSkre # define O_FSYNC 0
789949a12ecSchristos #endif
790949a12ecSchristos #ifdef O_DSYNC
7913aa6ba17Skre 	{ "dsync",	2,	O_DSYNC		},
792dbb7acccSkre #else
793dbb7acccSkre # define O_DSYNC 0
794949a12ecSchristos #endif
795949a12ecSchristos #ifdef O_RSYNC
7963aa6ba17Skre 	{ "rsync",	2,	O_RSYNC		},
797dbb7acccSkre #else
798dbb7acccSkre # define O_RSYNC 0
799949a12ecSchristos #endif
80070cafc1bSkamil #ifdef O_ALT_IO
8013aa6ba17Skre 	{ "altio",	2,	O_ALT_IO	},
802dbb7acccSkre #else
803dbb7acccSkre # define O_ALT_IO 0
804949a12ecSchristos #endif
805949a12ecSchristos #ifdef O_DIRECT
8063aa6ba17Skre 	{ "direct",	2,	O_DIRECT	},
807dbb7acccSkre #else
808dbb7acccSkre # define O_DIRECT 0
809949a12ecSchristos #endif
810949a12ecSchristos #ifdef O_NOSIGPIPE
8113aa6ba17Skre 	{ "nosigpipe",	3,	O_NOSIGPIPE	},
812dbb7acccSkre #else
813dbb7acccSkre # define O_NOSIGPIPE 0
814949a12ecSchristos #endif
815dbb7acccSkre 
816dbb7acccSkre #define ALLFLAGS (O_APPEND|O_ASYNC|O_SYNC|O_NONBLOCK|O_DSYNC|O_RSYNC|\
817dbb7acccSkre     O_ALT_IO|O_DIRECT|O_NOSIGPIPE)
818dbb7acccSkre 
819dbb7acccSkre #ifndef	O_CLOEXEC
8201d9290aeSkre # define O_CLOEXEC	((~ALLFLAGS) ^ ((~ALLFLAGS) & ((~ALLFLAGS) - 1)))
821dbb7acccSkre #endif
822dbb7acccSkre 
823dbb7acccSkre 	/* for any system we support, close on exec is always defined */
8243aa6ba17Skre 	{ "cloexec",	2,	O_CLOEXEC	},
8253aa6ba17Skre 	{ 0, 0, 0 }
826949a12ecSchristos };
827dbb7acccSkre 
828dbb7acccSkre #ifndef O_ACCMODE
829dbb7acccSkre # define O_ACCMODE	0
830dbb7acccSkre #endif
831dbb7acccSkre #ifndef O_RDONLY
832dbb7acccSkre # define O_RDONLY	0
833dbb7acccSkre #endif
834dbb7acccSkre #ifndef O_WRONLY
835dbb7acccSkre # define O_WRONLY	0
836dbb7acccSkre #endif
837dbb7acccSkre #ifndef O_RWDR
838dbb7acccSkre # define O_RWDR		0
839dbb7acccSkre #endif
840dbb7acccSkre #ifndef O_SHLOCK
841dbb7acccSkre # define O_SHLOCK	0
842dbb7acccSkre #endif
843dbb7acccSkre #ifndef O_EXLOCK
844dbb7acccSkre # define O_EXLOCK	0
845dbb7acccSkre #endif
846dbb7acccSkre #ifndef O_NOFOLLOW
847dbb7acccSkre # define O_NOFOLLOW	0
848dbb7acccSkre #endif
849dbb7acccSkre #ifndef O_CREAT
850dbb7acccSkre # define O_CREAT	0
851dbb7acccSkre #endif
852dbb7acccSkre #ifndef O_TRUNC
853dbb7acccSkre # define O_TRUNC	0
854dbb7acccSkre #endif
855dbb7acccSkre #ifndef O_EXCL
856dbb7acccSkre # define O_EXCL		0
857dbb7acccSkre #endif
858dbb7acccSkre #ifndef O_NOCTTY
859dbb7acccSkre # define O_NOCTTY	0
860dbb7acccSkre #endif
861dbb7acccSkre #ifndef O_DIRECTORY
862dbb7acccSkre # define O_DIRECTORY	0
863dbb7acccSkre #endif
864dbb7acccSkre #ifndef O_REGULAR
865dbb7acccSkre # define O_REGULAR	0
866dbb7acccSkre #endif
867dbb7acccSkre /*
868dbb7acccSkre  * flags that F_GETFL might return that we want to ignore
869dbb7acccSkre  *
870dbb7acccSkre  * F_GETFL should not actually return these, they're all just open()
871dbb7acccSkre  * modifiers, rather than state, but just in case...
872dbb7acccSkre  */
873dbb7acccSkre #define IGNFLAGS (O_ACCMODE|O_RDONLY|O_WRONLY|O_RDWR|O_SHLOCK|O_EXLOCK| \
874dbb7acccSkre     O_NOFOLLOW|O_CREAT|O_TRUNC|O_EXCL|O_NOCTTY|O_DIRECTORY|O_REGULAR)
875949a12ecSchristos 
876949a12ecSchristos static int
getflags(int fd,int p)877949a12ecSchristos getflags(int fd, int p)
878949a12ecSchristos {
879949a12ecSchristos 	int c, f;
880949a12ecSchristos 
881852ae738Skre 	if (sh_fd(fd) != NULL || saved_redirected_fd(fd) != NULL) {
8826bc33f84Skre 		if (!p)
8836bc33f84Skre 			return -1;
884285af6fdSkre 		error("Can't get status for fd=%d (%s)", fd, strerror(EBADF));
8856bc33f84Skre 	}
8866bc33f84Skre 
887949a12ecSchristos 	if ((c = fcntl(fd, F_GETFD)) == -1) {
888949a12ecSchristos 		if (!p)
889949a12ecSchristos 			return -1;
890949a12ecSchristos 		error("Can't get status for fd=%d (%s)", fd, strerror(errno));
891949a12ecSchristos 	}
892949a12ecSchristos 	if ((f = fcntl(fd, F_GETFL)) == -1) {
893949a12ecSchristos 		if (!p)
894949a12ecSchristos 			return -1;
895949a12ecSchristos 		error("Can't get flags for fd=%d (%s)", fd, strerror(errno));
896949a12ecSchristos 	}
897dbb7acccSkre 	f &= ~IGNFLAGS;		/* clear anything we know about, but ignore */
89870946d25Skre 	if (c & FD_CLOEXEC)
899949a12ecSchristos 		f |= O_CLOEXEC;
900dbb7acccSkre 	return f;
901949a12ecSchristos }
902949a12ecSchristos 
903949a12ecSchristos static void
printone(int fd,int p,int verbose,int pfd)90470946d25Skre printone(int fd, int p, int verbose, int pfd)
905949a12ecSchristos {
906949a12ecSchristos 	int f = getflags(fd, p);
90770946d25Skre 	const struct flgnames *fn;
908949a12ecSchristos 
909949a12ecSchristos 	if (f == -1)
910949a12ecSchristos 		return;
911949a12ecSchristos 
91270946d25Skre 	if (pfd)
913949a12ecSchristos 		outfmt(out1, "%d: ", fd);
91470946d25Skre 	for (fn = nv; fn->name; fn++) {
91570946d25Skre 		if (f & fn->value) {
91670946d25Skre 			outfmt(out1, "%s%s", verbose ? "+" : "", fn->name);
91770946d25Skre 			f &= ~fn->value;
918949a12ecSchristos 		} else if (verbose)
91970946d25Skre 			outfmt(out1, "-%s", fn->name);
920949a12ecSchristos 		else
921949a12ecSchristos 			continue;
92270946d25Skre 		if (f || (verbose && fn[1].name))
923949a12ecSchristos 			outfmt(out1, ",");
924949a12ecSchristos 	}
92570946d25Skre 	if (verbose && f)		/* f should be normally be 0 */
92670946d25Skre 		outfmt(out1, " +%#x", f);
927949a12ecSchristos 	outfmt(out1, "\n");
928949a12ecSchristos }
929949a12ecSchristos 
93070946d25Skre static void
parseflags(char * s,int * p,int * n)931949a12ecSchristos parseflags(char *s, int *p, int *n)
932949a12ecSchristos {
93370946d25Skre 	int *v, *w;
93470946d25Skre 	const struct flgnames *fn;
9353aa6ba17Skre 	size_t len;
936949a12ecSchristos 
937949a12ecSchristos 	*p = 0;
938949a12ecSchristos 	*n = 0;
939949a12ecSchristos 	for (s = strtok(s, ","); s; s = strtok(NULL, ",")) {
94070946d25Skre 		switch (*s++) {
941949a12ecSchristos 		case '+':
942949a12ecSchristos 			v = p;
94370946d25Skre 			w = n;
944949a12ecSchristos 			break;
945949a12ecSchristos 		case '-':
946949a12ecSchristos 			v = n;
94770946d25Skre 			w = p;
948949a12ecSchristos 			break;
949949a12ecSchristos 		default:
95070946d25Skre 			error("Missing +/- indicator before flag %s", s-1);
951949a12ecSchristos 		}
952949a12ecSchristos 
9533aa6ba17Skre 		len = strlen(s);
95470946d25Skre 		for (fn = nv; fn->name; fn++)
9553aa6ba17Skre 			if (len >= fn->minch && strncmp(s,fn->name,len) == 0) {
95670946d25Skre 				*v |= fn->value;
95770946d25Skre 				*w &=~ fn->value;
958949a12ecSchristos 				break;
959949a12ecSchristos 			}
96070946d25Skre 		if (fn->name == 0)
961949a12ecSchristos 			error("Bad flag `%s'", s);
962949a12ecSchristos 	}
963949a12ecSchristos }
964949a12ecSchristos 
965949a12ecSchristos static void
setone(int fd,int pos,int neg,int verbose)966949a12ecSchristos setone(int fd, int pos, int neg, int verbose)
967949a12ecSchristos {
968949a12ecSchristos 	int f = getflags(fd, 1);
96970946d25Skre 	int n, cloexec;
97070946d25Skre 
971949a12ecSchristos 	if (f == -1)
972949a12ecSchristos 		return;
973949a12ecSchristos 
97470946d25Skre 	cloexec = -1;
975949a12ecSchristos 	if ((pos & O_CLOEXEC) && !(f & O_CLOEXEC))
976949a12ecSchristos 		cloexec = FD_CLOEXEC;
977949a12ecSchristos 	if ((neg & O_CLOEXEC) && (f & O_CLOEXEC))
978949a12ecSchristos 		cloexec = 0;
979949a12ecSchristos 
980949a12ecSchristos 	if (cloexec != -1 && fcntl(fd, F_SETFD, cloexec) == -1)
981949a12ecSchristos 		error("Can't set status for fd=%d (%s)", fd, strerror(errno));
982949a12ecSchristos 
983949a12ecSchristos 	pos &= ~O_CLOEXEC;
984949a12ecSchristos 	neg &= ~O_CLOEXEC;
985949a12ecSchristos 	f &= ~O_CLOEXEC;
98670946d25Skre 	n = f;
987949a12ecSchristos 	n |= pos;
988949a12ecSchristos 	n &= ~neg;
989949a12ecSchristos 	if (n != f && fcntl(fd, F_SETFL, n) == -1)
990949a12ecSchristos 		error("Can't set flags for fd=%d (%s)", fd, strerror(errno));
991949a12ecSchristos 	if (verbose)
99270946d25Skre 		printone(fd, 1, verbose, 1);
993949a12ecSchristos }
994949a12ecSchristos 
995949a12ecSchristos int
fdflagscmd(int argc,char * argv[])996949a12ecSchristos fdflagscmd(int argc, char *argv[])
997949a12ecSchristos {
998949a12ecSchristos 	char *num;
999949a12ecSchristos 	int verbose = 0, ch, pos = 0, neg = 0;
1000949a12ecSchristos 	char *setflags = NULL;
1001949a12ecSchristos 
1002949a12ecSchristos 	optreset = 1; optind = 1; /* initialize getopt */
1003949a12ecSchristos 	while ((ch = getopt(argc, argv, ":vs:")) != -1)
1004949a12ecSchristos 		switch ((char)ch) {
1005949a12ecSchristos 		case 'v':
1006949a12ecSchristos 			verbose = 1;
1007949a12ecSchristos 			break;
1008949a12ecSchristos 		case 's':
100970946d25Skre 			if (setflags)
101070946d25Skre 				goto msg;
1011949a12ecSchristos 			setflags = optarg;
1012949a12ecSchristos 			break;
1013949a12ecSchristos 		case '?':
1014949a12ecSchristos 		default:
1015949a12ecSchristos 		msg:
101670946d25Skre 			error("Usage: fdflags [-v] [-s <flags> fd] [fd...]");
1017949a12ecSchristos 			/* NOTREACHED */
1018949a12ecSchristos 		}
1019949a12ecSchristos 
1020949a12ecSchristos 	argc -= optind, argv += optind;
1021949a12ecSchristos 
102270946d25Skre 	if (setflags)
102370946d25Skre 		parseflags(setflags, &pos, &neg);
1024949a12ecSchristos 
1025949a12ecSchristos 	if (argc == 0) {
1026eb5ab741Skre 		int i;
102770946d25Skre 
1028949a12ecSchristos 		if (setflags)
1029949a12ecSchristos 			goto msg;
103070946d25Skre 
1031eb5ab741Skre 		for (i = 0; i <= max_user_fd; i++)
103270946d25Skre 			printone(i, 0, verbose, 1);
1033949a12ecSchristos 
103403d7507fSkre 	} else while ((num = *argv++) != NULL) {
103570946d25Skre 		int fd = number(num);
103670946d25Skre 
1037f5e17f92Skre 		while (num[0] == '0' && num[1] != '\0')		/* skip 0's */
1038f5e17f92Skre 			num++;
1039852ae738Skre 		if (strlen(num) > 5 ||
1040852ae738Skre 		    (fd >= user_fd_limit && fd > max_user_fd))
1041852ae738Skre 			error("%s: too big to be a file descriptor", num);
104270946d25Skre 
1043949a12ecSchristos 		if (setflags)
1044949a12ecSchristos 			setone(fd, pos, neg, verbose);
1045949a12ecSchristos 		else
104670946d25Skre 			printone(fd, 1, verbose, argc > 1);
1047949a12ecSchristos 	}
104803d7507fSkre 	flushout(out1);
104903d7507fSkre 	if (io_err(out1)) {
105003d7507fSkre 		out2str("fdflags: I/O error\n");
105103d7507fSkre 		return 1;
105203d7507fSkre 	}
1053949a12ecSchristos 	return 0;
1054949a12ecSchristos }
10556ecc45deSkre 
10566ecc45deSkre #undef MAX		/* in case we inherited them from somewhere */
10576ecc45deSkre #undef MIN
10586ecc45deSkre 
10596ecc45deSkre #define	MIN(a,b)	(/*CONSTCOND*/((a)<=(b)) ? (a) : (b))
10606ecc45deSkre #define	MAX(a,b)	(/*CONSTCOND*/((a)>=(b)) ? (a) : (b))
10616ecc45deSkre 
10626ecc45deSkre 		/* now make the compiler work for us... */
10636ecc45deSkre #define	MIN_REDIR	MIN(MIN(MIN(MIN(NTO,NFROM), MIN(NTOFD,NFROMFD)), \
10646ecc45deSkre 		   MIN(MIN(NCLOBBER,NAPPEND), MIN(NHERE,NXHERE))), NFROMTO)
10656ecc45deSkre #define	MAX_REDIR	MAX(MAX(MAX(MAX(NTO,NFROM), MAX(NTOFD,NFROMFD)), \
10666ecc45deSkre 		   MAX(MAX(NCLOBBER,NAPPEND), MAX(NHERE,NXHERE))), NFROMTO)
10676ecc45deSkre 
10686ecc45deSkre static const char *redir_sym[MAX_REDIR - MIN_REDIR + 1] = {
10696ecc45deSkre 	[NTO      - MIN_REDIR]=	">",
10706ecc45deSkre 	[NFROM    - MIN_REDIR]=	"<",
10716ecc45deSkre 	[NTOFD    - MIN_REDIR]=	">&",
10726ecc45deSkre 	[NFROMFD  - MIN_REDIR]=	"<&",
10736ecc45deSkre 	[NCLOBBER - MIN_REDIR]=	">|",
10746ecc45deSkre 	[NAPPEND  - MIN_REDIR]=	">>",
10756ecc45deSkre 	[NHERE    - MIN_REDIR]=	"<<",
10766ecc45deSkre 	[NXHERE   - MIN_REDIR]=	"<<",
10776ecc45deSkre 	[NFROMTO  - MIN_REDIR]=	"<>",
10786ecc45deSkre };
10796ecc45deSkre 
10806ecc45deSkre int
outredir(struct output * out,union node * n,int sep)10816ecc45deSkre outredir(struct output *out, union node *n, int sep)
10826ecc45deSkre {
10836ecc45deSkre 	if (n == NULL)
10846ecc45deSkre 		return 0;
10856ecc45deSkre 	if (n->type < MIN_REDIR || n->type > MAX_REDIR ||
10866ecc45deSkre 	    redir_sym[n->type - MIN_REDIR] == NULL)
10876ecc45deSkre 		return 0;
10886ecc45deSkre 
10896ecc45deSkre 	if (sep)
10906ecc45deSkre 		outc(sep, out);
10916ecc45deSkre 
10926ecc45deSkre 	/*
10936ecc45deSkre 	 * ugly, but all redir node types have "fd" in same slot...
10946ecc45deSkre 	 *	(and code other places assumes it as well)
10956ecc45deSkre 	 */
10966ecc45deSkre 	if ((redir_sym[n->type - MIN_REDIR][0] == '<' && n->nfile.fd != 0) ||
10976ecc45deSkre 	    (redir_sym[n->type - MIN_REDIR][0] == '>' && n->nfile.fd != 1))
10986ecc45deSkre 		outfmt(out, "%d", n->nfile.fd);
10996ecc45deSkre 
11006ecc45deSkre 	outstr(redir_sym[n->type - MIN_REDIR], out);
11016ecc45deSkre 
11026ecc45deSkre 	switch (n->type) {
11036ecc45deSkre 	case NHERE:
11046ecc45deSkre 		outstr("'...'", out);
11056ecc45deSkre 		break;
11066ecc45deSkre 	case NXHERE:
11076ecc45deSkre 		outstr("...", out);
11086ecc45deSkre 		break;
11096ecc45deSkre 	case NTOFD:
11106ecc45deSkre 	case NFROMFD:
11116ecc45deSkre 		if (n->ndup.dupfd < 0)
11126ecc45deSkre 			outc('-', out);
11136ecc45deSkre 		else
11146ecc45deSkre 			outfmt(out, "%d", n->ndup.dupfd);
11156ecc45deSkre 		break;
11166ecc45deSkre 	default:
11176ecc45deSkre 		outstr(n->nfile.expfname, out);
11186ecc45deSkre 		break;
11196ecc45deSkre 	}
11206ecc45deSkre 	return 1;
11216ecc45deSkre }
1122