Module Name: src Committed By: kre Date: Sun Nov 10 01:22:24 UTC 2024
Modified Files: src/bin/sh: eval.c redir.c redir.h sh.1 show.c Log Message: exec builtin command redirection fixes Several changes, all related to the exec special built in command, or to close on exec, one way or another. (Except a few white space and comment additions, KNF, etc) 1. The bug found by Edgar Fuß reported in: http://mail-index.netbsd.org/tech-userlevel/2024/11/05/msg014588.html has been fixed, now "exec N>whatever" will set close-on-exec for fd N (as do ksh versions, and allowed by POSIX, though other shells do not) which has happened now for many years. But "exec cmd N>whatever" (which looks like the same command when redirections are processed) which was setting close-on-exec on N, now no longer does, so fd N can be passed to cmd as an open fd. For anyone who cares, the big block of change just after "case CMDBUILTIN:" in evalcommand() in eval.c is the fix for this (one line replaced by about 90 ... though most of that is comments or #if 0'd example code for later). It is a bit ugly, and will get worse if our exec command ever gets any options, as others have, but it does work. 2. when the exec builtin utility is used to alter the shell's redirections it is now "all or nothing". Previously the redirections were executed left to right. If one failed, no more were attempted, but the earlier ones remained. This makes no practical difference to a non-interactive shell, as a redirection error causes that shell to exit, but it makes a difference to interactive shells. Now if a redirection fails, any earlier ones which had been performed are undone. Note however that side-effects of redirections (like creating, or truncating, files in the filesystem, cannot be reversed - just the shell's file descriptors returned to how they were before the error). Similarly usage errors on exec now exist .. our exec takes no options (but does handle "--" as POSIX says it must - has done for ages). Until now, that was the only magic piece of exec, running exec -a name somecommand (which several other shells support) would attempt to exec the "-a" command, and most likely fail, causing immediate exit from the shell. Now that is a usage error - a non-interactive shell still exits, as exec is a special builtin, and any error from a special builtin causes a non-interactive shell to exit. But now, an interactive shell will no longer exit (and any redirections that were on the command will be undone, the same as for a redirection error). 3. When a "close on exec" file descriptor is temporarily saved, so the same fd can be redirected for another command (only built-in commands and functions matter, redirects for file system commands happen after a fork() and at that stage if anything goes wrong, the child simply exits - but for non-forking commands, doing something like printf >file required the previous stdout to be saved elsewhere, "file" opened to be the new stdout, then when printf is finished, the old stdout moved back. Anyway, if the fd being moved had close on exec set, then when it was moved back, the close on exec was lost. That is now fixed. 4. The fdflags command no longer allows setting close on exec on stdin, stdout, or stderr - POSIX requires that those 3 fd's always be open (to something) when any normal command is invoked. With close-on-exec set on one of these, that is impossible, so simply refuse it (when "exec N>file" sets close on exec, it only does it for N>2). Minor changes (should be invisible) a. The shell now keeps track of the highest fd number it sees doing normal operations (there are a few internal pipe() calls that aren't monitored and a couple of others, but in general the shell will now know the highest fd it ever saw allocated to it). This is mostly for debugging. b. calls to fcntl() passing an int as the "arg" are now all properly cast to the void * that the fcntl kernel is expecting to receive. I suspect that makes no actual difference to anything, but ... To generate a diff of this commit: cvs rdiff -u -r1.194 -r1.195 src/bin/sh/eval.c cvs rdiff -u -r1.72 -r1.73 src/bin/sh/redir.c cvs rdiff -u -r1.26 -r1.27 src/bin/sh/redir.h cvs rdiff -u -r1.267 -r1.268 src/bin/sh/sh.1 cvs rdiff -u -r1.56 -r1.57 src/bin/sh/show.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/bin/sh/eval.c diff -u src/bin/sh/eval.c:1.194 src/bin/sh/eval.c:1.195 --- src/bin/sh/eval.c:1.194 Mon Oct 21 15:31:34 2024 +++ src/bin/sh/eval.c Sun Nov 10 01:22:24 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: eval.c,v 1.194 2024/10/21 15:31:34 kre Exp $ */ +/* $NetBSD: eval.c,v 1.195 2024/11/10 01:22:24 kre Exp $ */ /*- * Copyright (c) 1993 @@ -37,7 +37,7 @@ #if 0 static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; #else -__RCSID("$NetBSD: eval.c,v 1.194 2024/10/21 15:31:34 kre Exp $"); +__RCSID("$NetBSD: eval.c,v 1.195 2024/11/10 01:22:24 kre Exp $"); #endif #endif /* not lint */ @@ -658,7 +658,7 @@ evalredir(union node *n, int flags) handler = savehandler; e = exception; - popredir(); + popredir(POPREDIR_UNDO); if (PS4 != NULL) { outxstr(PS4); /* { */ outxstr("} failed\n"); @@ -681,7 +681,7 @@ evalredir(union node *n, int flags) } INTOFF; handler = savehandler; - popredir(); + popredir(POPREDIR_UNDO); INTON; if (PS4 != NULL) { @@ -1251,7 +1251,7 @@ evalcommand(union node *cmd, int flgs, s shellparam = saveparam; } if (saved) - popredir(); + popredir(POPREDIR_UNDO); unreffunc(savefunc); poplocalvars(); localvars = savelocalvars; @@ -1296,7 +1296,7 @@ evalcommand(union node *cmd, int flgs, s shellparam = saveparam; handler = savehandler; if (saved) - popredir(); + popredir(POPREDIR_UNDO); INTON; if (evalskip == SKIPFUNC) { evalskip = SKIPNONE; @@ -1311,18 +1311,100 @@ evalcommand(union node *cmd, int flgs, s case CMDBUILTIN: VXTRACE(DBG_EVAL, ("builtin command [%d]%s: ", argc, vforked ? " VF" : ""), trargs(argv)); - mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH; + + if (cmdentry.u.bltin == execcmd) { + char **ap; + + /* + * Work out how we should process redirections + * on the "exec" command. We need REDIR_KEEP + * if we must not set close-on-exec, and REDIR_PUSH + * if we need to be able to undo them (in the + * exec command, only on some kind of error). + * + * Skip "exec" (argv[0]) then examine args. + * + * This must be done manually, as nextopt() + * hasn't been init'd for this command yet. + * And it won't be until after redirections are done. + * + * "exec" currently takes no options (except "--"), + * but might one day, and this needs to keep working, + * so do it, kind of, properly. + * + * Note in the common cases argv[1] will be NULL + * (for exec just setting up redirectons) or will + * not start with a '-' ("exec cmd") so normally + * this loop will either never start or will break + * at the first test of the first iteration. + */ + for (ap = argv + 1; *ap != NULL; ap++) { + + if (ap[0][0] != '-') + break; + + if (ap[0][1] == '\0') /* "exec -" */ + break; /* or continue ?? */ + + if (ap[0][1] == '-' && ap[0][2] == '\0') { + ap++; + break; + } + +#if defined(DUMMY_EXAMPLE_CODE) && 0 + /* + * if options are added to "exec" then + * any which take an arg (like the common + * in other shells "-a cmdname") need to + * be recognised here, lest "cmdname" be + * thought to be the cmd to exec + */ + + for (char *op = ap[0] + 1; *op; op++) { + switch (*op) { + case 'a': + case any others similar: + /* options needing an optarg */ + if (op[1] == '\0' && ap[1]) + ap++; + break; + + default: + /* options with no optarg */ + continue; + } + break; + } +#endif /* DUMMY EXAMPLE CODE */ + } + + if (*ap != NULL) + mode = REDIR_KEEP; /* exec cmd <... */ + else + mode = 0; /* exec < .... */ + + /* + * always save old fd setup in case of error() + * execcmd() will undo this if no error occurs + * (that is, in the case the shell has not vanished) + */ + mode |= REDIR_PUSH; + } else /* any builtin execpt "exec" */ + mode = REDIR_PUSH; + if (flags == EV_BACKCMD) { memout.nleft = 0; memout.nextc = memout.buf; memout.bufsize = 64; mode |= REDIR_BACKQ; } + e = -1; savecmdname = commandname; savetopfile = getcurrentfile(); savehandler = handler; temp_path = 0; + if (!setjmp(jmploc.loc)) { handler = &jmploc; @@ -1395,8 +1477,10 @@ evalcommand(union node *cmd, int flgs, s popfilesupto(savetopfile); FORCEINTON; } + if (cmdentry.u.bltin != execcmd) - popredir(); + popredir(POPREDIR_UNDO); + if (flags == EV_BACKCMD) { backcmd->buf = memout.buf; backcmd->nleft = memout.nextc - memout.buf; @@ -1699,7 +1783,13 @@ truecmd(int argc, char **argv) int execcmd(int argc, char **argv) { - (void) nextopt(NULL); /* ignore a leading "--" */ + /* + * BEWARE: if any options are added here, they must + * also be added in evalcommand(), look for "DUMMY_EXAMPLE_CODE" + * for example code for there. Here the options would be + * processed completely normally. + */ + (void) nextopt(""); /* ignore a leading "--" */ if (*argptr) { struct strlist *sp; @@ -1710,7 +1800,9 @@ execcmd(int argc, char **argv) for (sp = cmdenviron; sp; sp = sp->next) setvareq(sp->text, VDOEXPORT|VEXPORT|VSTACK); shellexec(argptr, environment(), pathval(), 0, 0); + /* NOTREACHED */ } + popredir(POPREDIR_PERMANENT); /* make any redirections permanent */ return 0; } Index: src/bin/sh/redir.c diff -u src/bin/sh/redir.c:1.72 src/bin/sh/redir.c:1.73 --- src/bin/sh/redir.c:1.72 Mon Nov 22 05:17:43 2021 +++ src/bin/sh/redir.c Sun Nov 10 01:22:24 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: redir.c,v 1.72 2021/11/22 05:17:43 kre Exp $ */ +/* $NetBSD: redir.c,v 1.73 2024/11/10 01:22:24 kre Exp $ */ /*- * Copyright (c) 1991, 1993 @@ -37,7 +37,7 @@ #if 0 static char sccsid[] = "@(#)redir.c 8.2 (Berkeley) 5/4/95"; #else -__RCSID("$NetBSD: redir.c,v 1.72 2021/11/22 05:17:43 kre Exp $"); +__RCSID("$NetBSD: redir.c,v 1.73 2024/11/10 01:22:24 kre Exp $"); #endif #endif /* not lint */ @@ -70,8 +70,8 @@ __RCSID("$NetBSD: redir.c,v 1.72 2021/11 #include "show.h" -#define EMPTY -2 /* marks an unused slot in redirtab */ #define CLOSED -1 /* fd was not open before redir */ + #ifndef PIPE_BUF # define PIPESIZE 4096 /* amount of buffering in a pipe */ #else @@ -84,17 +84,22 @@ __RCSID("$NetBSD: redir.c,v 1.72 2021/11 #ifndef F_DUPFD_CLOEXEC #define F_DUPFD_CLOEXEC F_DUPFD -#define CLOEXEC(fd) (fcntl((fd), F_SETFD, fcntl((fd),F_GETFD) | FD_CLOEXEC)) +#define CLOEXEC(fd) (fcntl((fd), F_SETFD, \ + (fcntl_int)(fcntl((fd), F_GETFD) | FD_CLOEXEC))) #else -#define CLOEXEC(fd) +#define CLOEXEC(fd) __nothing #endif +/* yes, this is correct, bizarre parens and all -- used only as a cast */ +#define fcntl_int void *)(intptr_t + MKINIT struct renamelist { struct renamelist *next; int orig; int into; + int cloexec; /* orig had FD_CLOEXEC set (into always does) */ }; MKINIT @@ -118,9 +123,10 @@ STATIC int fd0_redirected = 0; * way of user defined fds (normally) */ STATIC int big_sh_fd = 0; +STATIC int biggest_sh_fd = 2; STATIC const struct renamelist *is_renamed(const struct renamelist *, int); -STATIC void fd_rename(struct redirtab *, int, int); +STATIC void fd_rename(struct redirtab *, int, int, int); STATIC int * saved_redirected_fd(int); STATIC void free_rl(struct redirtab *, int); STATIC void openredirect(union node *, char[10], int); @@ -178,15 +184,35 @@ free_rl(struct redirtab *rt, int reset) fd0_redirected--; VTRACE(DBG_REDIR, ("popredir %d%s: %s", rl->orig, rl->orig==0 ? " (STDIN)" : "", - reset ? "" : "no reset\n")); - if (reset) { + reset == 1 ? "" : + reset == 2 ? "make permanent" : + "no reset\n")); + + switch (reset) { + case 1: if (rl->into < 0) { - VTRACE(DBG_REDIR, ("closed\n")); + VTRACE(DBG_REDIR, (" closed\n")); close(rl->orig); } else { - VTRACE(DBG_REDIR, ("from %d\n", rl->into)); - movefd(rl->into, rl->orig); + VTRACE(DBG_REDIR, + (" from %d%s\n", rl->into, + rl->cloexec ? " (colexec)" : "")); + copyfd(rl->into, rl->orig, rl->cloexec); } + break; + case 2: + if (rl->into < 0) { + VTRACE(DBG_REDIR, (" was closed\n")); + /* nothing to do */ + } else { + VTRACE(DBG_REDIR, + (" close savefd %d\n", rl->into)); + close(rl->into); + } + break; + default: + /* nothing to do */ + break; } ckfree(rl); } @@ -194,16 +220,22 @@ free_rl(struct redirtab *rt, int reset) } STATIC void -fd_rename(struct redirtab *rt, int from, int to) +fd_rename(struct redirtab *rt, int from, int to, int cloexec) { /* XXX someday keep a short list (8..10) of freed renamelists XXX */ struct renamelist *rl = ckmalloc(sizeof(struct renamelist)); + /* + * Note this list is operated as LIFO so saved fd's are + * undone in the opposite order to that they were saved + * (needed to ensure correct results) + */ rl->next = rt->renamed; rt->renamed = rl; rl->orig = from; rl->into = to; + rl->cloexec = cloexec; } /* @@ -224,6 +256,7 @@ redirect(union node *redir, int flags) char memory[10]; /* file descriptors to write to memory */ CTRACE(DBG_REDIR, ("redirect(F=0x%x):%s\n", flags, redir?"":" NONE")); + for (i = 10 ; --i >= 0 ; ) memory[i] = 0; memory[1] = flags & REDIR_BACKQ; @@ -253,7 +286,7 @@ redirect(union node *redir, int flags) error("fd %d: %s", fd, strerror(EBADF)); /* redirect from/to same file descriptor */ /* make sure it stays open */ - if (fcntl(fd, F_SETFD, 0) < 0) + if (fcntl(fd, F_SETFD, (fcntl_int)0) < 0) error("fd %d: %s", fd, strerror(errno)); continue; } @@ -270,13 +303,21 @@ redirect(union node *redir, int flags) if ((flags & REDIR_PUSH) && !is_renamed(sv->renamed, fd)) { int bigfd; + int cloexec; + + cloexec = fcntl(fd, F_GETFD); + if (cloexec >= 0) + cloexec &= FD_CLOEXEC; + else + cloexec = 0; INTOFF; if (big_sh_fd < 10) find_big_fd(); if ((bigfd = big_sh_fd) < max_user_fd) bigfd = max_user_fd; - if ((i = fcntl(fd, F_DUPFD, bigfd + 1)) == -1) { + if ((i = fcntl(fd, F_DUPFD, + (fcntl_int)(bigfd + 1))) == -1) { switch (errno) { case EBADF: i = CLOSED; @@ -284,11 +325,13 @@ redirect(union node *redir, int flags) case EMFILE: case EINVAL: find_big_fd(); - i = fcntl(fd, F_DUPFD, big_sh_fd); + i = fcntl(fd, F_DUPFD, + (fcntl_int) big_sh_fd); if (i >= 0) break; if (errno == EMFILE || errno == EINVAL) - i = fcntl(fd, F_DUPFD, 3); + i = fcntl(fd, F_DUPFD, + (fcntl_int) 3); if (i >= 0) break; /* FALLTHRU */ @@ -297,10 +340,13 @@ redirect(union node *redir, int flags) /* NOTREACHED */ } } + if (i > biggest_sh_fd) + biggest_sh_fd = i; if (i >= 0) - (void)fcntl(i, F_SETFD, FD_CLOEXEC); - fd_rename(sv, fd, i); - VTRACE(DBG_REDIR, ("fd %d saved as %d ", fd, i)); + (void)fcntl(i, F_SETFD, (fcntl_int) FD_CLOEXEC); + fd_rename(sv, fd, i, cloexec); + VTRACE(DBG_REDIR, ("fd %d saved as %d%s ", fd, i, + cloexec ? "+" : "")); INTON; } VTRACE(DBG_REDIR, ("%s\n", fd == 0 ? "STDIN" : "")); @@ -344,7 +390,8 @@ openredirect(union node *redir, char mem VTRACE(DBG_REDIR, ("openredirect(< '%s') -> %d [%#x]", fname, f, eflags)); if (eflags) - (void)fcntl(f, F_SETFL, fcntl(f, F_GETFL, 0) & ~eflags); + (void)fcntl(f, F_SETFL, + (fcntl_int)(fcntl(f, F_GETFL) & ~eflags)); break; case NFROMTO: fname = redir->nfile.expfname; @@ -418,6 +465,9 @@ openredirect(union node *redir, char mem abort(); } + if (f > biggest_sh_fd) + biggest_sh_fd = f; + cloexec = fd > 2 && (flags & REDIR_KEEP) == 0 && !posix; if (f != fd) { VTRACE(DBG_REDIR, (" -> %d", fd)); @@ -431,7 +481,7 @@ openredirect(union node *redir, char mem } close(f); } else if (cloexec) - (void)fcntl(f, F_SETFD, FD_CLOEXEC); + (void)fcntl(f, F_SETFD, (fcntl_int) FD_CLOEXEC); VTRACE(DBG_REDIR, ("%s\n", cloexec ? " cloexec" : "")); INTON; @@ -459,6 +509,8 @@ openhere(const union node *redir) if (pipe(pip) < 0) error("Pipe call failed"); + if (pip[1] > biggest_sh_fd) + biggest_sh_fd = pip[1]; len = strlen(redir->nhere.text); VTRACE(DBG_REDIR, ("openhere(%p) [%d] \"%.*s\"%s\n", redir, len, (len < 40 ? len : 40), redir->nhere.text, (len < 40 ? "" : "..."))); @@ -481,7 +533,7 @@ openhere(const union node *redir) _exit(0); } VTRACE(DBG_REDIR, ("openhere (closing %d)", pip[1])); - out: + out:; close(pip[1]); VTRACE(DBG_REDIR, (" (pipe fd=%d)", pip[0])); return pip[0]; @@ -490,16 +542,21 @@ openhere(const union node *redir) /* - * Undo the effects of the last redirection. + * if (reset == POPREDIR_UNDO) + * Undo the effects of the last redirection. + * else if (reset == POPREDIR_PERMANENT) + * Make the last redirection permanent + * else / * reset == POPREDIR_DISCARD * / + * Just throw away the redirection */ void -popredir(void) +popredir(int reset) { struct redirtab *rp = redirlist; INTOFF; - free_rl(rp, 1); + free_rl(rp, reset); redirlist = rp->next; ckfree(rp); INTON; @@ -515,7 +572,7 @@ INCLUDE "redir.h" RESET { while (redirlist) - popredir(); + popredir(POPREDIR_UNDO); } SHELLPROC { @@ -543,7 +600,7 @@ clearredir(int vforked) for (rp = redirlist ; rp ; rp = rp->next) { if (!vforked) - free_rl(rp, 0); + free_rl(rp, POPREDIR_DISCARD); else for (rl = rp->renamed; rl; rl = rl->next) if (rl->into >= 0) close(rl->into); @@ -568,11 +625,15 @@ copyfd(int from, int to, int cloexec) newfd = dup3(from, to, O_CLOEXEC); #else newfd = dup2(from, to); - fcntl(newfd, F_SETFD, fcntl(newfd,F_GETFD) | FD_CLOEXEC); + fcntl(newfd, F_SETFD, + (fcntl_int)(fcntl(newfd, F_GETFD) | FD_CLOEXEC)); #endif } else newfd = dup2(from, to); + if (newfd > biggest_sh_fd) + biggest_sh_fd = newfd; + return newfd; } @@ -588,6 +649,11 @@ copyfd(int from, int to, int cloexec) int movefd(int from, int to) { + if (from > biggest_sh_fd) + biggest_sh_fd = from; + if (to > biggest_sh_fd) + biggest_sh_fd = to; + if (from == to) return to; @@ -613,7 +679,9 @@ find_big_fd(void) last_start++; for (i = (1 << last_start); i >= 10; i >>= 1) { - if ((fd = fcntl(0, F_DUPFD, i - 1)) >= 0) { + if ((fd = fcntl(0, F_DUPFD, (fcntl_int)(i - 1))) >= 0) { + if (fd > biggest_sh_fd) + biggest_sh_fd = fd; close(fd); break; } @@ -642,8 +710,10 @@ to_upper_fd(int fd) if (big_sh_fd < 10 || big_sh_fd >= user_fd_limit) find_big_fd(); do { - i = fcntl(fd, F_DUPFD_CLOEXEC, big_sh_fd); + i = fcntl(fd, F_DUPFD_CLOEXEC, (fcntl_int) big_sh_fd); if (i >= 0) { + if (i > biggest_sh_fd) + biggest_sh_fd = i; if (fd != i) close(fd); VTRACE(DBG_REDIR|DBG_OUTPUT, ("-> %d\n", i)); @@ -659,7 +729,7 @@ to_upper_fd(int fd) * we certainly do not intend to pass it through exec, even * if the reassignment failed. */ - (void)fcntl(fd, F_SETFD, FD_CLOEXEC); + (void)fcntl(fd, F_SETFD, (fcntl_int) FD_CLOEXEC); VTRACE(DBG_REDIR|DBG_OUTPUT, (" fails ->%d\n", fd)); return fd; } @@ -712,18 +782,20 @@ pick_new_fd(int fd) { int to; - to = fcntl(fd, F_DUPFD_CLOEXEC, big_sh_fd); + to = fcntl(fd, F_DUPFD_CLOEXEC, (fcntl_int) big_sh_fd); if (to == -1 && big_sh_fd >= 22) - to = fcntl(fd, F_DUPFD_CLOEXEC, big_sh_fd/2); + to = fcntl(fd, F_DUPFD_CLOEXEC, (fcntl_int) (big_sh_fd / 2)); if (to == -1) - to = fcntl(fd, F_DUPFD_CLOEXEC, fd + 1); + to = fcntl(fd, F_DUPFD_CLOEXEC, (fcntl_int) (fd + 1)); if (to == -1) - to = fcntl(fd, F_DUPFD_CLOEXEC, 10); + to = fcntl(fd, F_DUPFD_CLOEXEC, (fcntl_int) 10); if (to == -1) - to = fcntl(fd, F_DUPFD_CLOEXEC, 3); + to = fcntl(fd, F_DUPFD_CLOEXEC, (fcntl_int) 3); if (to == -1) error("insufficient file descriptors available"); CLOEXEC(to); + if (to > biggest_sh_fd) + biggest_sh_fd = to; return to; } @@ -874,11 +946,11 @@ static const struct flgnames { O_NOFOLLOW|O_CREAT|O_TRUNC|O_EXCL|O_NOCTTY|O_DIRECTORY|O_REGULAR) static int -getflags(int fd, int p) +getflags(int fd, int p, int all) { int c, f; - if (sh_fd(fd) != NULL || saved_redirected_fd(fd) != NULL) { + if (!all && (sh_fd(fd) != NULL || saved_redirected_fd(fd) != NULL)) { if (!p) return -1; error("Can't get status for fd=%d (%s)", fd, strerror(EBADF)); @@ -903,7 +975,7 @@ getflags(int fd, int p) static void printone(int fd, int p, int verbose, int pfd) { - int f = getflags(fd, p); + int f = getflags(fd, p, verbose > 1); const struct flgnames *fn; if (f == -1) @@ -965,7 +1037,7 @@ parseflags(char *s, int *p, int *n) static void setone(int fd, int pos, int neg, int verbose) { - int f = getflags(fd, 1); + int f = getflags(fd, 1, 0); int n, cloexec; if (f == -1) @@ -977,7 +1049,9 @@ setone(int fd, int pos, int neg, int ver if ((neg & O_CLOEXEC) && (f & O_CLOEXEC)) cloexec = 0; - if (cloexec != -1 && fcntl(fd, F_SETFD, cloexec) == -1) + /* Don't allow O_CLOEXEC on stdin, stdout, or stderr */ + if ((cloexec > 0 && fd <= 2 && (errno = EINVAL)) || + (cloexec != -1 && fcntl(fd, F_SETFD, (fcntl_int) cloexec) == -1)) error("Can't set status for fd=%d (%s)", fd, strerror(errno)); pos &= ~O_CLOEXEC; @@ -986,7 +1060,7 @@ setone(int fd, int pos, int neg, int ver n = f; n |= pos; n &= ~neg; - if (n != f && fcntl(fd, F_SETFL, n) == -1) + if (n != f && fcntl(fd, F_SETFL, (fcntl_int)n) == -1) error("Can't set flags for fd=%d (%s)", fd, strerror(errno)); if (verbose) printone(fd, 1, verbose, 1); @@ -1000,8 +1074,17 @@ fdflagscmd(int argc, char *argv[]) char *setflags = NULL; optreset = 1; optind = 1; /* initialize getopt */ - while ((ch = getopt(argc, argv, ":vs:")) != -1) + while ((ch = getopt(argc, argv, ":vs:" +#ifdef DEBUG + "V" +#endif + )) != -1) switch ((char)ch) { +#ifdef DEBUG + case 'V': + verbose = 2; + break; +#endif case 'v': verbose = 1; break; @@ -1030,6 +1113,9 @@ fdflagscmd(int argc, char *argv[]) for (i = 0; i <= max_user_fd; i++) printone(i, 0, verbose, 1); + if (verbose > 1) + while (i <= biggest_sh_fd) + printone(i++, 0, verbose, 1); } else while ((num = *argv++) != NULL) { int fd = number(num); Index: src/bin/sh/redir.h diff -u src/bin/sh/redir.h:1.26 src/bin/sh/redir.h:1.27 --- src/bin/sh/redir.h:1.26 Wed Sep 15 18:29:45 2021 +++ src/bin/sh/redir.h Sun Nov 10 01:22:24 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: redir.h,v 1.26 2021/09/15 18:29:45 kre Exp $ */ +/* $NetBSD: redir.h,v 1.27 2024/11/10 01:22:24 kre Exp $ */ /*- * Copyright (c) 1991, 1993 @@ -40,9 +40,14 @@ #define REDIR_VFORK 0x04 /* running under vfork(2), be careful */ #define REDIR_KEEP 0x08 /* don't close-on-exec */ +/* flags passed to popredir and free_rl */ +#define POPREDIR_DISCARD 0 /* just abandon everything */ +#define POPREDIR_UNDO 1 /* undo saved redirects */ +#define POPREDIR_PERMANENT 2 /* keep renamed fd, close saving fd */ + union node; void redirect(union node *, int); -void popredir(void); +void popredir(int); int fd0_redirected_p(void); void clearredir(int); int movefd(int, int); Index: src/bin/sh/sh.1 diff -u src/bin/sh/sh.1:1.267 src/bin/sh/sh.1:1.268 --- src/bin/sh/sh.1:1.267 Mon Oct 14 08:27:53 2024 +++ src/bin/sh/sh.1 Sun Nov 10 01:22:24 2024 @@ -1,4 +1,4 @@ -.\" $NetBSD: sh.1,v 1.267 2024/10/14 08:27:53 kre Exp $ +.\" $NetBSD: sh.1,v 1.268 2024/11/10 01:22:24 kre Exp $ .\" Copyright (c) 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" @@ -31,7 +31,7 @@ .\" .\" @(#)sh.1 8.6 (Berkeley) 5/4/95 .\" -.Dd October 14, 2024 +.Dd November 9, 2024 .Dt SH 1 .\" everything except c o and s (keep them ordered) .ds flags abCEeFfhIiLlmnpqruVvXx @@ -381,7 +381,7 @@ or if it is a pipeline (or simple comman .Dq \&! operator. With pipelines, only the status of the entire pipeline -(indicated by the last command it contains) +(usually generated by the last command it contains) is tested when .Fl e is set to determine if the shell should exit. @@ -640,7 +640,7 @@ opened using the .Ic exec built-in command are passed on to utilities executed .Dq ( yes -in posix mode), +in posix mode, though the POSIX standard does not actually require this), whether a colon (:) terminates the user name in tilde (~) expansions other than in assignment statements .Dq ( no @@ -2771,11 +2771,18 @@ Any redirections on the .Ic exec command are marked as permanent, so that they are not undone when the .Ic exec -command finishes. +command finishes, which only happens if no +.Ar command +was given. +.Pp When the .Cm posix option is not set, -file descriptors created via such redirections are marked close-on-exec +file descriptors created via such redirections, +when no +.Ar command +is present, +are marked close-on-exec (see .Xr open 2 .Dv O_CLOEXEC @@ -2787,7 +2794,8 @@ unless the descriptors refer to the stan output, or error (file descriptors 0, 1, 2). Traditionally Bourne-like shells (except -.Xr ksh 1 ) , +.Xr ksh 1 +and its close relatives), made those file descriptors available to exec'ed processes. To be assured the close-on-exec setting is off, redirect the descriptor to (or from) itself, @@ -2798,11 +2806,30 @@ or by using .Ic exec .No as opened it, after the open Pc to leave the descriptor open in the shell -and pass it to all commands invoked subsequently. +and also pass it to all commands invoked subsequently. Alternatively, see the .Ic fdflags -command below, which can set, or clear, this, and other, +built-in command below, which can set, or clear, this, and other, file descriptor flags. +.Pp +If there is a usage, or redirection, error, +.Ic exec +will not exit from an interactive shell, +but will restore all modified file descriptors +to the state they had before the +.Ic exec +command was issued. +But note that side effects of any redirections that +succeeded, such as creating or truncating files, cannot +be reversed. +However if a +.Ar command +cannot be executed for any reason, even an +interactive shell will exit. +Non-interactive shells will exit on any error, +as +.Ic exec +is a special built-in utility. .\" .Pp .It Ic exit Op Ar exitstatus @@ -3076,6 +3103,10 @@ and .Cm cloexec . Unique abbreviations of these names, of at least 2 characters, may be used on input. +It is not permitted to set +.Cm cloexec +on file descriptors 0, 1, or 2 +.Pq standard input , standard output , and standard error . See .Xr fcntl 2 and Index: src/bin/sh/show.c diff -u src/bin/sh/show.c:1.56 src/bin/sh/show.c:1.57 --- src/bin/sh/show.c:1.56 Fri Jul 12 04:45:12 2024 +++ src/bin/sh/show.c Sun Nov 10 01:22:24 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: show.c,v 1.56 2024/07/12 04:45:12 kre Exp $ */ +/* $NetBSD: show.c,v 1.57 2024/11/10 01:22:24 kre Exp $ */ /*- * Copyright (c) 1991, 1993 @@ -39,7 +39,7 @@ #if 0 static char sccsid[] = "@(#)show.c 8.3 (Berkeley) 5/4/95"; #else -__RCSID("$NetBSD: show.c,v 1.56 2024/07/12 04:45:12 kre Exp $"); +__RCSID("$NetBSD: show.c,v 1.57 2024/11/10 01:22:24 kre Exp $"); #endif #endif /* not lint */ @@ -163,7 +163,7 @@ opentrace(void) */ if (tracefile) (void) fclose(tracefile); /* also closes tfd */ - tracefile = fdopen(fd, "a"); /* don't care if it is NULL */ + tracefile = fdopen(fd, "ae"); /* don't care if it is NULL */ if (tracefile) /* except here... */ setlinebuf(tracefile);