[prev in list] [next in list] [prev in thread] [next in thread] 

List:       busybox
Subject:    Telnetd flow control
From:       "Doug Graham" <dgraham () nortel ! com>
Date:       2009-01-22 6:20:53
Message-ID: 20090122062053.GF32016 () nortel ! com
[Download RAW message or body]

Hello,

Busybox's telnetd does not disable local (client-side) flow control
properly.  It does not put the pty into packet mode and then notify the
client whenever flow control is disabled by an application running under
its control.  The result is that ^S/^Q are not passed through to the
application, which is painful when the application is an emacs variant.

I suppose that support for this might be considered bloat, but the
included patch only adds about 200 bytes of text to x86 busybox and 300
bytes to mipsel busybox.  Please consider applying.

--- busybox-1.13.2/networking/telnetd.c	2009/01/21 20:02:39	1.1
+++ busybox-1.13.2/networking/telnetd.c	2009/01/22 00:35:28
@@ -38,6 +38,9 @@
 	int sockfd_read, sockfd_write, ptyfd;
 	int shell_pid;
 
+#ifdef TIOCPKT
+	int flowstate;
+#endif
 	/* two circular buffers */
 	/*char *buf1, *buf2;*/
 /*#define TS_BUF1 ts->buf1*/
@@ -170,6 +173,9 @@
 	int fd, pid;
 	char tty_name[GETPTY_BUFSIZE];
 	struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
+#ifdef TIOCPKT
+	int on = 1;
+#endif
 
 	/*ts->buf1 = (char *)(ts + 1);*/
 	/*ts->buf2 = ts->buf1 + BUFSIZE;*/
@@ -180,6 +186,10 @@
 		maxfd = fd;
 	ts->ptyfd = fd;
 	ndelay_on(fd);
+#ifdef TIOCPKT
+	ioctl(fd, TIOCPKT, &on);
+	ts->flowstate = TIOCPKT_DOSTOP;
+#endif
 #if ENABLE_FEATURE_TELNETD_STANDALONE
 	ts->sockfd_read = sock;
 	/* SO_KEEPALIVE by popular demand */
@@ -385,6 +395,16 @@
 		portnbr = 23,
 	};
 #endif
+#ifdef TIOCPKT
+	int control;
+	static const char lflow_on[] =
+	    {IAC, SB, TELOPT_LFLOW, LFLOW_ON, IAC, SE};
+	static const char lflow_off[] =
+	    {IAC, SB, TELOPT_LFLOW, LFLOW_OFF, IAC, SE};
+# define RESERVED sizeof(lflow_on)
+#else
+# define RESERVED 0
+#endif
 	/* Even if !STANDALONE, we accept (and ignore) -i, thus people
 	 * don't need to guess whether it's ok to pass -i to us */
 	opt = getopt32(argv, "f:l:Ki" USE_FEATURE_TELNETD_STANDALONE("p:b:F"),
@@ -475,7 +495,7 @@
 				FD_SET(ts->sockfd_read, &rdfdset);
 			if (ts->size2 > 0)       /* can write to socket */
 				FD_SET(ts->sockfd_write, &wrfdset);
-			if (ts->size2 < BUFSIZE) /* can read from pty */
+			if (ts->size2 < (BUFSIZE - RESERVED)) /* can read from pty */
 				FD_SET(ts->ptyfd, &rdfdset);
 		}
 		ts = next;
@@ -593,6 +613,52 @@
 					goto skip4;
 				goto kill_session;
 			}
+#ifdef TIOCPKT
+			control = TS_BUF2[ts->rdidx2];
+			if (--count > 0 && control == TIOCPKT_DATA) {
+				/*
+				 * If we are in packet mode, and we have
+				 * just read a chunk of actual data from
+				 * the pty, then there is the TIOCPKT_DATA
+				 * byte (zero) that we have got to remove
+				 * somehow.  If there were no chars in
+				 * TS_BUF2 before we did this read, then
+				 * we can optimize by just advancing wridx2.
+				 * Otherwise we have to copy the new data down
+				 * to close the gap (Could use readv() instead).
+				 */
+				if (ts->size2 == 0)
+					ts->wridx2++;
+				else {
+					memmove(TS_BUF2 + ts->rdidx2,
+						TS_BUF2 + ts->rdidx2 + 1, count);
+				}
+			}
+
+			/*
+			 * If the flow control state changed, notify
+			 * the client.  If "control" is not TIOCPKT_DATA,
+			 * then there are no data bytes to worry about.
+			 */
+			if ((control & (TIOCPKT_DOSTOP|TIOCPKT_NOSTOP)) != 0
+			 && ts->flowstate != (control & TIOCPKT_DOSTOP)) {
+				const char *p = ts->flowstate ? lflow_off : lflow_on;
+
+				/*
+				 * We know we have enough free slots available
+				 * (see RESERVED) but they are not necessarily
+				 * contiguous; we may have to wrap.
+				 */
+				for (count = sizeof(lflow_on); count > 0; count--) {
+					TS_BUF2[ts->rdidx2++] = *p++;
+					if (ts->rdidx2 >= BUFSIZE)
+						ts->rdidx2 = 0;
+					ts->size2++;
+				}
+
+				ts->flowstate = control & TIOCPKT_DOSTOP;
+			}
+#endif /* TIOCPKT */
 			ts->size2 += count;
 			ts->rdidx2 += count;
 			if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */

--Doug
_______________________________________________
busybox mailing list
busybox@busybox.net
http://lists.busybox.net/mailman/listinfo/busybox
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic