xref: /netbsd-src/bin/sh/redir.c (revision c38e7cc395b1472a774ff828e46123de44c628e9)
1 /*	$NetBSD: redir.c,v 1.59 2017/11/15 09:21:48 kre Exp $	*/
2 
3 /*-
4  * Copyright (c) 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Kenneth Almquist.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)redir.c	8.2 (Berkeley) 5/4/95";
39 #else
40 __RCSID("$NetBSD: redir.c,v 1.59 2017/11/15 09:21:48 kre Exp $");
41 #endif
42 #endif /* not lint */
43 
44 #include <sys/types.h>
45 #include <sys/param.h>	/* PIPE_BUF */
46 #include <sys/stat.h>
47 #include <signal.h>
48 #include <string.h>
49 #include <fcntl.h>
50 #include <errno.h>
51 #include <unistd.h>
52 #include <stdlib.h>
53 
54 /*
55  * Code for dealing with input/output redirection.
56  */
57 
58 #include "main.h"
59 #include "builtins.h"
60 #include "shell.h"
61 #include "nodes.h"
62 #include "jobs.h"
63 #include "options.h"
64 #include "expand.h"
65 #include "redir.h"
66 #include "output.h"
67 #include "memalloc.h"
68 #include "mystring.h"
69 #include "error.h"
70 #include "show.h"
71 
72 
73 #define EMPTY -2		/* marks an unused slot in redirtab */
74 #define CLOSED -1		/* fd was not open before redir */
75 #ifndef PIPE_BUF
76 # define PIPESIZE 4096		/* amount of buffering in a pipe */
77 #else
78 # define PIPESIZE PIPE_BUF
79 #endif
80 
81 
82 MKINIT
83 struct renamelist {
84 	struct renamelist *next;
85 	int orig;
86 	int into;
87 };
88 
89 MKINIT
90 struct redirtab {
91 	struct redirtab *next;
92 	struct renamelist *renamed;
93 };
94 
95 
96 MKINIT struct redirtab *redirlist;
97 
98 /*
99  * We keep track of whether or not fd0 has been redirected.  This is for
100  * background commands, where we want to redirect fd0 to /dev/null only
101  * if it hasn't already been redirected.
102  */
103 STATIC int fd0_redirected = 0;
104 
105 /*
106  * And also where to put internal use fds that should be out of the
107  * way of user defined fds (normally)
108  */
109 STATIC int big_sh_fd = 0;
110 
111 STATIC const struct renamelist *is_renamed(const struct renamelist *, int);
112 STATIC void fd_rename(struct redirtab *, int, int);
113 STATIC void free_rl(struct redirtab *, int);
114 STATIC void openredirect(union node *, char[10], int);
115 STATIC int openhere(const union node *);
116 STATIC int copyfd(int, int, int);
117 STATIC void find_big_fd(void);
118 
119 
120 struct shell_fds {		/* keep track of internal shell fds */
121 	struct shell_fds *nxt;
122 	void (*cb)(int, int);
123 	int fd;
124 };
125 
126 STATIC struct shell_fds *sh_fd_list;
127 
128 STATIC void renumber_sh_fd(struct shell_fds *);
129 STATIC struct shell_fds *sh_fd(int);
130 
131 STATIC const struct renamelist *
132 is_renamed(const struct renamelist *rl, int fd)
133 {
134 	while (rl != NULL) {
135 		if (rl->orig == fd)
136 			return rl;
137 		rl = rl->next;
138 	}
139 	return NULL;
140 }
141 
142 STATIC void
143 free_rl(struct redirtab *rt, int reset)
144 {
145 	struct renamelist *rl, *rn = rt->renamed;
146 
147 	while ((rl = rn) != NULL) {
148 		rn = rl->next;
149 		if (rl->orig == 0)
150 			fd0_redirected--;
151 		if (reset) {
152 			if (rl->into < 0)
153 				close(rl->orig);
154 			else
155 				movefd(rl->into, rl->orig);
156 		}
157 		ckfree(rl);
158 	}
159 	rt->renamed = NULL;
160 }
161 
162 STATIC void
163 fd_rename(struct redirtab *rt, int from, int to)
164 {
165 	struct renamelist *rl = ckmalloc(sizeof(struct renamelist));
166 
167 	rl->next = rt->renamed;
168 	rt->renamed = rl;
169 
170 	rl->orig = from;
171 	rl->into = to;
172 }
173 
174 /*
175  * Process a list of redirection commands.  If the REDIR_PUSH flag is set,
176  * old file descriptors are stashed away so that the redirection can be
177  * undone by calling popredir.  If the REDIR_BACKQ flag is set, then the
178  * standard output, and the standard error if it becomes a duplicate of
179  * stdout, is saved in memory.
180  */
181 
182 void
183 redirect(union node *redir, int flags)
184 {
185 	union node *n;
186 	struct redirtab *sv = NULL;
187 	int i;
188 	int fd;
189 	char memory[10];	/* file descriptors to write to memory */
190 
191 	for (i = 10 ; --i >= 0 ; )
192 		memory[i] = 0;
193 	memory[1] = flags & REDIR_BACKQ;
194 	if (flags & REDIR_PUSH) {
195 		/* We don't have to worry about REDIR_VFORK here, as
196 		 * flags & REDIR_PUSH is never true if REDIR_VFORK is set.
197 		 */
198 		sv = ckmalloc(sizeof (struct redirtab));
199 		sv->renamed = NULL;
200 		sv->next = redirlist;
201 		redirlist = sv;
202 	}
203 	for (n = redir ; n ; n = n->nfile.next) {
204 		fd = n->nfile.fd;
205 		if (fd > max_user_fd)
206 			max_user_fd = fd;
207 		renumber_sh_fd(sh_fd(fd));
208 		if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
209 		    n->ndup.dupfd == fd) {
210 			/* redirect from/to same file descriptor */
211 			/* make sure it stays open */
212 			if (fcntl(fd, F_SETFD, 0) < 0)
213 				error("fd %d: %s", fd, strerror(errno));
214 			continue;
215 		}
216 
217 		if ((flags & REDIR_PUSH) && !is_renamed(sv->renamed, fd)) {
218 			INTOFF;
219 			if (big_sh_fd < 10)
220 				find_big_fd();
221 			if ((i = fcntl(fd, F_DUPFD, big_sh_fd)) == -1) {
222 				switch (errno) {
223 				case EBADF:
224 					i = CLOSED;
225 					break;
226 				case EMFILE:
227 				case EINVAL:
228 					find_big_fd();
229 					i = fcntl(fd, F_DUPFD, big_sh_fd);
230 					if (i >= 0)
231 						break;
232 					/* FALLTHRU */
233 				default:
234 					i = errno;
235 					INTON;    /* XXX not needed here ? */
236 					error("%d: %s", fd, strerror(i));
237 					/* NOTREACHED */
238 				}
239 			}
240 			if (i >= 0)
241 				(void)fcntl(i, F_SETFD, FD_CLOEXEC);
242 			fd_rename(sv, fd, i);
243 			INTON;
244 		}
245 		if (fd == 0)
246 			fd0_redirected++;
247 		openredirect(n, memory, flags);
248 	}
249 	if (memory[1])
250 		out1 = &memout;
251 	if (memory[2])
252 		out2 = &memout;
253 }
254 
255 
256 STATIC void
257 openredirect(union node *redir, char memory[10], int flags)
258 {
259 	struct stat sb;
260 	int fd = redir->nfile.fd;
261 	char *fname;
262 	int f;
263 	int eflags, cloexec;
264 
265 	/*
266 	 * We suppress interrupts so that we won't leave open file
267 	 * descriptors around.  This may not be such a good idea because
268 	 * an open of a device or a fifo can block indefinitely.
269 	 */
270 	INTOFF;
271 	if (fd < 10)
272 		memory[fd] = 0;
273 	switch (redir->nfile.type) {
274 	case NFROM:
275 		fname = redir->nfile.expfname;
276 		if (flags & REDIR_VFORK)
277 			eflags = O_NONBLOCK;
278 		else
279 			eflags = 0;
280 		if ((f = open(fname, O_RDONLY|eflags)) < 0)
281 			goto eopen;
282 		VTRACE(DBG_REDIR, ("openredirect(< '%s') -> %d [%#x]",
283 		    fname, f, eflags));
284 		if (eflags)
285 			(void)fcntl(f, F_SETFL, fcntl(f, F_GETFL, 0) & ~eflags);
286 		break;
287 	case NFROMTO:
288 		fname = redir->nfile.expfname;
289 		if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
290 			goto ecreate;
291 		VTRACE(DBG_REDIR, ("openredirect(<> '%s') -> %d", fname, f));
292 		break;
293 	case NTO:
294 		if (Cflag) {
295 			fname = redir->nfile.expfname;
296 			if ((f = open(fname, O_WRONLY)) == -1) {
297 				if ((f = open(fname, O_WRONLY|O_CREAT|O_EXCL,
298 				    0666)) < 0)
299 					goto ecreate;
300 			} else if (fstat(f, &sb) == -1) {
301 				int serrno = errno;
302 				close(f);
303 				errno = serrno;
304 				goto ecreate;
305 			} else if (S_ISREG(sb.st_mode)) {
306 				close(f);
307 				errno = EEXIST;
308 				goto ecreate;
309 			}
310 			break;
311 		}
312 		/* FALLTHROUGH */
313 	case NCLOBBER:
314 		fname = redir->nfile.expfname;
315 		if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
316 			goto ecreate;
317 		VTRACE(DBG_REDIR, ("openredirect(> '%s') -> %d", fname, f));
318 		break;
319 	case NAPPEND:
320 		fname = redir->nfile.expfname;
321 		if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
322 			goto ecreate;
323 		VTRACE(DBG_REDIR, ("openredirect(>> '%s') -> %d", fname, f));
324 		break;
325 	case NTOFD:
326 	case NFROMFD:
327 		if (redir->ndup.dupfd >= 0) {	/* if not ">&-" */
328 			if (fd < 10 && redir->ndup.dupfd < 10 &&
329 			    memory[redir->ndup.dupfd])
330 				memory[fd] = 1;
331 			else if (copyfd(redir->ndup.dupfd, fd,
332 			    (flags & REDIR_KEEP) == 0) < 0)
333 				error("Redirect (from %d to %d) failed: %s",
334 				    redir->ndup.dupfd, fd, strerror(errno));
335 			VTRACE(DBG_REDIR, ("openredirect: %d%c&%d\n", fd,
336 			    "<>"[redir->nfile.type==NTOFD], redir->ndup.dupfd));
337 		} else {
338 			(void) close(fd);
339 			VTRACE(DBG_REDIR, ("openredirect: %d%c&-\n", fd,
340 			    "<>"[redir->nfile.type==NTOFD]));
341 		}
342 		INTON;
343 		return;
344 	case NHERE:
345 	case NXHERE:
346 		f = openhere(redir);
347 		VTRACE(DBG_REDIR, ("openredirect: %d<<...", fd));
348 		break;
349 	default:
350 		abort();
351 	}
352 
353 	cloexec = fd > 2 && (flags & REDIR_KEEP) == 0 && !posix;
354 	if (f != fd) {
355 		VTRACE(DBG_REDIR, (" -> %d", fd));
356 		if (copyfd(f, fd, cloexec) < 0) {
357 			int e = errno;
358 
359 			close(f);
360 			error("redirect reassignment (fd %d) failed: %s", fd,
361 			    strerror(e));
362 		}
363 		close(f);
364 	} else if (cloexec)
365 		(void)fcntl(f, F_SETFD, FD_CLOEXEC);
366 	VTRACE(DBG_REDIR, ("%s\n", cloexec ? " cloexec" : ""));
367 
368 	INTON;
369 	return;
370 ecreate:
371 	exerrno = 1;
372 	error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
373 eopen:
374 	exerrno = 1;
375 	error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
376 }
377 
378 
379 /*
380  * Handle here documents.  Normally we fork off a process to write the
381  * data to a pipe.  If the document is short, we can stuff the data in
382  * the pipe without forking.
383  */
384 
385 STATIC int
386 openhere(const union node *redir)
387 {
388 	int pip[2];
389 	int len = 0;
390 
391 	if (pipe(pip) < 0)
392 		error("Pipe call failed");
393 	if (redir->type == NHERE) {
394 		len = strlen(redir->nhere.doc->narg.text);
395 		if (len <= PIPESIZE) {
396 			xwrite(pip[1], redir->nhere.doc->narg.text, len);
397 			goto out;
398 		}
399 	}
400 	if (forkshell(NULL, NULL, FORK_NOJOB) == 0) {
401 		close(pip[0]);
402 		signal(SIGINT, SIG_IGN);
403 		signal(SIGQUIT, SIG_IGN);
404 		signal(SIGHUP, SIG_IGN);
405 #ifdef SIGTSTP
406 		signal(SIGTSTP, SIG_IGN);
407 #endif
408 		signal(SIGPIPE, SIG_DFL);
409 		if (redir->type == NHERE)
410 			xwrite(pip[1], redir->nhere.doc->narg.text, len);
411 		else
412 			expandhere(redir->nhere.doc, pip[1]);
413 		_exit(0);
414 	}
415 out:
416 	close(pip[1]);
417 	return pip[0];
418 }
419 
420 
421 
422 /*
423  * Undo the effects of the last redirection.
424  */
425 
426 void
427 popredir(void)
428 {
429 	struct redirtab *rp = redirlist;
430 
431 	INTOFF;
432 	free_rl(rp, 1);
433 	redirlist = rp->next;
434 	ckfree(rp);
435 	INTON;
436 }
437 
438 /*
439  * Undo all redirections.  Called on error or interrupt.
440  */
441 
442 #ifdef mkinit
443 
444 INCLUDE "redir.h"
445 
446 RESET {
447 	while (redirlist)
448 		popredir();
449 }
450 
451 SHELLPROC {
452 	clearredir(0);
453 }
454 
455 #endif
456 
457 /* Return true if fd 0 has already been redirected at least once.  */
458 int
459 fd0_redirected_p(void)
460 {
461 	return fd0_redirected != 0;
462 }
463 
464 /*
465  * Discard all saved file descriptors.
466  */
467 
468 void
469 clearredir(int vforked)
470 {
471 	struct redirtab *rp;
472 	struct renamelist *rl;
473 
474 	for (rp = redirlist ; rp ; rp = rp->next) {
475 		if (!vforked)
476 			free_rl(rp, 0);
477 		else for (rl = rp->renamed; rl; rl = rl->next)
478 			if (rl->into >= 0)
479 				close(rl->into);
480 	}
481 }
482 
483 
484 
485 /*
486  * Copy a file descriptor to be == to.
487  * cloexec indicates if we want close-on-exec or not.
488  * Returns -1 if any error occurs.
489  */
490 
491 STATIC int
492 copyfd(int from, int to, int cloexec)
493 {
494 	int newfd;
495 
496 	if (cloexec && to > 2)
497 		newfd = dup3(from, to, O_CLOEXEC);
498 	else
499 		newfd = dup2(from, to);
500 
501 	return newfd;
502 }
503 
504 /*
505  * rename fd from to be fd to (closing from).
506  * close-on-exec is never set on 'to' (unless
507  * from==to and it was set on from) - ie: a no-op
508  * returns to (or errors() if an error occurs).
509  *
510  * This is mostly used for rearranging the
511  * results from pipe().
512  */
513 int
514 movefd(int from, int to)
515 {
516 	if (from == to)
517 		return to;
518 
519 	(void) close(to);
520 	if (copyfd(from, to, 0) != to) {
521 		int e = errno;
522 
523 		(void) close(from);
524 		error("Unable to make fd %d: %s", to, strerror(e));
525 	}
526 	(void) close(from);
527 
528 	return to;
529 }
530 
531 STATIC void
532 find_big_fd(void)
533 {
534 	int i, fd;
535 	static int last_start = 3; /* aim to keep sh fd's under 20 */
536 
537 	if (last_start < 10)
538 		last_start++;
539 
540 	for (i = (1 << last_start); i >= 10; i >>= 1) {
541 		if ((fd = fcntl(0, F_DUPFD, i - 1)) >= 0) {
542 			close(fd);
543 			break;
544 		}
545 	}
546 
547 	fd = (i / 5) * 4;
548 	if (fd < 10)
549 		fd = 10;
550 
551 	big_sh_fd = fd;
552 }
553 
554 /*
555  * If possible, move file descriptor fd out of the way
556  * of expected user fd values.   Returns the new fd
557  * (which may be the input fd if things do not go well.)
558  * Always set close-on-exec on the result, and close
559  * the input fd unless it is to be our result.
560  */
561 int
562 to_upper_fd(int fd)
563 {
564 	int i;
565 
566 	VTRACE(DBG_REDIR|DBG_OUTPUT, ("to_upper_fd(%d)", fd));
567 	if (big_sh_fd < 10)
568 		find_big_fd();
569 	do {
570 		i = fcntl(fd, F_DUPFD_CLOEXEC, big_sh_fd);
571 		if (i >= 0) {
572 			if (fd != i)
573 				close(fd);
574 			VTRACE(DBG_REDIR|DBG_OUTPUT, ("-> %d\n", i));
575 			return i;
576 		}
577 		if (errno != EMFILE && errno != EINVAL)
578 			break;
579 		find_big_fd();
580 	} while (big_sh_fd > 10);
581 
582 	/*
583 	 * If we wanted to move this fd to some random high number
584 	 * we certainly do not intend to pass it through exec, even
585 	 * if the reassignment failed.
586 	 */
587 	(void)fcntl(fd, F_SETFD, FD_CLOEXEC);
588 	VTRACE(DBG_REDIR|DBG_OUTPUT, (" fails ->%d\n", fd));
589 	return fd;
590 }
591 
592 void
593 register_sh_fd(int fd, void (*cb)(int, int))
594 {
595 	struct shell_fds *fp;
596 
597 	fp = ckmalloc(sizeof (struct shell_fds));
598 	if (fp != NULL) {
599 		fp->nxt = sh_fd_list;
600 		sh_fd_list = fp;
601 
602 		fp->fd = fd;
603 		fp->cb = cb;
604 	}
605 }
606 
607 void
608 sh_close(int fd)
609 {
610 	struct shell_fds **fpp, *fp;
611 
612 	fpp = &sh_fd_list;
613 	while ((fp = *fpp) != NULL) {
614 		if (fp->fd == fd) {
615 			*fpp = fp->nxt;
616 			ckfree(fp);
617 			break;
618 		}
619 		fpp = &fp->nxt;
620 	}
621 	(void)close(fd);
622 }
623 
624 STATIC struct shell_fds *
625 sh_fd(int fd)
626 {
627 	struct shell_fds *fp;
628 
629 	for (fp = sh_fd_list; fp != NULL; fp = fp->nxt)
630 		if (fp->fd == fd)
631 			return fp;
632 	return NULL;
633 }
634 
635 STATIC void
636 renumber_sh_fd(struct shell_fds *fp)
637 {
638 	int to;
639 
640 	if (fp == NULL)
641 		return;
642 
643 #ifndef	F_DUPFD_CLOEXEC
644 #define	F_DUPFD_CLOEXEC	F_DUPFD
645 #define	CLOEXEC(fd)	(fcntl((fd), F_SETFD, fcntl((fd),F_GETFD) | FD_CLOEXEC))
646 #else
647 #define	CLOEXEC(fd)
648 #endif
649 
650 	/*
651 	 * if we have had a collision, and the sh fd was a "big" one
652 	 * try moving the sh fd base to a higher number (if possible)
653 	 * so future sh fds are less likely to be in the user's sights
654 	 * (incl this one when moved)
655 	 */
656 	if (fp->fd >= big_sh_fd)
657 		find_big_fd();
658 
659 	to = fcntl(fp->fd, F_DUPFD_CLOEXEC, big_sh_fd);
660 	if (to == -1)
661 		to = fcntl(fp->fd, F_DUPFD_CLOEXEC, big_sh_fd/2);
662 	if (to == -1)
663 		to = fcntl(fp->fd, F_DUPFD_CLOEXEC, fp->fd + 1);
664 	if (to == -1)
665 		to = fcntl(fp->fd, F_DUPFD_CLOEXEC, 10);
666 	if (to == -1)
667 		to = fcntl(fp->fd, F_DUPFD_CLOEXEC, 3);
668 	if (to == -1)
669 		error("insufficient file descriptors available");
670 	CLOEXEC(to);
671 
672 	if (fp->fd == to)	/* impossible? */
673 		return;
674 
675 	(*fp->cb)(fp->fd, to);
676 	(void)close(fp->fd);
677 	fp->fd = to;
678 }
679 
680 static const struct flgnames {
681 	const char *name;
682 	uint16_t minch;
683 	uint32_t value;
684 } nv[] = {
685 #ifdef O_APPEND
686 	{ "append",	2,	O_APPEND 	},
687 #endif
688 #ifdef O_ASYNC
689 	{ "async",	2,	O_ASYNC		},
690 #endif
691 #ifdef O_SYNC
692 	{ "sync",	2,	O_SYNC		},
693 #endif
694 #ifdef O_NONBLOCK
695 	{ "nonblock",	3,	O_NONBLOCK	},
696 #endif
697 #ifdef O_FSYNC
698 	{ "fsync",	2,	O_FSYNC		},
699 #endif
700 #ifdef O_DSYNC
701 	{ "dsync",	2,	O_DSYNC		},
702 #endif
703 #ifdef O_RSYNC
704 	{ "rsync",	2,	O_RSYNC		},
705 #endif
706 #ifdef O_ALTIO
707 	{ "altio",	2,	O_ALT_IO	},
708 #endif
709 #ifdef O_DIRECT
710 	{ "direct",	2,	O_DIRECT	},
711 #endif
712 #ifdef O_NOSIGPIPE
713 	{ "nosigpipe",	3,	O_NOSIGPIPE	},
714 #endif
715 #ifdef O_CLOEXEC
716 	{ "cloexec",	2,	O_CLOEXEC	},
717 #endif
718 	{ 0, 0, 0 }
719 };
720 #define ALLFLAGS (O_APPEND|O_ASYNC|O_SYNC|O_NONBLOCK|O_DSYNC|O_RSYNC|\
721     O_ALT_IO|O_DIRECT|O_NOSIGPIPE|O_CLOEXEC)
722 
723 static int
724 getflags(int fd, int p)
725 {
726 	int c, f;
727 
728 	if (sh_fd(fd) != NULL) {
729 		if (!p)
730 			return -1;
731 		error("Can't get status for fd=%d (%s)", fd,
732 		    "Bad file descriptor");			/*XXX*/
733 	}
734 
735 	if ((c = fcntl(fd, F_GETFD)) == -1) {
736 		if (!p)
737 			return -1;
738 		error("Can't get status for fd=%d (%s)", fd, strerror(errno));
739 	}
740 	if ((f = fcntl(fd, F_GETFL)) == -1) {
741 		if (!p)
742 			return -1;
743 		error("Can't get flags for fd=%d (%s)", fd, strerror(errno));
744 	}
745 	if (c & FD_CLOEXEC)
746 		f |= O_CLOEXEC;
747 	return f & ALLFLAGS;
748 }
749 
750 static void
751 printone(int fd, int p, int verbose, int pfd)
752 {
753 	int f = getflags(fd, p);
754 	const struct flgnames *fn;
755 
756 	if (f == -1)
757 		return;
758 
759 	if (pfd)
760 		outfmt(out1, "%d: ", fd);
761 	for (fn = nv; fn->name; fn++) {
762 		if (f & fn->value) {
763 			outfmt(out1, "%s%s", verbose ? "+" : "", fn->name);
764 			f &= ~fn->value;
765 		} else if (verbose)
766 			outfmt(out1, "-%s", fn->name);
767 		else
768 			continue;
769 		if (f || (verbose && fn[1].name))
770 			outfmt(out1, ",");
771 	}
772 	if (verbose && f)		/* f should be normally be 0 */
773 		outfmt(out1, " +%#x", f);
774 	outfmt(out1, "\n");
775 }
776 
777 static void
778 parseflags(char *s, int *p, int *n)
779 {
780 	int *v, *w;
781 	const struct flgnames *fn;
782 	size_t len;
783 
784 	*p = 0;
785 	*n = 0;
786 	for (s = strtok(s, ","); s; s = strtok(NULL, ",")) {
787 		switch (*s++) {
788 		case '+':
789 			v = p;
790 			w = n;
791 			break;
792 		case '-':
793 			v = n;
794 			w = p;
795 			break;
796 		default:
797 			error("Missing +/- indicator before flag %s", s-1);
798 		}
799 
800 		len = strlen(s);
801 		for (fn = nv; fn->name; fn++)
802 			if (len >= fn->minch && strncmp(s,fn->name,len) == 0) {
803 				*v |= fn->value;
804 				*w &=~ fn->value;
805 				break;
806 			}
807 		if (fn->name == 0)
808 			error("Bad flag `%s'", s);
809 	}
810 }
811 
812 static void
813 setone(int fd, int pos, int neg, int verbose)
814 {
815 	int f = getflags(fd, 1);
816 	int n, cloexec;
817 
818 	if (f == -1)
819 		return;
820 
821 	cloexec = -1;
822 	if ((pos & O_CLOEXEC) && !(f & O_CLOEXEC))
823 		cloexec = FD_CLOEXEC;
824 	if ((neg & O_CLOEXEC) && (f & O_CLOEXEC))
825 		cloexec = 0;
826 
827 	if (cloexec != -1 && fcntl(fd, F_SETFD, cloexec) == -1)
828 		error("Can't set status for fd=%d (%s)", fd, strerror(errno));
829 
830 	pos &= ~O_CLOEXEC;
831 	neg &= ~O_CLOEXEC;
832 	f &= ~O_CLOEXEC;
833 	n = f;
834 	n |= pos;
835 	n &= ~neg;
836 	if (n != f && fcntl(fd, F_SETFL, n) == -1)
837 		error("Can't set flags for fd=%d (%s)", fd, strerror(errno));
838 	if (verbose)
839 		printone(fd, 1, verbose, 1);
840 }
841 
842 int
843 fdflagscmd(int argc, char *argv[])
844 {
845 	char *num;
846 	int verbose = 0, ch, pos = 0, neg = 0;
847 	char *setflags = NULL;
848 
849 	optreset = 1; optind = 1; /* initialize getopt */
850 	while ((ch = getopt(argc, argv, ":vs:")) != -1)
851 		switch ((char)ch) {
852 		case 'v':
853 			verbose = 1;
854 			break;
855 		case 's':
856 			if (setflags)
857 				goto msg;
858 			setflags = optarg;
859 			break;
860 		case '?':
861 		default:
862 		msg:
863 			error("Usage: fdflags [-v] [-s <flags> fd] [fd...]");
864 			/* NOTREACHED */
865 		}
866 
867 	argc -= optind, argv += optind;
868 
869 	if (setflags)
870 		parseflags(setflags, &pos, &neg);
871 
872 	if (argc == 0) {
873 		int i;
874 
875 		if (setflags)
876 			goto msg;
877 
878 		for (i = 0; i <= max_user_fd; i++)
879 			printone(i, 0, verbose, 1);
880 		return 0;
881 	}
882 
883 	while ((num = *argv++) != NULL) {
884 		int fd = number(num);
885 
886 		while (num[0] == '0' && num[1] != '\0')		/* skip 0's */
887 			num++;
888 		if (strlen(num) > 5)
889 			error("%s too big to be a file descriptor", num);
890 
891 		if (setflags)
892 			setone(fd, pos, neg, verbose);
893 		else
894 			printone(fd, 1, verbose, argc > 1);
895 	}
896 	return 0;
897 }
898 
899 #undef MAX		/* in case we inherited them from somewhere */
900 #undef MIN
901 
902 #define	MIN(a,b)	(/*CONSTCOND*/((a)<=(b)) ? (a) : (b))
903 #define	MAX(a,b)	(/*CONSTCOND*/((a)>=(b)) ? (a) : (b))
904 
905 		/* now make the compiler work for us... */
906 #define	MIN_REDIR	MIN(MIN(MIN(MIN(NTO,NFROM), MIN(NTOFD,NFROMFD)), \
907 		   MIN(MIN(NCLOBBER,NAPPEND), MIN(NHERE,NXHERE))), NFROMTO)
908 #define	MAX_REDIR	MAX(MAX(MAX(MAX(NTO,NFROM), MAX(NTOFD,NFROMFD)), \
909 		   MAX(MAX(NCLOBBER,NAPPEND), MAX(NHERE,NXHERE))), NFROMTO)
910 
911 static const char *redir_sym[MAX_REDIR - MIN_REDIR + 1] = {
912 	[NTO      - MIN_REDIR]=	">",
913 	[NFROM    - MIN_REDIR]=	"<",
914 	[NTOFD    - MIN_REDIR]=	">&",
915 	[NFROMFD  - MIN_REDIR]=	"<&",
916 	[NCLOBBER - MIN_REDIR]=	">|",
917 	[NAPPEND  - MIN_REDIR]=	">>",
918 	[NHERE    - MIN_REDIR]=	"<<",
919 	[NXHERE   - MIN_REDIR]=	"<<",
920 	[NFROMTO  - MIN_REDIR]=	"<>",
921 };
922 
923 int
924 outredir(struct output *out, union node *n, int sep)
925 {
926 	if (n == NULL)
927 		return 0;
928 	if (n->type < MIN_REDIR || n->type > MAX_REDIR ||
929 	    redir_sym[n->type - MIN_REDIR] == NULL)
930 		return 0;
931 
932 	if (sep)
933 		outc(sep, out);
934 
935 	/*
936 	 * ugly, but all redir node types have "fd" in same slot...
937 	 *	(and code other places assumes it as well)
938 	 */
939 	if ((redir_sym[n->type - MIN_REDIR][0] == '<' && n->nfile.fd != 0) ||
940 	    (redir_sym[n->type - MIN_REDIR][0] == '>' && n->nfile.fd != 1))
941 		outfmt(out, "%d", n->nfile.fd);
942 
943 	outstr(redir_sym[n->type - MIN_REDIR], out);
944 
945 	switch (n->type) {
946 	case NHERE:
947 		outstr("'...'", out);
948 		break;
949 	case NXHERE:
950 		outstr("...", out);
951 		break;
952 	case NTOFD:
953 	case NFROMFD:
954 		if (n->ndup.dupfd < 0)
955 			outc('-', out);
956 		else
957 			outfmt(out, "%d", n->ndup.dupfd);
958 		break;
959 	default:
960 		outstr(n->nfile.expfname, out);
961 		break;
962 	}
963 	return 1;
964 }
965