redir.c revision 1.62 1 /* $NetBSD: redir.c,v 1.62 2018/11/26 20:03:39 kamil 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.62 2018/11/26 20:03:39 kamil 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 VTRACE(DBG_REDIR, ("popredir %d%s: %s",
152 rl->orig, rl->orig==0 ? " (STDIN)" : "",
153 reset ? "" : "no reset\n"));
154 if (reset) {
155 if (rl->into < 0) {
156 VTRACE(DBG_REDIR, ("closed\n"));
157 close(rl->orig);
158 } else {
159 VTRACE(DBG_REDIR, ("from %d\n", rl->into));
160 movefd(rl->into, rl->orig);
161 }
162 }
163 ckfree(rl);
164 }
165 rt->renamed = NULL;
166 }
167
168 STATIC void
169 fd_rename(struct redirtab *rt, int from, int to)
170 {
171 /* XXX someday keep a short list (8..10) of freed renamelists XXX */
172 struct renamelist *rl = ckmalloc(sizeof(struct renamelist));
173
174 rl->next = rt->renamed;
175 rt->renamed = rl;
176
177 rl->orig = from;
178 rl->into = to;
179 }
180
181 /*
182 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
183 * old file descriptors are stashed away so that the redirection can be
184 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
185 * standard output, and the standard error if it becomes a duplicate of
186 * stdout, is saved in memory.
187 */
188
189 void
190 redirect(union node *redir, int flags)
191 {
192 union node *n;
193 struct redirtab *sv = NULL;
194 int i;
195 int fd;
196 char memory[10]; /* file descriptors to write to memory */
197
198 CTRACE(DBG_REDIR, ("redirect(F=0x%x):%s\n", flags, redir?"":" NONE"));
199 for (i = 10 ; --i >= 0 ; )
200 memory[i] = 0;
201 memory[1] = flags & REDIR_BACKQ;
202 if (flags & REDIR_PUSH) {
203 /*
204 * We don't have to worry about REDIR_VFORK here, as
205 * flags & REDIR_PUSH is never true if REDIR_VFORK is set.
206 */
207 sv = ckmalloc(sizeof (struct redirtab));
208 sv->renamed = NULL;
209 sv->next = redirlist;
210 redirlist = sv;
211 }
212 for (n = redir ; n ; n = n->nfile.next) {
213 fd = n->nfile.fd;
214 VTRACE(DBG_REDIR, ("redir %d (max=%d) ", fd, max_user_fd));
215 if (fd > max_user_fd)
216 max_user_fd = fd;
217 renumber_sh_fd(sh_fd(fd));
218 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
219 n->ndup.dupfd == fd) {
220 /* redirect from/to same file descriptor */
221 /* make sure it stays open */
222 if (fcntl(fd, F_SETFD, 0) < 0)
223 error("fd %d: %s", fd, strerror(errno));
224 VTRACE(DBG_REDIR, ("!cloexec\n"));
225 continue;
226 }
227
228 if ((flags & REDIR_PUSH) && !is_renamed(sv->renamed, fd)) {
229 INTOFF;
230 if (big_sh_fd < 10)
231 find_big_fd();
232 if ((i = fcntl(fd, F_DUPFD, big_sh_fd)) == -1) {
233 switch (errno) {
234 case EBADF:
235 i = CLOSED;
236 break;
237 case EMFILE:
238 case EINVAL:
239 find_big_fd();
240 i = fcntl(fd, F_DUPFD, big_sh_fd);
241 if (i >= 0)
242 break;
243 /* FALLTHRU */
244 default:
245 i = errno;
246 INTON; /* XXX not needed here ? */
247 error("%d: %s", fd, strerror(i));
248 /* NOTREACHED */
249 }
250 }
251 if (i >= 0)
252 (void)fcntl(i, F_SETFD, FD_CLOEXEC);
253 fd_rename(sv, fd, i);
254 VTRACE(DBG_REDIR, ("saved as %d ", i));
255 INTON;
256 }
257 VTRACE(DBG_REDIR, ("%s\n", fd == 0 ? "STDIN" : ""));
258 if (fd == 0)
259 fd0_redirected++;
260 openredirect(n, memory, flags);
261 }
262 if (memory[1])
263 out1 = &memout;
264 if (memory[2])
265 out2 = &memout;
266 }
267
268
269 STATIC void
270 openredirect(union node *redir, char memory[10], int flags)
271 {
272 struct stat sb;
273 int fd = redir->nfile.fd;
274 char *fname;
275 int f;
276 int eflags, cloexec;
277
278 /*
279 * We suppress interrupts so that we won't leave open file
280 * descriptors around. This may not be such a good idea because
281 * an open of a device or a fifo can block indefinitely.
282 */
283 INTOFF;
284 if (fd < 10)
285 memory[fd] = 0;
286 switch (redir->nfile.type) {
287 case NFROM:
288 fname = redir->nfile.expfname;
289 if (flags & REDIR_VFORK)
290 eflags = O_NONBLOCK;
291 else
292 eflags = 0;
293 if ((f = open(fname, O_RDONLY|eflags)) < 0)
294 goto eopen;
295 VTRACE(DBG_REDIR, ("openredirect(< '%s') -> %d [%#x]",
296 fname, f, eflags));
297 if (eflags)
298 (void)fcntl(f, F_SETFL, fcntl(f, F_GETFL, 0) & ~eflags);
299 break;
300 case NFROMTO:
301 fname = redir->nfile.expfname;
302 if ((f = open(fname, O_RDWR|O_CREAT, 0666)) < 0)
303 goto ecreate;
304 VTRACE(DBG_REDIR, ("openredirect(<> '%s') -> %d", fname, f));
305 break;
306 case NTO:
307 if (Cflag) {
308 fname = redir->nfile.expfname;
309 if ((f = open(fname, O_WRONLY)) == -1) {
310 if ((f = open(fname, O_WRONLY|O_CREAT|O_EXCL,
311 0666)) < 0)
312 goto ecreate;
313 } else if (fstat(f, &sb) == -1) {
314 int serrno = errno;
315 close(f);
316 errno = serrno;
317 goto ecreate;
318 } else if (S_ISREG(sb.st_mode)) {
319 close(f);
320 errno = EEXIST;
321 goto ecreate;
322 }
323 VTRACE(DBG_REDIR, ("openredirect(>| '%s') -> %d",
324 fname, f));
325 break;
326 }
327 /* FALLTHROUGH */
328 case NCLOBBER:
329 fname = redir->nfile.expfname;
330 if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
331 goto ecreate;
332 VTRACE(DBG_REDIR, ("openredirect(> '%s') -> %d", fname, f));
333 break;
334 case NAPPEND:
335 fname = redir->nfile.expfname;
336 if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
337 goto ecreate;
338 VTRACE(DBG_REDIR, ("openredirect(>> '%s') -> %d", fname, f));
339 break;
340 case NTOFD:
341 case NFROMFD:
342 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
343 if (fd < 10 && redir->ndup.dupfd < 10 &&
344 memory[redir->ndup.dupfd])
345 memory[fd] = 1;
346 else if (copyfd(redir->ndup.dupfd, fd,
347 (flags & REDIR_KEEP) == 0) < 0)
348 error("Redirect (from %d to %d) failed: %s",
349 redir->ndup.dupfd, fd, strerror(errno));
350 VTRACE(DBG_REDIR, ("openredirect: %d%c&%d\n", fd,
351 "<>"[redir->nfile.type==NTOFD], redir->ndup.dupfd));
352 } else {
353 (void) close(fd);
354 VTRACE(DBG_REDIR, ("openredirect: %d%c&-\n", fd,
355 "<>"[redir->nfile.type==NTOFD]));
356 }
357 INTON;
358 return;
359 case NHERE:
360 case NXHERE:
361 VTRACE(DBG_REDIR, ("openredirect: %d<<...", fd));
362 f = openhere(redir);
363 break;
364 default:
365 abort();
366 }
367
368 cloexec = fd > 2 && (flags & REDIR_KEEP) == 0 && !posix;
369 if (f != fd) {
370 VTRACE(DBG_REDIR, (" -> %d", fd));
371 if (copyfd(f, fd, cloexec) < 0) {
372 int e = errno;
373
374 close(f);
375 error("redirect reassignment (fd %d) failed: %s", fd,
376 strerror(e));
377 }
378 close(f);
379 } else if (cloexec)
380 (void)fcntl(f, F_SETFD, FD_CLOEXEC);
381 VTRACE(DBG_REDIR, ("%s\n", cloexec ? " cloexec" : ""));
382
383 INTON;
384 return;
385 ecreate:
386 exerrno = 1;
387 error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
388 eopen:
389 exerrno = 1;
390 error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
391 }
392
393
394 /*
395 * Handle here documents. Normally we fork off a process to write the
396 * data to a pipe. If the document is short, we can stuff the data in
397 * the pipe without forking.
398 */
399
400 STATIC int
401 openhere(const union node *redir)
402 {
403 int pip[2];
404 int len = 0;
405
406 if (pipe(pip) < 0)
407 error("Pipe call failed");
408 if (redir->type == NHERE) {
409 len = strlen(redir->nhere.doc->narg.text);
410 if (len <= PIPESIZE) {
411 xwrite(pip[1], redir->nhere.doc->narg.text, len);
412 goto out;
413 }
414 }
415 VTRACE(DBG_REDIR, (" forking [%d,%d]\n", pip[0], pip[1]));
416 if (forkshell(NULL, NULL, FORK_NOJOB) == 0) {
417 close(pip[0]);
418 signal(SIGINT, SIG_IGN);
419 signal(SIGQUIT, SIG_IGN);
420 signal(SIGHUP, SIG_IGN);
421 #ifdef SIGTSTP
422 signal(SIGTSTP, SIG_IGN);
423 #endif
424 signal(SIGPIPE, SIG_DFL);
425 if (redir->type == NHERE)
426 xwrite(pip[1], redir->nhere.doc->narg.text, len);
427 else
428 expandhere(redir->nhere.doc, pip[1]);
429 _exit(0);
430 }
431 VTRACE(DBG_REDIR, ("openhere (closing %d)", pip[1]));
432 out:
433 close(pip[1]);
434 VTRACE(DBG_REDIR, (" (pipe fd=%d)", pip[0]));
435 return pip[0];
436 }
437
438
439
440 /*
441 * Undo the effects of the last redirection.
442 */
443
444 void
445 popredir(void)
446 {
447 struct redirtab *rp = redirlist;
448
449 INTOFF;
450 free_rl(rp, 1);
451 redirlist = rp->next;
452 ckfree(rp);
453 INTON;
454 }
455
456 /*
457 * Undo all redirections. Called on error or interrupt.
458 */
459
460 #ifdef mkinit
461
462 INCLUDE "redir.h"
463
464 RESET {
465 while (redirlist)
466 popredir();
467 }
468
469 SHELLPROC {
470 clearredir(0);
471 }
472
473 #endif
474
475 /* Return true if fd 0 has already been redirected at least once. */
476 int
477 fd0_redirected_p(void)
478 {
479 return fd0_redirected != 0;
480 }
481
482 /*
483 * Discard all saved file descriptors.
484 */
485
486 void
487 clearredir(int vforked)
488 {
489 struct redirtab *rp;
490 struct renamelist *rl;
491
492 for (rp = redirlist ; rp ; rp = rp->next) {
493 if (!vforked)
494 free_rl(rp, 0);
495 else for (rl = rp->renamed; rl; rl = rl->next)
496 if (rl->into >= 0)
497 close(rl->into);
498 }
499 }
500
501
502
503 /*
504 * Copy a file descriptor to be == to.
505 * cloexec indicates if we want close-on-exec or not.
506 * Returns -1 if any error occurs.
507 */
508
509 STATIC int
510 copyfd(int from, int to, int cloexec)
511 {
512 int newfd;
513
514 if (cloexec && to > 2)
515 newfd = dup3(from, to, O_CLOEXEC);
516 else
517 newfd = dup2(from, to);
518
519 return newfd;
520 }
521
522 /*
523 * rename fd from to be fd to (closing from).
524 * close-on-exec is never set on 'to' (unless
525 * from==to and it was set on from) - ie: a no-op
526 * returns to (or errors() if an error occurs).
527 *
528 * This is mostly used for rearranging the
529 * results from pipe().
530 */
531 int
532 movefd(int from, int to)
533 {
534 if (from == to)
535 return to;
536
537 (void) close(to);
538 if (copyfd(from, to, 0) != to) {
539 int e = errno;
540
541 (void) close(from);
542 error("Unable to make fd %d: %s", to, strerror(e));
543 }
544 (void) close(from);
545
546 return to;
547 }
548
549 STATIC void
550 find_big_fd(void)
551 {
552 int i, fd;
553 static int last_start = 3; /* aim to keep sh fd's under 20 */
554
555 if (last_start < 10)
556 last_start++;
557
558 for (i = (1 << last_start); i >= 10; i >>= 1) {
559 if ((fd = fcntl(0, F_DUPFD, i - 1)) >= 0) {
560 close(fd);
561 break;
562 }
563 }
564
565 fd = (i / 5) * 4;
566 if (fd < 10)
567 fd = 10;
568
569 big_sh_fd = fd;
570 }
571
572 /*
573 * If possible, move file descriptor fd out of the way
574 * of expected user fd values. Returns the new fd
575 * (which may be the input fd if things do not go well.)
576 * Always set close-on-exec on the result, and close
577 * the input fd unless it is to be our result.
578 */
579 int
580 to_upper_fd(int fd)
581 {
582 int i;
583
584 VTRACE(DBG_REDIR|DBG_OUTPUT, ("to_upper_fd(%d)", fd));
585 if (big_sh_fd < 10)
586 find_big_fd();
587 do {
588 i = fcntl(fd, F_DUPFD_CLOEXEC, big_sh_fd);
589 if (i >= 0) {
590 if (fd != i)
591 close(fd);
592 VTRACE(DBG_REDIR|DBG_OUTPUT, ("-> %d\n", i));
593 return i;
594 }
595 if (errno != EMFILE && errno != EINVAL)
596 break;
597 find_big_fd();
598 } while (big_sh_fd > 10);
599
600 /*
601 * If we wanted to move this fd to some random high number
602 * we certainly do not intend to pass it through exec, even
603 * if the reassignment failed.
604 */
605 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
606 VTRACE(DBG_REDIR|DBG_OUTPUT, (" fails ->%d\n", fd));
607 return fd;
608 }
609
610 void
611 register_sh_fd(int fd, void (*cb)(int, int))
612 {
613 struct shell_fds *fp;
614
615 fp = ckmalloc(sizeof (struct shell_fds));
616 if (fp != NULL) {
617 fp->nxt = sh_fd_list;
618 sh_fd_list = fp;
619
620 fp->fd = fd;
621 fp->cb = cb;
622 }
623 }
624
625 void
626 sh_close(int fd)
627 {
628 struct shell_fds **fpp, *fp;
629
630 fpp = &sh_fd_list;
631 while ((fp = *fpp) != NULL) {
632 if (fp->fd == fd) {
633 *fpp = fp->nxt;
634 ckfree(fp);
635 break;
636 }
637 fpp = &fp->nxt;
638 }
639 (void)close(fd);
640 }
641
642 STATIC struct shell_fds *
643 sh_fd(int fd)
644 {
645 struct shell_fds *fp;
646
647 for (fp = sh_fd_list; fp != NULL; fp = fp->nxt)
648 if (fp->fd == fd)
649 return fp;
650 return NULL;
651 }
652
653 STATIC void
654 renumber_sh_fd(struct shell_fds *fp)
655 {
656 int to;
657
658 if (fp == NULL)
659 return;
660
661 #ifndef F_DUPFD_CLOEXEC
662 #define F_DUPFD_CLOEXEC F_DUPFD
663 #define CLOEXEC(fd) (fcntl((fd), F_SETFD, fcntl((fd),F_GETFD) | FD_CLOEXEC))
664 #else
665 #define CLOEXEC(fd)
666 #endif
667
668 /*
669 * if we have had a collision, and the sh fd was a "big" one
670 * try moving the sh fd base to a higher number (if possible)
671 * so future sh fds are less likely to be in the user's sights
672 * (incl this one when moved)
673 */
674 if (fp->fd >= big_sh_fd)
675 find_big_fd();
676
677 to = fcntl(fp->fd, F_DUPFD_CLOEXEC, big_sh_fd);
678 if (to == -1)
679 to = fcntl(fp->fd, F_DUPFD_CLOEXEC, big_sh_fd/2);
680 if (to == -1)
681 to = fcntl(fp->fd, F_DUPFD_CLOEXEC, fp->fd + 1);
682 if (to == -1)
683 to = fcntl(fp->fd, F_DUPFD_CLOEXEC, 10);
684 if (to == -1)
685 to = fcntl(fp->fd, F_DUPFD_CLOEXEC, 3);
686 if (to == -1)
687 error("insufficient file descriptors available");
688 CLOEXEC(to);
689
690 if (fp->fd == to) /* impossible? */
691 return;
692
693 (*fp->cb)(fp->fd, to);
694 (void)close(fp->fd);
695 fp->fd = to;
696 }
697
698 static const struct flgnames {
699 const char *name;
700 uint16_t minch;
701 uint32_t value;
702 } nv[] = {
703 #ifdef O_APPEND
704 { "append", 2, O_APPEND },
705 #endif
706 #ifdef O_ASYNC
707 { "async", 2, O_ASYNC },
708 #endif
709 #ifdef O_SYNC
710 { "sync", 2, O_SYNC },
711 #endif
712 #ifdef O_NONBLOCK
713 { "nonblock", 3, O_NONBLOCK },
714 #endif
715 #ifdef O_FSYNC
716 { "fsync", 2, O_FSYNC },
717 #endif
718 #ifdef O_DSYNC
719 { "dsync", 2, O_DSYNC },
720 #endif
721 #ifdef O_RSYNC
722 { "rsync", 2, O_RSYNC },
723 #endif
724 #ifdef O_ALT_IO
725 { "altio", 2, O_ALT_IO },
726 #endif
727 #ifdef O_DIRECT
728 { "direct", 2, O_DIRECT },
729 #endif
730 #ifdef O_NOSIGPIPE
731 { "nosigpipe", 3, O_NOSIGPIPE },
732 #endif
733 #ifdef O_CLOEXEC
734 { "cloexec", 2, O_CLOEXEC },
735 #endif
736 { 0, 0, 0 }
737 };
738 #define ALLFLAGS (O_APPEND|O_ASYNC|O_SYNC|O_NONBLOCK|O_DSYNC|O_RSYNC|\
739 O_ALT_IO|O_DIRECT|O_NOSIGPIPE|O_CLOEXEC)
740
741 static int
742 getflags(int fd, int p)
743 {
744 int c, f;
745
746 if (sh_fd(fd) != NULL) {
747 if (!p)
748 return -1;
749 error("Can't get status for fd=%d (%s)", fd,
750 "Bad file descriptor"); /*XXX*/
751 }
752
753 if ((c = fcntl(fd, F_GETFD)) == -1) {
754 if (!p)
755 return -1;
756 error("Can't get status for fd=%d (%s)", fd, strerror(errno));
757 }
758 if ((f = fcntl(fd, F_GETFL)) == -1) {
759 if (!p)
760 return -1;
761 error("Can't get flags for fd=%d (%s)", fd, strerror(errno));
762 }
763 if (c & FD_CLOEXEC)
764 f |= O_CLOEXEC;
765 return f & ALLFLAGS;
766 }
767
768 static void
769 printone(int fd, int p, int verbose, int pfd)
770 {
771 int f = getflags(fd, p);
772 const struct flgnames *fn;
773
774 if (f == -1)
775 return;
776
777 if (pfd)
778 outfmt(out1, "%d: ", fd);
779 for (fn = nv; fn->name; fn++) {
780 if (f & fn->value) {
781 outfmt(out1, "%s%s", verbose ? "+" : "", fn->name);
782 f &= ~fn->value;
783 } else if (verbose)
784 outfmt(out1, "-%s", fn->name);
785 else
786 continue;
787 if (f || (verbose && fn[1].name))
788 outfmt(out1, ",");
789 }
790 if (verbose && f) /* f should be normally be 0 */
791 outfmt(out1, " +%#x", f);
792 outfmt(out1, "\n");
793 }
794
795 static void
796 parseflags(char *s, int *p, int *n)
797 {
798 int *v, *w;
799 const struct flgnames *fn;
800 size_t len;
801
802 *p = 0;
803 *n = 0;
804 for (s = strtok(s, ","); s; s = strtok(NULL, ",")) {
805 switch (*s++) {
806 case '+':
807 v = p;
808 w = n;
809 break;
810 case '-':
811 v = n;
812 w = p;
813 break;
814 default:
815 error("Missing +/- indicator before flag %s", s-1);
816 }
817
818 len = strlen(s);
819 for (fn = nv; fn->name; fn++)
820 if (len >= fn->minch && strncmp(s,fn->name,len) == 0) {
821 *v |= fn->value;
822 *w &=~ fn->value;
823 break;
824 }
825 if (fn->name == 0)
826 error("Bad flag `%s'", s);
827 }
828 }
829
830 static void
831 setone(int fd, int pos, int neg, int verbose)
832 {
833 int f = getflags(fd, 1);
834 int n, cloexec;
835
836 if (f == -1)
837 return;
838
839 cloexec = -1;
840 if ((pos & O_CLOEXEC) && !(f & O_CLOEXEC))
841 cloexec = FD_CLOEXEC;
842 if ((neg & O_CLOEXEC) && (f & O_CLOEXEC))
843 cloexec = 0;
844
845 if (cloexec != -1 && fcntl(fd, F_SETFD, cloexec) == -1)
846 error("Can't set status for fd=%d (%s)", fd, strerror(errno));
847
848 pos &= ~O_CLOEXEC;
849 neg &= ~O_CLOEXEC;
850 f &= ~O_CLOEXEC;
851 n = f;
852 n |= pos;
853 n &= ~neg;
854 if (n != f && fcntl(fd, F_SETFL, n) == -1)
855 error("Can't set flags for fd=%d (%s)", fd, strerror(errno));
856 if (verbose)
857 printone(fd, 1, verbose, 1);
858 }
859
860 int
861 fdflagscmd(int argc, char *argv[])
862 {
863 char *num;
864 int verbose = 0, ch, pos = 0, neg = 0;
865 char *setflags = NULL;
866
867 optreset = 1; optind = 1; /* initialize getopt */
868 while ((ch = getopt(argc, argv, ":vs:")) != -1)
869 switch ((char)ch) {
870 case 'v':
871 verbose = 1;
872 break;
873 case 's':
874 if (setflags)
875 goto msg;
876 setflags = optarg;
877 break;
878 case '?':
879 default:
880 msg:
881 error("Usage: fdflags [-v] [-s <flags> fd] [fd...]");
882 /* NOTREACHED */
883 }
884
885 argc -= optind, argv += optind;
886
887 if (setflags)
888 parseflags(setflags, &pos, &neg);
889
890 if (argc == 0) {
891 int i;
892
893 if (setflags)
894 goto msg;
895
896 for (i = 0; i <= max_user_fd; i++)
897 printone(i, 0, verbose, 1);
898 return 0;
899 }
900
901 while ((num = *argv++) != NULL) {
902 int fd = number(num);
903
904 while (num[0] == '0' && num[1] != '\0') /* skip 0's */
905 num++;
906 if (strlen(num) > 5)
907 error("%s too big to be a file descriptor", num);
908
909 if (setflags)
910 setone(fd, pos, neg, verbose);
911 else
912 printone(fd, 1, verbose, argc > 1);
913 }
914 return 0;
915 }
916
917 #undef MAX /* in case we inherited them from somewhere */
918 #undef MIN
919
920 #define MIN(a,b) (/*CONSTCOND*/((a)<=(b)) ? (a) : (b))
921 #define MAX(a,b) (/*CONSTCOND*/((a)>=(b)) ? (a) : (b))
922
923 /* now make the compiler work for us... */
924 #define MIN_REDIR MIN(MIN(MIN(MIN(NTO,NFROM), MIN(NTOFD,NFROMFD)), \
925 MIN(MIN(NCLOBBER,NAPPEND), MIN(NHERE,NXHERE))), NFROMTO)
926 #define MAX_REDIR MAX(MAX(MAX(MAX(NTO,NFROM), MAX(NTOFD,NFROMFD)), \
927 MAX(MAX(NCLOBBER,NAPPEND), MAX(NHERE,NXHERE))), NFROMTO)
928
929 static const char *redir_sym[MAX_REDIR - MIN_REDIR + 1] = {
930 [NTO - MIN_REDIR]= ">",
931 [NFROM - MIN_REDIR]= "<",
932 [NTOFD - MIN_REDIR]= ">&",
933 [NFROMFD - MIN_REDIR]= "<&",
934 [NCLOBBER - MIN_REDIR]= ">|",
935 [NAPPEND - MIN_REDIR]= ">>",
936 [NHERE - MIN_REDIR]= "<<",
937 [NXHERE - MIN_REDIR]= "<<",
938 [NFROMTO - MIN_REDIR]= "<>",
939 };
940
941 int
942 outredir(struct output *out, union node *n, int sep)
943 {
944 if (n == NULL)
945 return 0;
946 if (n->type < MIN_REDIR || n->type > MAX_REDIR ||
947 redir_sym[n->type - MIN_REDIR] == NULL)
948 return 0;
949
950 if (sep)
951 outc(sep, out);
952
953 /*
954 * ugly, but all redir node types have "fd" in same slot...
955 * (and code other places assumes it as well)
956 */
957 if ((redir_sym[n->type - MIN_REDIR][0] == '<' && n->nfile.fd != 0) ||
958 (redir_sym[n->type - MIN_REDIR][0] == '>' && n->nfile.fd != 1))
959 outfmt(out, "%d", n->nfile.fd);
960
961 outstr(redir_sym[n->type - MIN_REDIR], out);
962
963 switch (n->type) {
964 case NHERE:
965 outstr("'...'", out);
966 break;
967 case NXHERE:
968 outstr("...", out);
969 break;
970 case NTOFD:
971 case NFROMFD:
972 if (n->ndup.dupfd < 0)
973 outc('-', out);
974 else
975 outfmt(out, "%d", n->ndup.dupfd);
976 break;
977 default:
978 outstr(n->nfile.expfname, out);
979 break;
980 }
981 return 1;
982 }
983