Module Name:    src
Committed By:   kre
Date:           Mon Nov 11 22:57:42 UTC 2024

Modified Files:
        src/bin/sh: eval.c redir.c redir.h sh.1 show.c

Log Message:
This commit is intended to be what was intended to happen in the
commit of Sun Nov 10 01:22:24 UTC 2024, see:

    http://mail-index.netbsd.org/source-changes/2024/11/10/msg154310.html

The commit message for that applies to this one (wholly).   I believe that
the problem with that version which caused it to be reverted has been found
and fixed in this version (a necessary change was made as part of one of
the fixes, but the side-effect implications of that were missed -- bad bad me.)

In addition, I found some more issues with setting close-on-exec on other
command lines

With:
        func 3>whatever

fd 3 (anything > 2) got close on exec set.   That makes no difference
to the function itself (nothing gets exec'd therefore nothing gets closed)
but does to any exec that might happen running a command within the function.

I believe that if this is done (just as if "func" was a regular command,
and not a function) such open fds should be passed through to anything
they exec - unless the function (or other command) takes care to close the
fd passed to it, or explicitly turn on close-on exec.

I expect this usage to be quite rare, and not make much practical difference.

The same applies do builtin commands, but is even less relevant there, eg:

        printf 3>whatever

would have set close-on-exec on fd 3 for printf.  This is generally
completely immaterial, as printf - and most other built-in commands -
neither uses any fd other than (some of) 0 1 & 2, nor do they exec anything.

That is, except for the "exec" built-in which was the focus of the original
fix (mentioned above) and which should remain fixed here, and for the "."
command.

Because of that last one (".") close-on-exec should not be set on built-in
commands (any of them) for redirections on the command line.   This will
almost never make a difference - any such redirections last only as long
as the built-in command lasts (same with functions) and so will generally
never care about the state of close-on-exec, and I have never seen a use
of the "." command with any redirections other than stderr (which is unaffected
here, fd's <= 2 never get close-on-exec set).   That's probably why no-one
ever noticed.

There are still "fd issues" when running a (non #!) shell script, that
are hard to fix, which we should probably handle the way most other shells
have, by simply abandoning the optimisation of not exec'ing a whole new
shell (#! scripts do that exec) and just doing it that way.   Issues solved!
One day.


To generate a diff of this commit:
cvs rdiff -u -r1.196 -r1.197 src/bin/sh/eval.c
cvs rdiff -u -r1.75 -r1.76 src/bin/sh/redir.c
cvs rdiff -u -r1.28 -r1.29 src/bin/sh/redir.h
cvs rdiff -u -r1.269 -r1.270 src/bin/sh/sh.1
cvs rdiff -u -r1.58 -r1.59 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.196 src/bin/sh/eval.c:1.197
--- src/bin/sh/eval.c:1.196	Sun Nov 10 09:06:24 2024
+++ src/bin/sh/eval.c	Mon Nov 11 22:57:42 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: eval.c,v 1.196 2024/11/10 09:06:24 kre Exp $	*/
+/*	$NetBSD: eval.c,v 1.197 2024/11/11 22:57:42 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.196 2024/11/10 09:06:24 kre Exp $");
+__RCSID("$NetBSD: eval.c,v 1.197 2024/11/11 22:57:42 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) {
@@ -1228,8 +1228,8 @@ evalcommand(union node *cmd, int flgs, s
 	case CMDFUNCTION:
 		VXTRACE(DBG_EVAL, ("Shell function%s:  ",vforked?" VF":""),
 		    trargs(argv));
-		redirect(cmd->ncmd.redirect, saved =
-			!(flags & EV_EXIT) || have_traps() ? REDIR_PUSH : 0);
+		redirect(cmd->ncmd.redirect, REDIR_KEEP | (saved =
+			!(flags & EV_EXIT) || have_traps() ? REDIR_PUSH : 0));
 		saveparam = shellparam;
 		shellparam.malloc = 0;
 		shellparam.reset = 1;
@@ -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 | REDIR_KEEP;
+
 		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.75 src/bin/sh/redir.c:1.76
--- src/bin/sh/redir.c:1.75	Sun Nov 10 09:06:24 2024
+++ src/bin/sh/redir.c	Mon Nov 11 22:57:42 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: redir.c,v 1.75 2024/11/10 09:06:24 kre Exp $	*/
+/*	$NetBSD: redir.c,v 1.76 2024/11/11 22:57:42 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.75 2024/11/10 09:06:24 kre Exp $");
+__RCSID("$NetBSD: redir.c,v 1.76 2024/11/11 22:57:42 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -70,8 +70,8 @@ __RCSID("$NetBSD: redir.c,v 1.75 2024/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,24 @@ __RCSID("$NetBSD: redir.c,v 1.75 2024/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
+#undef  fcntl_int
+#define fcntl_int	int
+
 
 MKINIT
 struct renamelist {
 	struct renamelist *next;
 	int orig;
 	int into;
+	int cloexec;	/* orig had FD_CLOEXEC set (into always does) */
 };
 
 MKINIT
@@ -118,14 +125,15 @@ 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);
 STATIC int openhere(const union node *);
-STATIC int copyfd(int, int, int);
+STATIC int copyfd(int, int, int, int);
 STATIC void find_big_fd(void);
 
 
@@ -174,19 +182,43 @@ free_rl(struct redirtab *rt, int reset)
 
 	while ((rl = rn) != NULL) {
 		rn = rl->next;
+
 		if (rl->orig == 0)
 			fd0_redirected--;
+
 		VTRACE(DBG_REDIR, ("popredir %d%s: %s",
 		    rl->orig, rl->orig==0 ? " (STDIN)" : "",
-		    reset ? "" : "no reset\n"));
-		if (reset) {
+		    reset == POPREDIR_UNDO ? "" :
+		    reset == POPREDIR_PERMANENT ? "make permanent" :
+					     "no reset\n"));
+
+		switch (reset) {
+		case POPREDIR_UNDO:
 			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, 1);
+			}
+			break;
+
+		case POPREDIR_PERMANENT:
+			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 +226,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 +262,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 +292,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 +309,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 +331,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 +346,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 +396,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;
@@ -397,7 +450,7 @@ openredirect(union node *redir, char mem
 			    memory[redir->ndup.dupfd])
 				memory[fd] = 1;
 			else if (copyfd(redir->ndup.dupfd, fd,
-			    (flags & REDIR_KEEP) == 0) < 0)
+			    (flags & REDIR_KEEP) == 0, 0) < 0)
 				error("Redirect (from %d to %d) failed: %s",
 				    redir->ndup.dupfd, fd, strerror(errno));
 			VTRACE(DBG_REDIR, ("openredirect: %d%c&%d\n", fd,
@@ -418,20 +471,21 @@ 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));
-		if (copyfd(f, fd, cloexec) < 0) {
+		if (copyfd(f, fd, cloexec, 1) < 0) {
 			int e = errno;
 
 			VTRACE(DBG_REDIR, (" failed: %s\n", strerror(e)));
-			close(f);
 			error("redirect reassignment (fd %d) failed: %s", fd,
 			    strerror(e));
 		}
-		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 +513,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 +537,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 +546,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 +576,7 @@ INCLUDE "redir.h"
 
 RESET {
 	while (redirlist)
-		popredir();
+		popredir(POPREDIR_UNDO);
 }
 
 SHELLPROC {
@@ -541,9 +602,22 @@ clearredir(int vforked)
 	struct redirtab *rp;
 	struct renamelist *rl;
 
+	/*
+	 * This happens only in a child process attempting to
+	 * exec a script which failed ENOEXEC.   Any redirections
+	 * that have been made are for that script, after it
+	 * is done, we exit, there is nothing  to restore, so
+	 * just make the refirectoins permanent.
+	 *
+	 * free_rl() does that does that for us, unless we're
+	 * a child of a vfork() in which case that is unsafe to
+	 * call - that should never happen, but just in case, fale
+	 * it here
+	 */
+
 	for (rp = redirlist ; rp ; rp = rp->next) {
 		if (!vforked)
-			free_rl(rp, 0);
+			free_rl(rp, POPREDIR_PERMANENT);
 		else for (rl = rp->renamed; rl; rl = rl->next)
 			if (rl->into >= 0)
 				close(rl->into);
@@ -553,13 +627,14 @@ clearredir(int vforked)
 
 
 /*
- * Copy a file descriptor to be == to.
+ * Copy a file descriptor (from) to (also) be 'to'..
  * cloexec indicates if we want close-on-exec or not.
+ * move indicates if "from" should be closed on success (yes if set).
  * Returns -1 if any error occurs.
  */
 
 STATIC int
-copyfd(int from, int to, int cloexec)
+copyfd(int from, int to, int cloexec, int move)
 {
 	int newfd;
 
@@ -568,11 +643,29 @@ 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);
+		if (newfd >= 0)
+			fcntl(newfd, F_SETFD,
+			    (fcntl_int)(fcntl(newfd, F_GETFD) | FD_CLOEXEC));
 #endif
 	} else
 		newfd = dup2(from, to);
 
+	if (move && newfd == to && from != to)
+		close(from);
+
+	if (newfd != to) {
+		if (newfd < 0)
+			error("Unable to dup(%d->%d): %s", from, to,
+			    strerror(errno));
+		else {
+			close(newfd);
+			error("Moving fd %d to %d made %d!", from, to, newfd);
+		}
+	}
+
+	if (newfd > biggest_sh_fd)
+		biggest_sh_fd = newfd;
+
 	return newfd;
 }
 
@@ -588,17 +681,21 @@ 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;
 
-	(void) close(to);
-	if (copyfd(from, to, 0) != to) {
+	(void)close(to);
+	if (copyfd(from, to, 0, 1) != to) {
 		int e = errno;
 
 		(void) close(from);
 		error("Unable to make fd %d: %s", to, strerror(e));
 	}
-	(void) close(from);
 
 	return to;
 }
@@ -613,7 +710,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 +741,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 +760,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 +813,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 +977,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 +1006,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 +1068,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 +1080,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 +1091,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 +1105,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 +1144,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.28 src/bin/sh/redir.h:1.29
--- src/bin/sh/redir.h:1.28	Sun Nov 10 09:06:24 2024
+++ src/bin/sh/redir.h	Mon Nov 11 22:57:42 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: redir.h,v 1.28 2024/11/10 09:06:24 kre Exp $	*/
+/*	$NetBSD: redir.h,v 1.29 2024/11/11 22:57:42 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.269 src/bin/sh/sh.1:1.270
--- src/bin/sh/sh.1:1.269	Sun Nov 10 09:06:24 2024
+++ src/bin/sh/sh.1	Mon Nov 11 22:57:42 2024
@@ -1,4 +1,4 @@
-.\"	$NetBSD: sh.1,v 1.269 2024/11/10 09:06:24 kre Exp $
+.\"	$NetBSD: sh.1,v 1.270 2024/11/11 22:57:42 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 11, 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.58 src/bin/sh/show.c:1.59
--- src/bin/sh/show.c:1.58	Sun Nov 10 09:06:24 2024
+++ src/bin/sh/show.c	Mon Nov 11 22:57:42 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: show.c,v 1.58 2024/11/10 09:06:24 kre Exp $	*/
+/*	$NetBSD: show.c,v 1.59 2024/11/11 22:57:42 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.58 2024/11/10 09:06:24 kre Exp $");
+__RCSID("$NetBSD: show.c,v 1.59 2024/11/11 22:57:42 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);
 

Reply via email to