Subject: PR 11733: rcmd(1) always asks for stderr channel
To: None <tech-userlevel@netbsd.org>
From: Simon J. Gerraty <sjg@quick.com.au>
List: tech-userlevel
Date: 12/26/2000 01:07:46
The essential problem with the way rcmd(1) and rcmd(3) interract is
that there is no way to communicate to rcmd(1) that no stderr channel
should be established - eg for rcp(1).

The following patch provides a fix for PR 11733 but 
a/ only solves it for rcmd(1), not replacement commands.
b/ might be better if resrcmd(3) used putenv("RCMD_NO_FD2=1") rather
than pass a new option (-2) to rcmd_cmd.  In fact that was my first
inclination.

Input welcome.
--sjg

Index: lib/libc/net/rcmd.c
===================================================================
RCS file: /cvsroot/basesrc/lib/libc/net/rcmd.c,v
retrieving revision 1.44
diff -u -p -r1.44 rcmd.c
--- lib/libc/net/rcmd.c	2000/07/07 08:03:39	1.44
+++ lib/libc/net/rcmd.c	2000/12/26 09:00:28
@@ -487,8 +487,9 @@ rshrcmd(ahost, rport, locuser, remuser, 
 			execlp(rshcmd, p ? p + 1 : rshcmd, "-c", cmd, NULL);
 		} else {
 			p = strrchr(rshcmd, '/');
-			execlp(rshcmd, p ? p + 1 : rshcmd, *ahost, "-l",
-			    remuser, cmd, NULL);
+			execlp(rshcmd, p ? p + 1 : rshcmd, *ahost,
+			       (fd2p) ? "-l" : "-2l",
+			       remuser, cmd, NULL);
 		}
 		warn("rshrcmd: exec %s", rshcmd);
 		_exit(1);
Index: usr.bin/rsh/rsh.c
===================================================================
RCS file: /cvsroot/basesrc/usr.bin/rsh/rsh.c,v
retrieving revision 1.13
diff -u -p -r1.13 rsh.c
--- usr.bin/rsh/rsh.c	2000/01/31 14:19:34	1.13
+++ usr.bin/rsh/rsh.c	2000/12/26 09:00:29
@@ -88,7 +88,7 @@ void	warning __P((const char *, ...));
  * rsh - remote shell
  */
 extern	char *__progname;		/* XXX */
-int	remerr;
+int	*remerrp;
 
 static int sigs[] = { SIGINT, SIGTERM, SIGQUIT };
 
@@ -113,6 +113,7 @@ main(argc, argv)
 	struct passwd *pw;
 	struct servent *sp;
 	sigset_t oset, nset;
+	int fd2;
 
 #ifdef IN_RCMD
 	char	*locuser = 0, *loop;
@@ -125,7 +126,8 @@ main(argc, argv)
 	argoff = asrsh = dflag = nflag = 0;
 	one = 1;
 	host = user = NULL;
-
+	remerrp = &fd2;
+	
 #ifndef IN_RCMD
 	/*
 	 * If called as something other than "rsh" use it as the host name,
@@ -145,6 +147,9 @@ main(argc, argv)
 	}
 
 #ifdef IN_RCMD
+	if (getenv("RCMD_NO_FD2") != NULL)
+		remerrp = NULL;
+
 	if ((loop = getenv("RCMD_LOOP")) && strcmp(loop, "YES") == 0)
 		warnx("rcmd appears to be looping!");
 
@@ -152,24 +157,24 @@ main(argc, argv)
 
 # ifdef KERBEROS
 #  ifdef CRYPT
-#   define	OPTIONS	"8KLdek:l:nu:wx"
+#   define	OPTIONS	"28KLdek:l:nu:wx"
 #  else
-#   define	OPTIONS	"8KLdek:l:nu:w"
+#   define	OPTIONS	"28KLdek:l:nu:w"
 #  endif
 # else
-#  define	OPTIONS	"8KLdel:nu:w"
+#  define	OPTIONS	"28KLdel:nu:w"
 # endif
 
 #else /* IN_RCMD */
 
 # ifdef KERBEROS
 #  ifdef CRYPT
-#   define	OPTIONS	"8KLdek:l:nwx"
+#   define	OPTIONS	"28KLdek:l:nwx"
 #  else
-#   define	OPTIONS	"8KLdek:l:nw"
+#   define	OPTIONS	"28KLdek:l:nw"
 #  endif
 # else
-#  define	OPTIONS	"8KLdel:nw"
+#  define	OPTIONS	"28KLdel:nw"
 # endif
 
 #endif /* IN_RCMD */
@@ -181,6 +186,9 @@ main(argc, argv)
 		err(1, "malloc");
 	while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
 		switch(ch) {
+		case '2':
+			remerrp = NULL;
+			break;
 		case 'K':
 #ifdef KERBEROS
 			use_kerberos = 0;
@@ -306,10 +314,10 @@ try_connect:
 #ifdef CRYPT
 		if (doencrypt)
 			rem = krcmd_mutual(&host, sp->s_port, user, args,
-			    &remerr, dest_realm, &cred, schedule);
+			    remerrp, dest_realm, &cred, schedule);
 		else
 #endif
-			rem = krcmd(&host, sp->s_port, user, args, &remerr,
+			rem = krcmd(&host, sp->s_port, user, args, remerrp,
 			    dest_realm);
 		if (rem < 0) {
 			use_kerberos = 0;
@@ -331,7 +339,7 @@ try_connect:
 		rem = rcmd_af(&host, sp->s_port,
 #endif
 		    name,
-		    user, args, &remerr, PF_UNSPEC);
+		    user, args, remerrp, PF_UNSPEC);
 	}
 #else /* KERBEROS */
 
@@ -340,20 +348,21 @@ try_connect:
 #else
 	rem = rcmd_af(&host, sp->s_port,
 #endif
-	    name, user, args, &remerr, PF_UNSPEC);
+	    name, user, args, remerrp, PF_UNSPEC);
 #endif /* KERBEROS */
 	(void)free(name);
 
 	if (rem < 0)
 		exit(1);
 
-	if (remerr < 0)
+	if (remerrp != NULL && *remerrp < 0)
 		errx(1, "can't establish stderr");
 	if (dflag) {
 		if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one,
 		    sizeof(one)) < 0)
 			warn("setsockopt remote");
-		if (setsockopt(remerr, SOL_SOCKET, SO_DEBUG, &one,
+		if (remerrp != NULL &&
+		    setsockopt(*remerrp, SOL_SOCKET, SO_DEBUG, &one,
 		    sizeof(one)) < 0)
 			warn("setsockopt stderr");
 	}
@@ -387,7 +396,8 @@ try_connect:
 	if (!doencrypt)
 #endif
 	{
-		(void)ioctl(remerr, FIONBIO, &one);
+		if (remerrp)
+			(void)ioctl(*remerrp, FIONBIO, &one);
 		(void)ioctl(rem, FIONBIO, &one);
 	}
 
@@ -451,7 +461,8 @@ talk(nflag, oset, pid, rem)
 
 
 	if (!nflag && pid == 0) {
-		(void)close(remerr);
+		if (remerrp)
+			(void)close(*remerrp);
 
 		fdp->events = POLLOUT|POLLNVAL|POLLERR|POLLHUP;
 		fdp->fd = rem;
@@ -507,11 +518,15 @@ done:
 	}
 
 	(void) sigprocmask(SIG_SETMASK, oset, NULL);
+	nfds = 0;
 	fds[0].events = fds[1].events = POLLIN|POLLNVAL|POLLERR|POLLHUP;
-	fds[0].fd = remerr;
-	fds[1].fd = rem;
+	if (remerrp)
+		fds[nfds++].fd = *remerrp;
+	else
+		fds[1].events = 0;
+	fds[nfds++].fd = rem;
 	fdp = &fds[0];
-	nfds = 2;
+
 	do {
 		if (poll(fdp, nfds, INFTIM) == -1) {
 			if (errno != EINTR)
@@ -537,15 +552,18 @@ sendsig(sig)
 {
 	char signo;
 
+	if (!remerrp)
+		return;
+	
 	signo = sig;
 #ifdef KERBEROS
 #ifdef CRYPT
 	if (doencrypt)
-		(void)des_write(remerr, &signo, 1);
+		(void)des_write(*remerrp, &signo, 1);
 	else
 #endif
 #endif
-		(void)write(remerr, &signo, 1);
+		(void)write(*remerrp, &signo, 1);
 }
 
 #ifdef KERBEROS