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