xref: /netbsd-src/bin/sh/redir.c (revision 1c3b098334123732efc3e38acf3c8a83909e00b2)
1*1c3b0983Skre /*	$NetBSD: redir.c,v 1.76 2024/11/11 22:57:42 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*1c3b0983Skre __RCSID("$NetBSD: redir.c,v 1.76 2024/11/11 22:57:42 kre Exp $");
4149f0ad86Scgd #endif
4261f28255Scgd #endif /* not lint */
4361f28255Scgd 
4407bae7edSchristos #include <sys/types.h>
45d06f1504Schristos #include <sys/param.h>	/* PIPE_BUF */
467792ef1aSchristos #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"
59168f1d4aSchristos #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"
680ff2aa10Skre #include "mystring.h"
6961f28255Scgd #include "error.h"
70b86d4a74Skre #include "show.h"
7161f28255Scgd 
7261f28255Scgd 
7344f76837Syamt #define CLOSED -1		/* fd was not open before redir */
74*1c3b0983Skre 
75d06f1504Schristos #ifndef PIPE_BUF
7661f28255Scgd # define PIPESIZE 4096		/* amount of buffering in a pipe */
77d06f1504Schristos #else
78d06f1504Schristos # define PIPESIZE PIPE_BUF
79d06f1504Schristos #endif
8061f28255Scgd 
81f2dc7540Skre #ifndef FD_CLOEXEC
82f2dc7540Skre # define FD_CLOEXEC	1	/* well known from before there was a name */
83f2dc7540Skre #endif
84f2dc7540Skre 
85f2dc7540Skre #ifndef F_DUPFD_CLOEXEC
86f2dc7540Skre #define F_DUPFD_CLOEXEC	F_DUPFD
87*1c3b0983Skre #define CLOEXEC(fd)	(fcntl((fd), F_SETFD,				\
88*1c3b0983Skre 			     (fcntl_int)(fcntl((fd), F_GETFD) | FD_CLOEXEC)))
89f2dc7540Skre #else
90*1c3b0983Skre #define CLOEXEC(fd)	__nothing
91f2dc7540Skre #endif
92f2dc7540Skre 
93*1c3b0983Skre /* yes, this is correct, bizarre parens and all -- used only as a cast */
94*1c3b0983Skre #define fcntl_int	void *)(intptr_t
95*1c3b0983Skre #undef  fcntl_int
96*1c3b0983Skre #define fcntl_int	int
97*1c3b0983Skre 
9861f28255Scgd 
9961f28255Scgd MKINIT
1001fad4bb6Schristos struct renamelist {
1011fad4bb6Schristos 	struct renamelist *next;
1021fad4bb6Schristos 	int orig;
1031fad4bb6Schristos 	int into;
104*1c3b0983Skre 	int cloexec;	/* orig had FD_CLOEXEC set (into always does) */
1051fad4bb6Schristos };
1061fad4bb6Schristos 
1071fad4bb6Schristos MKINIT
10861f28255Scgd struct redirtab {
10961f28255Scgd 	struct redirtab *next;
1101fad4bb6Schristos 	struct renamelist *renamed;
11161f28255Scgd };
11261f28255Scgd 
11361f28255Scgd 
11461f28255Scgd MKINIT struct redirtab *redirlist;
11561f28255Scgd 
11637ed7877Sjtc /*
11737ed7877Sjtc  * We keep track of whether or not fd0 has been redirected.  This is for
11837ed7877Sjtc  * background commands, where we want to redirect fd0 to /dev/null only
11937ed7877Sjtc  * if it hasn't already been redirected.
12037ed7877Sjtc  */
1211fad4bb6Schristos STATIC int fd0_redirected = 0;
12261f28255Scgd 
1231fad4bb6Schristos /*
1241fad4bb6Schristos  * And also where to put internal use fds that should be out of the
1251fad4bb6Schristos  * way of user defined fds (normally)
1261fad4bb6Schristos  */
1271fad4bb6Schristos STATIC int big_sh_fd = 0;
128*1c3b0983Skre STATIC int biggest_sh_fd = 2;
1291fad4bb6Schristos 
1301fad4bb6Schristos STATIC const struct renamelist *is_renamed(const struct renamelist *, int);
131*1c3b0983Skre STATIC void fd_rename(struct redirtab *, int, int, int);
13252967993Skre STATIC int * saved_redirected_fd(int);
1331fad4bb6Schristos STATIC void free_rl(struct redirtab *, int);
1345dd90992Schristos STATIC void openredirect(union node *, char[10], int);
135071e965cSyamt STATIC int openhere(const union node *);
136*1c3b0983Skre STATIC int copyfd(int, int, int, int);
1371fad4bb6Schristos STATIC void find_big_fd(void);
13861f28255Scgd 
13951c4dfe4Skre 
14051c4dfe4Skre struct shell_fds {		/* keep track of internal shell fds */
14151c4dfe4Skre 	struct shell_fds *nxt;
14251c4dfe4Skre 	void (*cb)(int, int);
14351c4dfe4Skre 	int fd;
14451c4dfe4Skre };
14551c4dfe4Skre 
14651c4dfe4Skre STATIC struct shell_fds *sh_fd_list;
14751c4dfe4Skre 
14852967993Skre STATIC int pick_new_fd(int);
14951c4dfe4Skre STATIC void renumber_sh_fd(struct shell_fds *);
15051c4dfe4Skre STATIC struct shell_fds *sh_fd(int);
15151c4dfe4Skre 
1521fad4bb6Schristos STATIC const struct renamelist *
1531fad4bb6Schristos is_renamed(const struct renamelist *rl, int fd)
1541fad4bb6Schristos {
1551fad4bb6Schristos 	while (rl != NULL) {
1561fad4bb6Schristos 		if (rl->orig == fd)
1571fad4bb6Schristos 			return rl;
1581fad4bb6Schristos 		rl = rl->next;
1591fad4bb6Schristos 	}
1601fad4bb6Schristos 	return NULL;
1611fad4bb6Schristos }
1621fad4bb6Schristos 
16352967993Skre STATIC int *
16452967993Skre saved_redirected_fd(int fd)
16552967993Skre {
16652967993Skre 	struct redirtab *rt;
16752967993Skre 	struct renamelist *rl;
16852967993Skre 
16952967993Skre 	for (rt = redirlist; rt != NULL; rt = rt->next) {
17052967993Skre 		for (rl =  rt->renamed; rl != NULL; rl = rl->next) {
17152967993Skre 			if (rl->into == fd)
17252967993Skre 				return &rl->into;
17352967993Skre 		}
17452967993Skre 	}
17552967993Skre 	return NULL;
17652967993Skre }
17752967993Skre 
1781fad4bb6Schristos STATIC void
1791fad4bb6Schristos free_rl(struct redirtab *rt, int reset)
1801fad4bb6Schristos {
1811fad4bb6Schristos 	struct renamelist *rl, *rn = rt->renamed;
1821fad4bb6Schristos 
1831fad4bb6Schristos 	while ((rl = rn) != NULL) {
1841fad4bb6Schristos 		rn = rl->next;
185*1c3b0983Skre 
1861fad4bb6Schristos 		if (rl->orig == 0)
1871fad4bb6Schristos 			fd0_redirected--;
188*1c3b0983Skre 
189ab6821e0Skre 		VTRACE(DBG_REDIR, ("popredir %d%s: %s",
190ab6821e0Skre 		    rl->orig, rl->orig==0 ? " (STDIN)" : "",
191*1c3b0983Skre 		    reset == POPREDIR_UNDO ? "" :
192*1c3b0983Skre 		    reset == POPREDIR_PERMANENT ? "make permanent" :
193*1c3b0983Skre 					     "no reset\n"));
194*1c3b0983Skre 
195*1c3b0983Skre 		switch (reset) {
196*1c3b0983Skre 		case POPREDIR_UNDO:
197ab6821e0Skre 			if (rl->into < 0) {
198ab6821e0Skre 				VTRACE(DBG_REDIR, (" closed\n"));
1991fad4bb6Schristos 				close(rl->orig);
200ab6821e0Skre 			} else {
201*1c3b0983Skre 				VTRACE(DBG_REDIR,
202*1c3b0983Skre 				    (" from %d%s\n", rl->into,
203*1c3b0983Skre 				       rl->cloexec ? " (colexec)" : ""));
204*1c3b0983Skre 				copyfd(rl->into, rl->orig, rl->cloexec, 1);
2051fad4bb6Schristos 			}
206*1c3b0983Skre 			break;
207*1c3b0983Skre 
208*1c3b0983Skre 		case POPREDIR_PERMANENT:
209*1c3b0983Skre 			if (rl->into < 0) {
210*1c3b0983Skre 				VTRACE(DBG_REDIR, (" was closed\n"));
211*1c3b0983Skre 				/* nothing to do */
212*1c3b0983Skre 			} else {
213*1c3b0983Skre 				VTRACE(DBG_REDIR,
214*1c3b0983Skre 				    (" close savefd %d\n", rl->into));
215*1c3b0983Skre 				close(rl->into);
216*1c3b0983Skre 			}
217*1c3b0983Skre 			break;
218*1c3b0983Skre 
219*1c3b0983Skre 		default:
220*1c3b0983Skre 			/* nothing to do */
221*1c3b0983Skre 			break;
222ab6821e0Skre 		}
2231fad4bb6Schristos 		ckfree(rl);
2241fad4bb6Schristos 	}
2251fad4bb6Schristos 	rt->renamed = NULL;
2261fad4bb6Schristos }
2271fad4bb6Schristos 
2281fad4bb6Schristos STATIC void
229*1c3b0983Skre fd_rename(struct redirtab *rt, int from, int to, int cloexec)
2301fad4bb6Schristos {
231ab6821e0Skre 	/* XXX someday keep a short list (8..10) of freed renamelists XXX */
2321fad4bb6Schristos 	struct renamelist *rl = ckmalloc(sizeof(struct renamelist));
2331fad4bb6Schristos 
234*1c3b0983Skre 	/*
235*1c3b0983Skre 	 * Note this list is operated as LIFO so saved fd's are
236*1c3b0983Skre 	 * undone in the opposite order to that they were saved
237*1c3b0983Skre 	 * (needed to ensure correct results)
238*1c3b0983Skre 	 */
2391fad4bb6Schristos 	rl->next = rt->renamed;
2401fad4bb6Schristos 	rt->renamed = rl;
2411fad4bb6Schristos 
2421fad4bb6Schristos 	rl->orig = from;
2431fad4bb6Schristos 	rl->into = to;
244*1c3b0983Skre 	rl->cloexec = cloexec;
2451fad4bb6Schristos }
24661f28255Scgd 
24761f28255Scgd /*
24861f28255Scgd  * Process a list of redirection commands.  If the REDIR_PUSH flag is set,
24961f28255Scgd  * old file descriptors are stashed away so that the redirection can be
25061f28255Scgd  * undone by calling popredir.  If the REDIR_BACKQ flag is set, then the
25161f28255Scgd  * standard output, and the standard error if it becomes a duplicate of
25261f28255Scgd  * stdout, is saved in memory.
25361f28255Scgd  */
25461f28255Scgd 
25561f28255Scgd void
256c02b3bbdSchristos redirect(union node *redir, int flags)
25761f28255Scgd {
25861f28255Scgd 	union node *n;
259cd799663Schristos 	struct redirtab *sv = NULL;
26061f28255Scgd 	int i;
26161f28255Scgd 	int fd;
26261f28255Scgd 	char memory[10];	/* file descriptors to write to memory */
26361f28255Scgd 
264ab6821e0Skre 	CTRACE(DBG_REDIR, ("redirect(F=0x%x):%s\n", flags, redir?"":" NONE"));
265*1c3b0983Skre 
26661f28255Scgd 	for (i = 10 ; --i >= 0 ; )
26761f28255Scgd 		memory[i] = 0;
26861f28255Scgd 	memory[1] = flags & REDIR_BACKQ;
26961f28255Scgd 	if (flags & REDIR_PUSH) {
270ab6821e0Skre 		/*
271ab6821e0Skre 		 * We don't have to worry about REDIR_VFORK here, as
272edcb4544Schristos 		 * flags & REDIR_PUSH is never true if REDIR_VFORK is set.
273edcb4544Schristos 		 */
27461f28255Scgd 		sv = ckmalloc(sizeof (struct redirtab));
2751fad4bb6Schristos 		sv->renamed = NULL;
27661f28255Scgd 		sv->next = redirlist;
27761f28255Scgd 		redirlist = sv;
27861f28255Scgd 	}
27961f28255Scgd 	for (n = redir ; n ; n = n->nfile.next) {
28052967993Skre 		int *renamed;
28152967993Skre 
28261f28255Scgd 		fd = n->nfile.fd;
28352967993Skre 		VTRACE(DBG_REDIR, ("redir %d (max=%d limit=%ld) ",
28452967993Skre 		    fd, max_user_fd, user_fd_limit));
28552967993Skre 		if (fd < user_fd_limit && fd > max_user_fd)
286c1cbf199Skre 			max_user_fd = fd;
28751a7b242Skre 		if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
28851a7b242Skre 		    n->ndup.dupfd == fd) {
28951a7b242Skre 			VTRACE(DBG_REDIR, ("!cloexec\n"));
29051a7b242Skre 			if (sh_fd(fd) != NULL ||
29151a7b242Skre 			    saved_redirected_fd(fd) != NULL)
29251a7b242Skre 				error("fd %d: %s", fd, strerror(EBADF));
29351a7b242Skre 			/* redirect from/to same file descriptor */
29451a7b242Skre 			/* make sure it stays open */
295*1c3b0983Skre 			if (fcntl(fd, F_SETFD, (fcntl_int)0) < 0)
29651a7b242Skre 				error("fd %d: %s", fd, strerror(errno));
29751a7b242Skre 			continue;
29851a7b242Skre 		}
29952967993Skre 		if ((renamed = saved_redirected_fd(fd)) != NULL) {
30052967993Skre 			int to = pick_new_fd(fd);
30152967993Skre 
30252967993Skre 			VTRACE(DBG_REDIR,
30352967993Skre 			    ("redirect: moved holding fd %d to %d\n", fd, to));
30452967993Skre 			*renamed = to;
30552967993Skre 			if (to != fd)	/* always... */
30652967993Skre 				(void)close(fd);
30752967993Skre 		}
30851c4dfe4Skre 		renumber_sh_fd(sh_fd(fd));
309fb758fd8Schristos 
3101fad4bb6Schristos 		if ((flags & REDIR_PUSH) && !is_renamed(sv->renamed, fd)) {
311c88e74efSkre 			int bigfd;
312*1c3b0983Skre 			int cloexec;
313*1c3b0983Skre 
314*1c3b0983Skre 			cloexec = fcntl(fd, F_GETFD);
315*1c3b0983Skre 			if (cloexec >= 0)
316*1c3b0983Skre 				cloexec &= FD_CLOEXEC;
317*1c3b0983Skre 			else
318*1c3b0983Skre 				cloexec = 0;
319c88e74efSkre 
32061f28255Scgd 			INTOFF;
3211fad4bb6Schristos 			if (big_sh_fd < 10)
3221fad4bb6Schristos 				find_big_fd();
323c88e74efSkre 			if ((bigfd = big_sh_fd) < max_user_fd)
324c88e74efSkre 				bigfd = max_user_fd;
325*1c3b0983Skre 			if ((i = fcntl(fd, F_DUPFD,
326*1c3b0983Skre 			    (fcntl_int)(bigfd + 1))) == -1) {
327fb758fd8Schristos 				switch (errno) {
328fb758fd8Schristos 				case EBADF:
32944f76837Syamt 					i = CLOSED;
33044f76837Syamt 					break;
3311fad4bb6Schristos 				case EMFILE:
3321fad4bb6Schristos 				case EINVAL:
3331fad4bb6Schristos 					find_big_fd();
334*1c3b0983Skre 					i = fcntl(fd, F_DUPFD,
335*1c3b0983Skre 					          (fcntl_int) big_sh_fd);
3361fad4bb6Schristos 					if (i >= 0)
3371fad4bb6Schristos 						break;
33852967993Skre 					if (errno == EMFILE || errno == EINVAL)
339*1c3b0983Skre 						i = fcntl(fd, F_DUPFD,
340*1c3b0983Skre 							  (fcntl_int) 3);
34152967993Skre 					if (i >= 0)
34252967993Skre 						break;
3431fad4bb6Schristos 					/* FALLTHRU */
344fb758fd8Schristos 				default:
345c88e74efSkre 					error("%d: %s", fd, strerror(errno));
346ee9e50eaSmycroft 					/* NOTREACHED */
347fb758fd8Schristos 				}
3481fad4bb6Schristos 			}
349*1c3b0983Skre 			if (i > biggest_sh_fd)
350*1c3b0983Skre 				biggest_sh_fd = i;
3511fad4bb6Schristos 			if (i >= 0)
352*1c3b0983Skre 				(void)fcntl(i, F_SETFD, (fcntl_int) FD_CLOEXEC);
353*1c3b0983Skre 			fd_rename(sv, fd, i, cloexec);
354*1c3b0983Skre 			VTRACE(DBG_REDIR, ("fd %d saved as %d%s ", fd, i,
355*1c3b0983Skre 			    cloexec ? "+" : ""));
35661f28255Scgd 			INTON;
35761f28255Scgd 		}
358ab6821e0Skre 		VTRACE(DBG_REDIR, ("%s\n", fd == 0 ? "STDIN" : ""));
3595916a085Ssef 		if (fd == 0)
3605916a085Ssef 			fd0_redirected++;
3615dd90992Schristos 		openredirect(n, memory, flags);
36261f28255Scgd 	}
36361f28255Scgd 	if (memory[1])
36461f28255Scgd 		out1 = &memout;
36561f28255Scgd 	if (memory[2])
36661f28255Scgd 		out2 = &memout;
36761f28255Scgd }
36861f28255Scgd 
36961f28255Scgd 
37061f28255Scgd STATIC void
3715dd90992Schristos openredirect(union node *redir, char memory[10], int flags)
37261f28255Scgd {
373a2572443Schristos 	struct stat sb;
37461f28255Scgd 	int fd = redir->nfile.fd;
37561f28255Scgd 	char *fname;
37661f28255Scgd 	int f;
377e47a2585Schristos 	int eflags, cloexec;
37861f28255Scgd 
37961f28255Scgd 	/*
38061f28255Scgd 	 * We suppress interrupts so that we won't leave open file
38161f28255Scgd 	 * descriptors around.  This may not be such a good idea because
38261f28255Scgd 	 * an open of a device or a fifo can block indefinitely.
38361f28255Scgd 	 */
38461f28255Scgd 	INTOFF;
3851fad4bb6Schristos 	if (fd < 10)
38661f28255Scgd 		memory[fd] = 0;
38761f28255Scgd 	switch (redir->nfile.type) {
38861f28255Scgd 	case NFROM:
38961f28255Scgd 		fname = redir->nfile.expfname;
3905dd90992Schristos 		if (flags & REDIR_VFORK)
3915dd90992Schristos 			eflags = O_NONBLOCK;
3925dd90992Schristos 		else
3935dd90992Schristos 			eflags = 0;
3945dd90992Schristos 		if ((f = open(fname, O_RDONLY|eflags)) < 0)
3956e50d7a8Schristos 			goto eopen;
396b86d4a74Skre 		VTRACE(DBG_REDIR, ("openredirect(< '%s') -> %d [%#x]",
397b86d4a74Skre 		    fname, f, eflags));
3985dd90992Schristos 		if (eflags)
399*1c3b0983Skre 			(void)fcntl(f, F_SETFL,
400*1c3b0983Skre 				  (fcntl_int)(fcntl(f, F_GETFL) & ~eflags));
4016e50d7a8Schristos 		break;
4026e50d7a8Schristos 	case NFROMTO:
4036e50d7a8Schristos 		fname = redir->nfile.expfname;
40400655d6cSkre 		if ((f = open(fname, O_RDWR|O_CREAT, 0666)) < 0)
4056e50d7a8Schristos 			goto ecreate;
406b86d4a74Skre 		VTRACE(DBG_REDIR, ("openredirect(<> '%s') -> %d", fname, f));
40761f28255Scgd 		break;
40861f28255Scgd 	case NTO:
409a2572443Schristos 		if (Cflag) {
410a2572443Schristos 			fname = redir->nfile.expfname;
4119e91d168Schristos 			if ((f = open(fname, O_WRONLY)) == -1) {
412a2572443Schristos 				if ((f = open(fname, O_WRONLY|O_CREAT|O_EXCL,
413a2572443Schristos 				    0666)) < 0)
414a2572443Schristos 					goto ecreate;
4159e91d168Schristos 			} else if (fstat(f, &sb) == -1) {
4169e91d168Schristos 				int serrno = errno;
417a2572443Schristos 				close(f);
4189e91d168Schristos 				errno = serrno;
419a2572443Schristos 				goto ecreate;
4209e91d168Schristos 			} else if (S_ISREG(sb.st_mode)) {
4219e91d168Schristos 				close(f);
422a2572443Schristos 				errno = EEXIST;
423a2572443Schristos 				goto ecreate;
424a2572443Schristos 			}
425ab6821e0Skre 			VTRACE(DBG_REDIR, ("openredirect(>| '%s') -> %d",
426ab6821e0Skre 			    fname, f));
427a2572443Schristos 			break;
428a2572443Schristos 		}
429f629aa28Schristos 		/* FALLTHROUGH */
430f629aa28Schristos 	case NCLOBBER:
43161f28255Scgd 		fname = redir->nfile.expfname;
432a2572443Schristos 		if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
4336e50d7a8Schristos 			goto ecreate;
434b86d4a74Skre 		VTRACE(DBG_REDIR, ("openredirect(> '%s') -> %d", fname, f));
4356e50d7a8Schristos 		break;
43661f28255Scgd 	case NAPPEND:
43761f28255Scgd 		fname = redir->nfile.expfname;
43861f28255Scgd 		if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
4396e50d7a8Schristos 			goto ecreate;
440b86d4a74Skre 		VTRACE(DBG_REDIR, ("openredirect(>> '%s') -> %d", fname, f));
4416e50d7a8Schristos 		break;
44261f28255Scgd 	case NTOFD:
44361f28255Scgd 	case NFROMFD:
44461f28255Scgd 		if (redir->ndup.dupfd >= 0) {	/* if not ">&-" */
44552967993Skre 			if (sh_fd(redir->ndup.dupfd) != NULL ||
44652967993Skre 			    saved_redirected_fd(redir->ndup.dupfd) != NULL)
447c88e74efSkre 				error("Redirect (from %d to %d) failed: %s",
448c88e74efSkre 				    redir->ndup.dupfd, fd, strerror(EBADF));
4491fad4bb6Schristos 			if (fd < 10 && redir->ndup.dupfd < 10 &&
4501fad4bb6Schristos 			    memory[redir->ndup.dupfd])
45161f28255Scgd 				memory[fd] = 1;
452698541ceSkre 			else if (copyfd(redir->ndup.dupfd, fd,
453*1c3b0983Skre 			    (flags & REDIR_KEEP) == 0, 0) < 0)
454698541ceSkre 				error("Redirect (from %d to %d) failed: %s",
455698541ceSkre 				    redir->ndup.dupfd, fd, strerror(errno));
456b86d4a74Skre 			VTRACE(DBG_REDIR, ("openredirect: %d%c&%d\n", fd,
457b86d4a74Skre 			    "<>"[redir->nfile.type==NTOFD], redir->ndup.dupfd));
458b86d4a74Skre 		} else {
459698541ceSkre 			(void) close(fd);
460b86d4a74Skre 			VTRACE(DBG_REDIR, ("openredirect: %d%c&-\n", fd,
461b86d4a74Skre 			    "<>"[redir->nfile.type==NTOFD]));
462b86d4a74Skre 		}
4636e50d7a8Schristos 		INTON;
4646e50d7a8Schristos 		return;
46561f28255Scgd 	case NHERE:
46661f28255Scgd 	case NXHERE:
467b86d4a74Skre 		VTRACE(DBG_REDIR, ("openredirect: %d<<...", fd));
468ab6821e0Skre 		f = openhere(redir);
4696e50d7a8Schristos 		break;
47061f28255Scgd 	default:
47161f28255Scgd 		abort();
47261f28255Scgd 	}
4736e50d7a8Schristos 
474*1c3b0983Skre 	if (f > biggest_sh_fd)
475*1c3b0983Skre 		biggest_sh_fd = f;
476*1c3b0983Skre 
47784071b75Skre 	cloexec = fd > 2 && (flags & REDIR_KEEP) == 0 && !posix;
4786e50d7a8Schristos 	if (f != fd) {
479b86d4a74Skre 		VTRACE(DBG_REDIR, (" -> %d", fd));
480*1c3b0983Skre 		if (copyfd(f, fd, cloexec, 1) < 0) {
481698541ceSkre 			int e = errno;
482698541ceSkre 
48351a7b242Skre 			VTRACE(DBG_REDIR, (" failed: %s\n", strerror(e)));
484698541ceSkre 			error("redirect reassignment (fd %d) failed: %s", fd,
485698541ceSkre 			    strerror(e));
486698541ceSkre 		}
487e47a2585Schristos 	} else if (cloexec)
488*1c3b0983Skre 		(void)fcntl(f, F_SETFD, (fcntl_int) FD_CLOEXEC);
489b86d4a74Skre 	VTRACE(DBG_REDIR, ("%s\n", cloexec ? " cloexec" : ""));
490d18385a5Schristos 
49161f28255Scgd 	INTON;
4926e50d7a8Schristos 	return;
4936e50d7a8Schristos  ecreate:
494899c734bSmsaitoh 	exerrno = 1;
4956e50d7a8Schristos 	error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
4966e50d7a8Schristos  eopen:
497899c734bSmsaitoh 	exerrno = 1;
4986e50d7a8Schristos 	error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
49961f28255Scgd }
50061f28255Scgd 
50161f28255Scgd 
50261f28255Scgd /*
50361f28255Scgd  * Handle here documents.  Normally we fork off a process to write the
50461f28255Scgd  * data to a pipe.  If the document is short, we can stuff the data in
50561f28255Scgd  * the pipe without forking.
50661f28255Scgd  */
50761f28255Scgd 
50861f28255Scgd STATIC int
509071e965cSyamt openhere(const union node *redir)
51061f28255Scgd {
51161f28255Scgd 	int pip[2];
51207bae7edSchristos 	int len = 0;
51361f28255Scgd 
51461f28255Scgd 	if (pipe(pip) < 0)
51561f28255Scgd 		error("Pipe call failed");
516*1c3b0983Skre 	if (pip[1] > biggest_sh_fd)
517*1c3b0983Skre 		biggest_sh_fd = pip[1];
51816d85571Skre 	len = strlen(redir->nhere.text);
51916d85571Skre 	VTRACE(DBG_REDIR, ("openhere(%p) [%d] \"%.*s\"%s\n", redir, len,
52016d85571Skre 	    (len < 40 ? len : 40), redir->nhere.text, (len < 40 ? "" : "...")));
52116d85571Skre 	if (len <= PIPESIZE) {		/* XXX eventually copy FreeBSD method */
52216d85571Skre 		xwrite(pip[1], redir->nhere.text, len);
52361f28255Scgd 		goto out;
52461f28255Scgd 	}
525ab6821e0Skre 	VTRACE(DBG_REDIR, (" forking [%d,%d]\n", pip[0], pip[1]));
5269f61b804Splunky 	if (forkshell(NULL, NULL, FORK_NOJOB) == 0) {
52761f28255Scgd 		close(pip[0]);
52861f28255Scgd 		signal(SIGINT, SIG_IGN);
52961f28255Scgd 		signal(SIGQUIT, SIG_IGN);
53061f28255Scgd 		signal(SIGHUP, SIG_IGN);
53161f28255Scgd #ifdef SIGTSTP
53261f28255Scgd 		signal(SIGTSTP, SIG_IGN);
53361f28255Scgd #endif
53461f28255Scgd 		signal(SIGPIPE, SIG_DFL);
53516d85571Skre 		xwrite(pip[1], redir->nhere.text, len);
536b8bee70dSkre 		VTRACE(DBG_PROCS|DBG_REDIR, ("wrote here doc.  exiting(0)\n"));
53761f28255Scgd 		_exit(0);
53861f28255Scgd 	}
539ab6821e0Skre 	VTRACE(DBG_REDIR, ("openhere (closing %d)", pip[1]));
540*1c3b0983Skre  out:;
54161f28255Scgd 	close(pip[1]);
542ab6821e0Skre 	VTRACE(DBG_REDIR, (" (pipe fd=%d)", pip[0]));
54361f28255Scgd 	return pip[0];
54461f28255Scgd }
54561f28255Scgd 
54661f28255Scgd 
54761f28255Scgd 
54861f28255Scgd /*
549*1c3b0983Skre  * if (reset == POPREDIR_UNDO)
55061f28255Scgd  *	Undo the effects of the last redirection.
551*1c3b0983Skre  * else if (reset == POPREDIR_PERMANENT)
552*1c3b0983Skre  *	Make the last redirection permanent
553*1c3b0983Skre  * else / * reset == POPREDIR_DISCARD * /
554*1c3b0983Skre  *	Just throw away the redirection
55561f28255Scgd  */
55661f28255Scgd 
55761f28255Scgd void
558*1c3b0983Skre popredir(int reset)
559c02b3bbdSchristos {
56048250187Stls 	struct redirtab *rp = redirlist;
56161f28255Scgd 
56261f28255Scgd 	INTOFF;
563*1c3b0983Skre 	free_rl(rp, reset);
56461f28255Scgd 	redirlist = rp->next;
56561f28255Scgd 	ckfree(rp);
56661f28255Scgd 	INTON;
56761f28255Scgd }
56861f28255Scgd 
56961f28255Scgd /*
57061f28255Scgd  * Undo all redirections.  Called on error or interrupt.
57161f28255Scgd  */
57261f28255Scgd 
57361f28255Scgd #ifdef mkinit
57461f28255Scgd 
57561f28255Scgd INCLUDE "redir.h"
57661f28255Scgd 
57761f28255Scgd RESET {
57861f28255Scgd 	while (redirlist)
579*1c3b0983Skre 		popredir(POPREDIR_UNDO);
58061f28255Scgd }
58161f28255Scgd 
58261f28255Scgd SHELLPROC {
583edcb4544Schristos 	clearredir(0);
58461f28255Scgd }
58561f28255Scgd 
58661f28255Scgd #endif
58761f28255Scgd 
58837ed7877Sjtc /* Return true if fd 0 has already been redirected at least once.  */
58937ed7877Sjtc int
5901fad4bb6Schristos fd0_redirected_p(void)
5911fad4bb6Schristos {
59237ed7877Sjtc 	return fd0_redirected != 0;
59337ed7877Sjtc }
59461f28255Scgd 
59561f28255Scgd /*
59661f28255Scgd  * Discard all saved file descriptors.
59761f28255Scgd  */
59861f28255Scgd 
59961f28255Scgd void
600da4f7877Smatt clearredir(int vforked)
601edcb4544Schristos {
60248250187Stls 	struct redirtab *rp;
6031fad4bb6Schristos 	struct renamelist *rl;
60461f28255Scgd 
605*1c3b0983Skre 	/*
606*1c3b0983Skre 	 * This happens only in a child process attempting to
607*1c3b0983Skre 	 * exec a script which failed ENOEXEC.   Any redirections
608*1c3b0983Skre 	 * that have been made are for that script, after it
609*1c3b0983Skre 	 * is done, we exit, there is nothing  to restore, so
610*1c3b0983Skre 	 * just make the refirectoins permanent.
611*1c3b0983Skre 	 *
612*1c3b0983Skre 	 * free_rl() does that does that for us, unless we're
613*1c3b0983Skre 	 * a child of a vfork() in which case that is unsafe to
614*1c3b0983Skre 	 * call - that should never happen, but just in case, fale
615*1c3b0983Skre 	 * it here
616*1c3b0983Skre 	 */
617*1c3b0983Skre 
61861f28255Scgd 	for (rp = redirlist ; rp ; rp = rp->next) {
619edcb4544Schristos 		if (!vforked)
620*1c3b0983Skre 			free_rl(rp, POPREDIR_PERMANENT);
6211fad4bb6Schristos 		else for (rl = rp->renamed; rl; rl = rl->next)
6221fad4bb6Schristos 			if (rl->into >= 0)
6231fad4bb6Schristos 				close(rl->into);
62461f28255Scgd 	}
62561f28255Scgd }
62661f28255Scgd 
62761f28255Scgd 
62861f28255Scgd 
62961f28255Scgd /*
630*1c3b0983Skre  * Copy a file descriptor (from) to (also) be 'to'..
631698541ceSkre  * cloexec indicates if we want close-on-exec or not.
632*1c3b0983Skre  * move indicates if "from" should be closed on success (yes if set).
633698541ceSkre  * Returns -1 if any error occurs.
63461f28255Scgd  */
63561f28255Scgd 
636698541ceSkre STATIC int
637*1c3b0983Skre copyfd(int from, int to, int cloexec, int move)
6384ce0d34aScgd {
63961f28255Scgd 	int newfd;
64061f28255Scgd 
641f2dc7540Skre 	if (cloexec && to > 2) {
642f2dc7540Skre #ifdef O_CLOEXEC
643d18385a5Schristos 		newfd = dup3(from, to, O_CLOEXEC);
644f2dc7540Skre #else
645f2dc7540Skre 		newfd = dup2(from, to);
646*1c3b0983Skre 		if (newfd >= 0)
647*1c3b0983Skre 			fcntl(newfd, F_SETFD,
648*1c3b0983Skre 			    (fcntl_int)(fcntl(newfd, F_GETFD) | FD_CLOEXEC));
649f2dc7540Skre #endif
650f2dc7540Skre 	} else
651db28d566Spooka 		newfd = dup2(from, to);
652698541ceSkre 
653*1c3b0983Skre 	if (move && newfd == to && from != to)
654*1c3b0983Skre 		close(from);
655*1c3b0983Skre 
656*1c3b0983Skre 	if (newfd != to) {
657*1c3b0983Skre 		if (newfd < 0)
658*1c3b0983Skre 			error("Unable to dup(%d->%d): %s", from, to,
659*1c3b0983Skre 			    strerror(errno));
660*1c3b0983Skre 		else {
661*1c3b0983Skre 			close(newfd);
662*1c3b0983Skre 			error("Moving fd %d to %d made %d!", from, to, newfd);
663*1c3b0983Skre 		}
664*1c3b0983Skre 	}
665*1c3b0983Skre 
666*1c3b0983Skre 	if (newfd > biggest_sh_fd)
667*1c3b0983Skre 		biggest_sh_fd = newfd;
668*1c3b0983Skre 
66961f28255Scgd 	return newfd;
6705916a085Ssef }
6711fad4bb6Schristos 
672698541ceSkre /*
673698541ceSkre  * rename fd from to be fd to (closing from).
674698541ceSkre  * close-on-exec is never set on 'to' (unless
675698541ceSkre  * from==to and it was set on from) - ie: a no-op
676698541ceSkre  * returns to (or errors() if an error occurs).
677698541ceSkre  *
678698541ceSkre  * This is mostly used for rearranging the
679698541ceSkre  * results from pipe().
680698541ceSkre  */
6811fad4bb6Schristos int
6821fad4bb6Schristos movefd(int from, int to)
6831fad4bb6Schristos {
684*1c3b0983Skre 	if (from > biggest_sh_fd)
685*1c3b0983Skre 		biggest_sh_fd = from;
686*1c3b0983Skre 	if (to > biggest_sh_fd)
687*1c3b0983Skre 		biggest_sh_fd = to;
688*1c3b0983Skre 
6891fad4bb6Schristos 	if (from == to)
6901fad4bb6Schristos 		return to;
6911fad4bb6Schristos 
6921fad4bb6Schristos 	(void)close(to);
693*1c3b0983Skre 	if (copyfd(from, to, 0, 1) != to) {
694698541ceSkre 		int e = errno;
695698541ceSkre 
696698541ceSkre 		(void) close(from);
697698541ceSkre 		error("Unable to make fd %d: %s", to, strerror(e));
698698541ceSkre 	}
6991fad4bb6Schristos 
7001fad4bb6Schristos 	return to;
7011fad4bb6Schristos }
7021fad4bb6Schristos 
7031fad4bb6Schristos STATIC void
7041fad4bb6Schristos find_big_fd(void)
7051fad4bb6Schristos {
7061fad4bb6Schristos 	int i, fd;
707374c12e6Skre 	static int last_start = 3; /* aim to keep sh fd's under 20 */
7081fad4bb6Schristos 
70951c4dfe4Skre 	if (last_start < 10)
71051c4dfe4Skre 		last_start++;
71151c4dfe4Skre 
71251c4dfe4Skre 	for (i = (1 << last_start); i >= 10; i >>= 1) {
713*1c3b0983Skre 		if ((fd = fcntl(0, F_DUPFD, (fcntl_int)(i - 1))) >= 0) {
714*1c3b0983Skre 			if (fd > biggest_sh_fd)
715*1c3b0983Skre 				biggest_sh_fd = fd;
7161fad4bb6Schristos 			close(fd);
7171fad4bb6Schristos 			break;
7181fad4bb6Schristos 		}
7191fad4bb6Schristos 	}
7201fad4bb6Schristos 
7211fad4bb6Schristos 	fd = (i / 5) * 4;
72251c4dfe4Skre 	if (fd < 10)
7231fad4bb6Schristos 		fd = 10;
7241fad4bb6Schristos 
7251fad4bb6Schristos 	big_sh_fd = fd;
7261fad4bb6Schristos }
7271fad4bb6Schristos 
728698541ceSkre /*
729698541ceSkre  * If possible, move file descriptor fd out of the way
730698541ceSkre  * of expected user fd values.   Returns the new fd
731698541ceSkre  * (which may be the input fd if things do not go well.)
732698541ceSkre  * Always set close-on-exec on the result, and close
733698541ceSkre  * the input fd unless it is to be our result.
734698541ceSkre  */
7351fad4bb6Schristos int
7361fad4bb6Schristos to_upper_fd(int fd)
7371fad4bb6Schristos {
7381fad4bb6Schristos 	int i;
7391fad4bb6Schristos 
740b86d4a74Skre 	VTRACE(DBG_REDIR|DBG_OUTPUT, ("to_upper_fd(%d)", fd));
74152967993Skre 	if (big_sh_fd < 10 || big_sh_fd >= user_fd_limit)
7421fad4bb6Schristos 		find_big_fd();
7431fad4bb6Schristos 	do {
744*1c3b0983Skre 		i = fcntl(fd, F_DUPFD_CLOEXEC, (fcntl_int) big_sh_fd);
7451fad4bb6Schristos 		if (i >= 0) {
746*1c3b0983Skre 			if (i > biggest_sh_fd)
747*1c3b0983Skre 				biggest_sh_fd = i;
7481fad4bb6Schristos 			if (fd != i)
7491fad4bb6Schristos 				close(fd);
750b86d4a74Skre 			VTRACE(DBG_REDIR|DBG_OUTPUT, ("-> %d\n", i));
7511fad4bb6Schristos 			return i;
7521fad4bb6Schristos 		}
75351c4dfe4Skre 		if (errno != EMFILE && errno != EINVAL)
7541fad4bb6Schristos 			break;
7551fad4bb6Schristos 		find_big_fd();
7561fad4bb6Schristos 	} while (big_sh_fd > 10);
7571fad4bb6Schristos 
75807ee700aSkre 	/*
759698541ceSkre 	 * If we wanted to move this fd to some random high number
76007ee700aSkre 	 * we certainly do not intend to pass it through exec, even
76107ee700aSkre 	 * if the reassignment failed.
76207ee700aSkre 	 */
763*1c3b0983Skre 	(void)fcntl(fd, F_SETFD, (fcntl_int) FD_CLOEXEC);
764b86d4a74Skre 	VTRACE(DBG_REDIR|DBG_OUTPUT, (" fails ->%d\n", fd));
7651fad4bb6Schristos 	return fd;
7661fad4bb6Schristos }
767168f1d4aSchristos 
76851c4dfe4Skre void
76951c4dfe4Skre register_sh_fd(int fd, void (*cb)(int, int))
77051c4dfe4Skre {
77151c4dfe4Skre 	struct shell_fds *fp;
77251c4dfe4Skre 
77351c4dfe4Skre 	fp = ckmalloc(sizeof (struct shell_fds));
77451c4dfe4Skre 	if (fp != NULL) {
77551c4dfe4Skre 		fp->nxt = sh_fd_list;
77651c4dfe4Skre 		sh_fd_list = fp;
77751c4dfe4Skre 
77851c4dfe4Skre 		fp->fd = fd;
77951c4dfe4Skre 		fp->cb = cb;
78051c4dfe4Skre 	}
78151c4dfe4Skre }
78251c4dfe4Skre 
78351c4dfe4Skre void
78451c4dfe4Skre sh_close(int fd)
78551c4dfe4Skre {
78651c4dfe4Skre 	struct shell_fds **fpp, *fp;
78751c4dfe4Skre 
78851c4dfe4Skre 	fpp = &sh_fd_list;
78951c4dfe4Skre 	while ((fp = *fpp) != NULL) {
79051c4dfe4Skre 		if (fp->fd == fd) {
79151c4dfe4Skre 			*fpp = fp->nxt;
79251c4dfe4Skre 			ckfree(fp);
79351c4dfe4Skre 			break;
79451c4dfe4Skre 		}
79551c4dfe4Skre 		fpp = &fp->nxt;
79651c4dfe4Skre 	}
79751c4dfe4Skre 	(void)close(fd);
79851c4dfe4Skre }
79951c4dfe4Skre 
80051c4dfe4Skre STATIC struct shell_fds *
80151c4dfe4Skre sh_fd(int fd)
80251c4dfe4Skre {
80351c4dfe4Skre 	struct shell_fds *fp;
80451c4dfe4Skre 
80551c4dfe4Skre 	for (fp = sh_fd_list; fp != NULL; fp = fp->nxt)
80651c4dfe4Skre 		if (fp->fd == fd)
80751c4dfe4Skre 			return fp;
80851c4dfe4Skre 	return NULL;
80951c4dfe4Skre }
81051c4dfe4Skre 
81152967993Skre STATIC int
81252967993Skre pick_new_fd(int fd)
81352967993Skre {
81452967993Skre 	int to;
81552967993Skre 
816*1c3b0983Skre 	to = fcntl(fd, F_DUPFD_CLOEXEC, (fcntl_int) big_sh_fd);
81752967993Skre 	if (to == -1 && big_sh_fd >= 22)
818*1c3b0983Skre 		to = fcntl(fd, F_DUPFD_CLOEXEC, (fcntl_int) (big_sh_fd / 2));
81952967993Skre 	if (to == -1)
820*1c3b0983Skre 		to = fcntl(fd, F_DUPFD_CLOEXEC, (fcntl_int) (fd + 1));
82152967993Skre 	if (to == -1)
822*1c3b0983Skre 		to = fcntl(fd, F_DUPFD_CLOEXEC, (fcntl_int) 10);
82352967993Skre 	if (to == -1)
824*1c3b0983Skre 		to = fcntl(fd, F_DUPFD_CLOEXEC, (fcntl_int)  3);
82552967993Skre 	if (to == -1)
82652967993Skre 		error("insufficient file descriptors available");
82752967993Skre 	CLOEXEC(to);
828*1c3b0983Skre 	if (to > biggest_sh_fd)
829*1c3b0983Skre 		biggest_sh_fd = to;
83052967993Skre 	return to;
83152967993Skre }
83252967993Skre 
83351c4dfe4Skre STATIC void
83451c4dfe4Skre renumber_sh_fd(struct shell_fds *fp)
83551c4dfe4Skre {
83651c4dfe4Skre 	int to;
83751c4dfe4Skre 
83851c4dfe4Skre 	if (fp == NULL)
83951c4dfe4Skre 		return;
84051c4dfe4Skre 
841374c12e6Skre 	/*
842374c12e6Skre 	 * if we have had a collision, and the sh fd was a "big" one
843374c12e6Skre 	 * try moving the sh fd base to a higher number (if possible)
844374c12e6Skre 	 * so future sh fds are less likely to be in the user's sights
845374c12e6Skre 	 * (incl this one when moved)
846374c12e6Skre 	 */
847374c12e6Skre 	if (fp->fd >= big_sh_fd)
848374c12e6Skre 		find_big_fd();
849374c12e6Skre 
85052967993Skre 	to = pick_new_fd(fp->fd);
85151c4dfe4Skre 
85251c4dfe4Skre 	if (fp->fd == to)	/* impossible? */
85351c4dfe4Skre 		return;
85451c4dfe4Skre 
85552967993Skre 	VTRACE(DBG_REDIR, ("renumber_sh_fd: moved shell fd %d to %d\n",
85652967993Skre 	    fp->fd, to));
85752967993Skre 
85851c4dfe4Skre 	(*fp->cb)(fp->fd, to);
85951c4dfe4Skre 	(void)close(fp->fd);
86051c4dfe4Skre 	fp->fd = to;
86151c4dfe4Skre }
86251c4dfe4Skre 
8630ff2aa10Skre static const struct flgnames {
864168f1d4aSchristos 	const char *name;
8651f81ff14Skre 	uint16_t minch;
866168f1d4aSchristos 	uint32_t value;
867168f1d4aSchristos } nv[] = {
868168f1d4aSchristos #ifdef O_APPEND
8691f81ff14Skre 	{ "append",	2,	O_APPEND 	},
870f2dc7540Skre #else
871f2dc7540Skre # define O_APPEND 0
872168f1d4aSchristos #endif
873168f1d4aSchristos #ifdef O_ASYNC
8741f81ff14Skre 	{ "async",	2,	O_ASYNC		},
875f2dc7540Skre #else
876f2dc7540Skre # define O_ASYNC 0
877168f1d4aSchristos #endif
878168f1d4aSchristos #ifdef O_SYNC
8791f81ff14Skre 	{ "sync",	2,	O_SYNC		},
880f2dc7540Skre #else
881f2dc7540Skre # define O_SYNC 0
882168f1d4aSchristos #endif
883168f1d4aSchristos #ifdef O_NONBLOCK
8841f81ff14Skre 	{ "nonblock",	3,	O_NONBLOCK	},
885f2dc7540Skre #else
886f2dc7540Skre # define O_NONBLOCK 0
887168f1d4aSchristos #endif
888168f1d4aSchristos #ifdef O_FSYNC
8891f81ff14Skre 	{ "fsync",	2,	O_FSYNC		},
890f2dc7540Skre #else
891f2dc7540Skre # define O_FSYNC 0
892168f1d4aSchristos #endif
893168f1d4aSchristos #ifdef O_DSYNC
8941f81ff14Skre 	{ "dsync",	2,	O_DSYNC		},
895f2dc7540Skre #else
896f2dc7540Skre # define O_DSYNC 0
897168f1d4aSchristos #endif
898168f1d4aSchristos #ifdef O_RSYNC
8991f81ff14Skre 	{ "rsync",	2,	O_RSYNC		},
900f2dc7540Skre #else
901f2dc7540Skre # define O_RSYNC 0
902168f1d4aSchristos #endif
903e405eb35Skamil #ifdef O_ALT_IO
9041f81ff14Skre 	{ "altio",	2,	O_ALT_IO	},
905f2dc7540Skre #else
906f2dc7540Skre # define O_ALT_IO 0
907168f1d4aSchristos #endif
908168f1d4aSchristos #ifdef O_DIRECT
9091f81ff14Skre 	{ "direct",	2,	O_DIRECT	},
910f2dc7540Skre #else
911f2dc7540Skre # define O_DIRECT 0
912168f1d4aSchristos #endif
913168f1d4aSchristos #ifdef O_NOSIGPIPE
9141f81ff14Skre 	{ "nosigpipe",	3,	O_NOSIGPIPE	},
915f2dc7540Skre #else
916f2dc7540Skre # define O_NOSIGPIPE 0
917168f1d4aSchristos #endif
918f2dc7540Skre 
919f2dc7540Skre #define ALLFLAGS (O_APPEND|O_ASYNC|O_SYNC|O_NONBLOCK|O_DSYNC|O_RSYNC|\
920f2dc7540Skre     O_ALT_IO|O_DIRECT|O_NOSIGPIPE)
921f2dc7540Skre 
922f2dc7540Skre #ifndef	O_CLOEXEC
9238d9b0751Skre # define O_CLOEXEC	((~ALLFLAGS) ^ ((~ALLFLAGS) & ((~ALLFLAGS) - 1)))
924f2dc7540Skre #endif
925f2dc7540Skre 
926f2dc7540Skre 	/* for any system we support, close on exec is always defined */
9271f81ff14Skre 	{ "cloexec",	2,	O_CLOEXEC	},
9281f81ff14Skre 	{ 0, 0, 0 }
929168f1d4aSchristos };
930f2dc7540Skre 
931f2dc7540Skre #ifndef O_ACCMODE
932f2dc7540Skre # define O_ACCMODE	0
933f2dc7540Skre #endif
934f2dc7540Skre #ifndef O_RDONLY
935f2dc7540Skre # define O_RDONLY	0
936f2dc7540Skre #endif
937f2dc7540Skre #ifndef O_WRONLY
938f2dc7540Skre # define O_WRONLY	0
939f2dc7540Skre #endif
940f2dc7540Skre #ifndef O_RWDR
941f2dc7540Skre # define O_RWDR		0
942f2dc7540Skre #endif
943f2dc7540Skre #ifndef O_SHLOCK
944f2dc7540Skre # define O_SHLOCK	0
945f2dc7540Skre #endif
946f2dc7540Skre #ifndef O_EXLOCK
947f2dc7540Skre # define O_EXLOCK	0
948f2dc7540Skre #endif
949f2dc7540Skre #ifndef O_NOFOLLOW
950f2dc7540Skre # define O_NOFOLLOW	0
951f2dc7540Skre #endif
952f2dc7540Skre #ifndef O_CREAT
953f2dc7540Skre # define O_CREAT	0
954f2dc7540Skre #endif
955f2dc7540Skre #ifndef O_TRUNC
956f2dc7540Skre # define O_TRUNC	0
957f2dc7540Skre #endif
958f2dc7540Skre #ifndef O_EXCL
959f2dc7540Skre # define O_EXCL		0
960f2dc7540Skre #endif
961f2dc7540Skre #ifndef O_NOCTTY
962f2dc7540Skre # define O_NOCTTY	0
963f2dc7540Skre #endif
964f2dc7540Skre #ifndef O_DIRECTORY
965f2dc7540Skre # define O_DIRECTORY	0
966f2dc7540Skre #endif
967f2dc7540Skre #ifndef O_REGULAR
968f2dc7540Skre # define O_REGULAR	0
969f2dc7540Skre #endif
970f2dc7540Skre /*
971f2dc7540Skre  * flags that F_GETFL might return that we want to ignore
972f2dc7540Skre  *
973f2dc7540Skre  * F_GETFL should not actually return these, they're all just open()
974f2dc7540Skre  * modifiers, rather than state, but just in case...
975f2dc7540Skre  */
976f2dc7540Skre #define IGNFLAGS (O_ACCMODE|O_RDONLY|O_WRONLY|O_RDWR|O_SHLOCK|O_EXLOCK| \
977f2dc7540Skre     O_NOFOLLOW|O_CREAT|O_TRUNC|O_EXCL|O_NOCTTY|O_DIRECTORY|O_REGULAR)
978168f1d4aSchristos 
979168f1d4aSchristos static int
980*1c3b0983Skre getflags(int fd, int p, int all)
981168f1d4aSchristos {
982168f1d4aSchristos 	int c, f;
983168f1d4aSchristos 
984*1c3b0983Skre 	if (!all && (sh_fd(fd) != NULL || saved_redirected_fd(fd) != NULL)) {
98551c4dfe4Skre 		if (!p)
98651c4dfe4Skre 			return -1;
987c88e74efSkre 		error("Can't get status for fd=%d (%s)", fd, strerror(EBADF));
98851c4dfe4Skre 	}
98951c4dfe4Skre 
990168f1d4aSchristos 	if ((c = fcntl(fd, F_GETFD)) == -1) {
991168f1d4aSchristos 		if (!p)
992168f1d4aSchristos 			return -1;
993168f1d4aSchristos 		error("Can't get status for fd=%d (%s)", fd, strerror(errno));
994168f1d4aSchristos 	}
995168f1d4aSchristos 	if ((f = fcntl(fd, F_GETFL)) == -1) {
996168f1d4aSchristos 		if (!p)
997168f1d4aSchristos 			return -1;
998168f1d4aSchristos 		error("Can't get flags for fd=%d (%s)", fd, strerror(errno));
999168f1d4aSchristos 	}
1000f2dc7540Skre 	f &= ~IGNFLAGS;		/* clear anything we know about, but ignore */
10010ff2aa10Skre 	if (c & FD_CLOEXEC)
1002168f1d4aSchristos 		f |= O_CLOEXEC;
1003f2dc7540Skre 	return f;
1004168f1d4aSchristos }
1005168f1d4aSchristos 
1006168f1d4aSchristos static void
10070ff2aa10Skre printone(int fd, int p, int verbose, int pfd)
1008168f1d4aSchristos {
1009*1c3b0983Skre 	int f = getflags(fd, p, verbose > 1);
10100ff2aa10Skre 	const struct flgnames *fn;
1011168f1d4aSchristos 
1012168f1d4aSchristos 	if (f == -1)
1013168f1d4aSchristos 		return;
1014168f1d4aSchristos 
10150ff2aa10Skre 	if (pfd)
1016168f1d4aSchristos 		outfmt(out1, "%d: ", fd);
10170ff2aa10Skre 	for (fn = nv; fn->name; fn++) {
10180ff2aa10Skre 		if (f & fn->value) {
10190ff2aa10Skre 			outfmt(out1, "%s%s", verbose ? "+" : "", fn->name);
10200ff2aa10Skre 			f &= ~fn->value;
1021168f1d4aSchristos 		} else if (verbose)
10220ff2aa10Skre 			outfmt(out1, "-%s", fn->name);
1023168f1d4aSchristos 		else
1024168f1d4aSchristos 			continue;
10250ff2aa10Skre 		if (f || (verbose && fn[1].name))
1026168f1d4aSchristos 			outfmt(out1, ",");
1027168f1d4aSchristos 	}
10280ff2aa10Skre 	if (verbose && f)		/* f should be normally be 0 */
10290ff2aa10Skre 		outfmt(out1, " +%#x", f);
1030168f1d4aSchristos 	outfmt(out1, "\n");
1031168f1d4aSchristos }
1032168f1d4aSchristos 
10330ff2aa10Skre static void
1034168f1d4aSchristos parseflags(char *s, int *p, int *n)
1035168f1d4aSchristos {
10360ff2aa10Skre 	int *v, *w;
10370ff2aa10Skre 	const struct flgnames *fn;
10381f81ff14Skre 	size_t len;
1039168f1d4aSchristos 
1040168f1d4aSchristos 	*p = 0;
1041168f1d4aSchristos 	*n = 0;
1042168f1d4aSchristos 	for (s = strtok(s, ","); s; s = strtok(NULL, ",")) {
10430ff2aa10Skre 		switch (*s++) {
1044168f1d4aSchristos 		case '+':
1045168f1d4aSchristos 			v = p;
10460ff2aa10Skre 			w = n;
1047168f1d4aSchristos 			break;
1048168f1d4aSchristos 		case '-':
1049168f1d4aSchristos 			v = n;
10500ff2aa10Skre 			w = p;
1051168f1d4aSchristos 			break;
1052168f1d4aSchristos 		default:
10530ff2aa10Skre 			error("Missing +/- indicator before flag %s", s-1);
1054168f1d4aSchristos 		}
1055168f1d4aSchristos 
10561f81ff14Skre 		len = strlen(s);
10570ff2aa10Skre 		for (fn = nv; fn->name; fn++)
10581f81ff14Skre 			if (len >= fn->minch && strncmp(s,fn->name,len) == 0) {
10590ff2aa10Skre 				*v |= fn->value;
10600ff2aa10Skre 				*w &=~ fn->value;
1061168f1d4aSchristos 				break;
1062168f1d4aSchristos 			}
10630ff2aa10Skre 		if (fn->name == 0)
1064168f1d4aSchristos 			error("Bad flag `%s'", s);
1065168f1d4aSchristos 	}
1066168f1d4aSchristos }
1067168f1d4aSchristos 
1068168f1d4aSchristos static void
1069168f1d4aSchristos setone(int fd, int pos, int neg, int verbose)
1070168f1d4aSchristos {
1071*1c3b0983Skre 	int f = getflags(fd, 1, 0);
10720ff2aa10Skre 	int n, cloexec;
10730ff2aa10Skre 
1074168f1d4aSchristos 	if (f == -1)
1075168f1d4aSchristos 		return;
1076168f1d4aSchristos 
10770ff2aa10Skre 	cloexec = -1;
1078168f1d4aSchristos 	if ((pos & O_CLOEXEC) && !(f & O_CLOEXEC))
1079168f1d4aSchristos 		cloexec = FD_CLOEXEC;
1080168f1d4aSchristos 	if ((neg & O_CLOEXEC) && (f & O_CLOEXEC))
1081168f1d4aSchristos 		cloexec = 0;
1082168f1d4aSchristos 
1083*1c3b0983Skre 	/* Don't allow O_CLOEXEC on stdin, stdout, or stderr */
1084*1c3b0983Skre 	if ((cloexec > 0 && fd <= 2 && (errno = EINVAL)) ||
1085*1c3b0983Skre 	    (cloexec != -1 && fcntl(fd, F_SETFD, (fcntl_int) cloexec) == -1))
1086168f1d4aSchristos 		error("Can't set status for fd=%d (%s)", fd, strerror(errno));
1087168f1d4aSchristos 
1088168f1d4aSchristos 	pos &= ~O_CLOEXEC;
1089168f1d4aSchristos 	neg &= ~O_CLOEXEC;
1090168f1d4aSchristos 	f &= ~O_CLOEXEC;
10910ff2aa10Skre 	n = f;
1092168f1d4aSchristos 	n |= pos;
1093168f1d4aSchristos 	n &= ~neg;
1094*1c3b0983Skre 	if (n != f && fcntl(fd, F_SETFL, (fcntl_int)n) == -1)
1095168f1d4aSchristos 		error("Can't set flags for fd=%d (%s)", fd, strerror(errno));
1096168f1d4aSchristos 	if (verbose)
10970ff2aa10Skre 		printone(fd, 1, verbose, 1);
1098168f1d4aSchristos }
1099168f1d4aSchristos 
1100168f1d4aSchristos int
1101168f1d4aSchristos fdflagscmd(int argc, char *argv[])
1102168f1d4aSchristos {
1103168f1d4aSchristos 	char *num;
1104168f1d4aSchristos 	int verbose = 0, ch, pos = 0, neg = 0;
1105168f1d4aSchristos 	char *setflags = NULL;
1106168f1d4aSchristos 
1107168f1d4aSchristos 	optreset = 1; optind = 1; /* initialize getopt */
1108*1c3b0983Skre 	while ((ch = getopt(argc, argv, ":vs:"
1109*1c3b0983Skre #ifdef DEBUG
1110*1c3b0983Skre 					     "V"
1111*1c3b0983Skre #endif
1112*1c3b0983Skre 						)) != -1)
1113168f1d4aSchristos 		switch ((char)ch) {
1114*1c3b0983Skre #ifdef DEBUG
1115*1c3b0983Skre 		case 'V':
1116*1c3b0983Skre 			verbose = 2;
1117*1c3b0983Skre 			break;
1118*1c3b0983Skre #endif
1119168f1d4aSchristos 		case 'v':
1120168f1d4aSchristos 			verbose = 1;
1121168f1d4aSchristos 			break;
1122168f1d4aSchristos 		case 's':
11230ff2aa10Skre 			if (setflags)
11240ff2aa10Skre 				goto msg;
1125168f1d4aSchristos 			setflags = optarg;
1126168f1d4aSchristos 			break;
1127168f1d4aSchristos 		case '?':
1128168f1d4aSchristos 		default:
1129168f1d4aSchristos 		msg:
11300ff2aa10Skre 			error("Usage: fdflags [-v] [-s <flags> fd] [fd...]");
1131168f1d4aSchristos 			/* NOTREACHED */
1132168f1d4aSchristos 		}
1133168f1d4aSchristos 
1134168f1d4aSchristos 	argc -= optind, argv += optind;
1135168f1d4aSchristos 
11360ff2aa10Skre 	if (setflags)
11370ff2aa10Skre 		parseflags(setflags, &pos, &neg);
1138168f1d4aSchristos 
1139168f1d4aSchristos 	if (argc == 0) {
1140c1cbf199Skre 		int i;
11410ff2aa10Skre 
1142168f1d4aSchristos 		if (setflags)
1143168f1d4aSchristos 			goto msg;
11440ff2aa10Skre 
1145c1cbf199Skre 		for (i = 0; i <= max_user_fd; i++)
11460ff2aa10Skre 			printone(i, 0, verbose, 1);
1147*1c3b0983Skre 		if (verbose > 1)
1148*1c3b0983Skre 			while (i <= biggest_sh_fd)
1149*1c3b0983Skre 				printone(i++, 0, verbose, 1);
1150168f1d4aSchristos 
1151b65a5b90Skre 	} else while ((num = *argv++) != NULL) {
11520ff2aa10Skre 		int fd = number(num);
11530ff2aa10Skre 
11549df68badSkre 		while (num[0] == '0' && num[1] != '\0')		/* skip 0's */
11559df68badSkre 			num++;
115652967993Skre 		if (strlen(num) > 5 ||
115752967993Skre 		    (fd >= user_fd_limit && fd > max_user_fd))
115852967993Skre 			error("%s: too big to be a file descriptor", num);
11590ff2aa10Skre 
1160168f1d4aSchristos 		if (setflags)
1161168f1d4aSchristos 			setone(fd, pos, neg, verbose);
1162168f1d4aSchristos 		else
11630ff2aa10Skre 			printone(fd, 1, verbose, argc > 1);
1164168f1d4aSchristos 	}
1165b65a5b90Skre 	flushout(out1);
1166b65a5b90Skre 	if (io_err(out1)) {
1167b65a5b90Skre 		out2str("fdflags: I/O error\n");
1168b65a5b90Skre 		return 1;
1169b65a5b90Skre 	}
1170168f1d4aSchristos 	return 0;
1171168f1d4aSchristos }
11723b297678Skre 
11733b297678Skre #undef MAX		/* in case we inherited them from somewhere */
11743b297678Skre #undef MIN
11753b297678Skre 
11763b297678Skre #define	MIN(a,b)	(/*CONSTCOND*/((a)<=(b)) ? (a) : (b))
11773b297678Skre #define	MAX(a,b)	(/*CONSTCOND*/((a)>=(b)) ? (a) : (b))
11783b297678Skre 
11793b297678Skre 		/* now make the compiler work for us... */
11803b297678Skre #define	MIN_REDIR	MIN(MIN(MIN(MIN(NTO,NFROM), MIN(NTOFD,NFROMFD)), \
11813b297678Skre 		   MIN(MIN(NCLOBBER,NAPPEND), MIN(NHERE,NXHERE))), NFROMTO)
11823b297678Skre #define	MAX_REDIR	MAX(MAX(MAX(MAX(NTO,NFROM), MAX(NTOFD,NFROMFD)), \
11833b297678Skre 		   MAX(MAX(NCLOBBER,NAPPEND), MAX(NHERE,NXHERE))), NFROMTO)
11843b297678Skre 
11853b297678Skre static const char *redir_sym[MAX_REDIR - MIN_REDIR + 1] = {
11863b297678Skre 	[NTO      - MIN_REDIR]=	">",
11873b297678Skre 	[NFROM    - MIN_REDIR]=	"<",
11883b297678Skre 	[NTOFD    - MIN_REDIR]=	">&",
11893b297678Skre 	[NFROMFD  - MIN_REDIR]=	"<&",
11903b297678Skre 	[NCLOBBER - MIN_REDIR]=	">|",
11913b297678Skre 	[NAPPEND  - MIN_REDIR]=	">>",
11923b297678Skre 	[NHERE    - MIN_REDIR]=	"<<",
11933b297678Skre 	[NXHERE   - MIN_REDIR]=	"<<",
11943b297678Skre 	[NFROMTO  - MIN_REDIR]=	"<>",
11953b297678Skre };
11963b297678Skre 
11973b297678Skre int
11983b297678Skre outredir(struct output *out, union node *n, int sep)
11993b297678Skre {
12003b297678Skre 	if (n == NULL)
12013b297678Skre 		return 0;
12023b297678Skre 	if (n->type < MIN_REDIR || n->type > MAX_REDIR ||
12033b297678Skre 	    redir_sym[n->type - MIN_REDIR] == NULL)
12043b297678Skre 		return 0;
12053b297678Skre 
12063b297678Skre 	if (sep)
12073b297678Skre 		outc(sep, out);
12083b297678Skre 
12093b297678Skre 	/*
12103b297678Skre 	 * ugly, but all redir node types have "fd" in same slot...
12113b297678Skre 	 *	(and code other places assumes it as well)
12123b297678Skre 	 */
12133b297678Skre 	if ((redir_sym[n->type - MIN_REDIR][0] == '<' && n->nfile.fd != 0) ||
12143b297678Skre 	    (redir_sym[n->type - MIN_REDIR][0] == '>' && n->nfile.fd != 1))
12153b297678Skre 		outfmt(out, "%d", n->nfile.fd);
12163b297678Skre 
12173b297678Skre 	outstr(redir_sym[n->type - MIN_REDIR], out);
12183b297678Skre 
12193b297678Skre 	switch (n->type) {
12203b297678Skre 	case NHERE:
12213b297678Skre 		outstr("'...'", out);
12223b297678Skre 		break;
12233b297678Skre 	case NXHERE:
12243b297678Skre 		outstr("...", out);
12253b297678Skre 		break;
12263b297678Skre 	case NTOFD:
12273b297678Skre 	case NFROMFD:
12283b297678Skre 		if (n->ndup.dupfd < 0)
12293b297678Skre 			outc('-', out);
12303b297678Skre 		else
12313b297678Skre 			outfmt(out, "%d", n->ndup.dupfd);
12323b297678Skre 		break;
12333b297678Skre 	default:
12343b297678Skre 		outstr(n->nfile.expfname, out);
12353b297678Skre 		break;
12363b297678Skre 	}
12373b297678Skre 	return 1;
12383b297678Skre }
1239