blob: eec4417ca96aaceec0619b0eee9771efede518c0 [file] [log] [blame]
Bernhard Reutner-Fischer2c998512006-04-12 18:09:26 +00001/* vi: set sw=4 ts=4: */
Bernhard Reutner-Fischerdac7ff12006-04-12 17:55:51 +00002/*
Eric Andersen08a72202002-09-30 20:52:10 +00003 * Simple telnet server
4 * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
5 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02006 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersen08a72202002-09-30 20:52:10 +00007 *
8 * ---------------------------------------------------------------------------
9 * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
10 ****************************************************************************
11 *
12 * The telnetd manpage says it all:
13 *
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020014 * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for
15 * a client, then creating a login process which has the slave side of the
16 * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the
17 * master side of the pseudo-terminal, implementing the telnet protocol and
18 * passing characters between the remote client and the login process.
Eric Andersen08a72202002-09-30 20:52:10 +000019 *
20 * Vladimir Oleynik <dzo@simtreas.ru> 2001
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020021 * Set process group corrections, initial busybox port
Eric Andersen08a72202002-09-30 20:52:10 +000022 */
Denis Vlasenko75f8d082006-11-22 15:54:52 +000023#define DEBUG 0
Eric Andersen08a72202002-09-30 20:52:10 +000024
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000025#include "libbb.h"
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +000026#include <syslog.h>
Rob Landley099ed502006-08-28 09:41:49 +000027
Denis Vlasenko75f8d082006-11-22 15:54:52 +000028#if DEBUG
Denys Vlasenko3a416112010-04-05 22:10:38 +020029# define TELCMDS
30# define TELOPTS
Eric Andersen08a72202002-09-30 20:52:10 +000031#endif
32#include <arpa/telnet.h>
Eric Andersen08a72202002-09-30 20:52:10 +000033
Denys Vlasenko3a416112010-04-05 22:10:38 +020034
Eric Andersen08a72202002-09-30 20:52:10 +000035struct tsession {
36 struct tsession *next;
Denis Vlasenkod814c982009-02-02 23:43:57 +000037 pid_t shell_pid;
Denys Vlasenko1d77db82009-06-10 13:38:08 +020038 int sockfd_read;
39 int sockfd_write;
40 int ptyfd;
Denis Vlasenko59d7c432007-10-15 15:19:36 +000041
Eric Andersen08a72202002-09-30 20:52:10 +000042 /* two circular buffers */
Denis Vlasenko59d7c432007-10-15 15:19:36 +000043 /*char *buf1, *buf2;*/
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020044/*#define TS_BUF1(ts) ts->buf1*/
45/*#define TS_BUF2(ts) TS_BUF2(ts)*/
46#define TS_BUF1(ts) ((unsigned char*)(ts + 1))
47#define TS_BUF2(ts) (((unsigned char*)(ts + 1)) + BUFSIZE)
Eric Andersen08a72202002-09-30 20:52:10 +000048 int rdidx1, wridx1, size1;
49 int rdidx2, wridx2, size2;
50};
51
Denis Vlasenko88308fe2007-06-25 10:35:11 +000052/* Two buffers are directly after tsession in malloced memory.
53 * Make whole thing fit in 4k */
Denis Vlasenko59d7c432007-10-15 15:19:36 +000054enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 };
Denis Vlasenko88308fe2007-06-25 10:35:11 +000055
Eric Andersen08a72202002-09-30 20:52:10 +000056
Denis Vlasenko59d7c432007-10-15 15:19:36 +000057/* Globals */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020058struct globals {
59 struct tsession *sessions;
60 const char *loginpath;
61 const char *issuefile;
62 int maxfd;
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +010063} FIX_ALIASING;
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020064#define G (*(struct globals*)&bb_common_bufsiz1)
65#define INIT_G() do { \
66 G.loginpath = "/bin/login"; \
67 G.issuefile = "/etc/issue.net"; \
68} while (0)
Eric Andersen08a72202002-09-30 20:52:10 +000069
70
71/*
Denis Vlasenko59d7c432007-10-15 15:19:36 +000072 Remove all IAC's from buf1 (received IACs are ignored and must be removed
73 so as to not be interpreted by the terminal). Make an uninterrupted
74 string of characters fit for the terminal. Do this by packing
75 all characters meant for the terminal sequentially towards the end of buf.
Eric Andersen08a72202002-09-30 20:52:10 +000076
Denys Vlasenko4c721042010-04-04 23:45:09 +020077 Return a pointer to the beginning of the characters meant for the terminal
Eric Andersen08a72202002-09-30 20:52:10 +000078 and make *num_totty the number of characters that should be sent to
79 the terminal.
80
Denys Vlasenko4c721042010-04-04 23:45:09 +020081 Note - if an IAC (3 byte quantity) starts before (bf + len) but extends
Denis Vlasenko59d7c432007-10-15 15:19:36 +000082 past (bf + len) then that IAC will be left unprocessed and *processed
83 will be less than len.
Eric Andersen08a72202002-09-30 20:52:10 +000084
Denis Vlasenko10916c52007-10-15 17:28:00 +000085 CR-LF ->'s CR mapping is also done here, for convenience.
86
87 NB: may fail to remove iacs which wrap around buffer!
Denis Vlasenko75f8d082006-11-22 15:54:52 +000088 */
Denis Vlasenko10916c52007-10-15 17:28:00 +000089static unsigned char *
Denis Vlasenko75f8d082006-11-22 15:54:52 +000090remove_iacs(struct tsession *ts, int *pnum_totty)
91{
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020092 unsigned char *ptr0 = TS_BUF1(ts) + ts->wridx1;
Eric Andersen08a72202002-09-30 20:52:10 +000093 unsigned char *ptr = ptr0;
94 unsigned char *totty = ptr;
95 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
Eric Andersen08a72202002-09-30 20:52:10 +000096 int num_totty;
97
98 while (ptr < end) {
99 if (*ptr != IAC) {
Denis Vlasenko10916c52007-10-15 17:28:00 +0000100 char c = *ptr;
101
102 *totty++ = c;
103 ptr++;
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000104 /* We map \r\n ==> \r for pragmatic reasons.
Eric Andersen3752d332003-12-19 11:30:13 +0000105 * Many client implementations send \r\n when
106 * the user hits the CarriageReturn key.
107 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000108 if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0'))
Eric Andersen3752d332003-12-19 11:30:13 +0000109 ptr++;
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000110 continue;
Eric Andersen08a72202002-09-30 20:52:10 +0000111 }
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000112
113 if ((ptr+1) >= end)
114 break;
115 if (ptr[1] == NOP) { /* Ignore? (putty keepalive, etc.) */
116 ptr += 2;
117 continue;
118 }
119 if (ptr[1] == IAC) { /* Literal IAC? (emacs M-DEL) */
120 *totty++ = ptr[1];
121 ptr += 2;
122 continue;
123 }
124
125 /*
126 * TELOPT_NAWS support!
127 */
128 if ((ptr+2) >= end) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200129 /* Only the beginning of the IAC is in the
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000130 buffer we were asked to process, we can't
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200131 process this char */
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000132 break;
133 }
134 /*
135 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
136 */
137 if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
138 struct winsize ws;
139 if ((ptr+8) >= end)
Denys Vlasenkofb132e42010-10-29 11:46:52 +0200140 break; /* incomplete, can't process */
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000141 ws.ws_col = (ptr[3] << 8) | ptr[4];
142 ws.ws_row = (ptr[5] << 8) | ptr[6];
143 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
144 ptr += 9;
145 continue;
146 }
147 /* skip 3-byte IAC non-SB cmd */
148#if DEBUG
149 fprintf(stderr, "Ignoring IAC %s,%s\n",
150 TELCMD(ptr[1]), TELOPT(ptr[2]));
151#endif
152 ptr += 3;
Eric Andersen08a72202002-09-30 20:52:10 +0000153 }
154
Denis Vlasenko0de37e12007-10-17 11:08:53 +0000155 num_totty = totty - ptr0;
Eric Andersen08a72202002-09-30 20:52:10 +0000156 *pnum_totty = num_totty;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200157 /* The difference between ptr and totty is number of iacs
158 we removed from the stream. Adjust buf1 accordingly */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000159 if ((ptr - totty) == 0) /* 99.999% of cases */
160 return ptr0;
161 ts->wridx1 += ptr - totty;
162 ts->size1 -= ptr - totty;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200163 /* Move chars meant for the terminal towards the end of the buffer */
Eric Andersen08a72202002-09-30 20:52:10 +0000164 return memmove(ptr - num_totty, ptr0, num_totty);
165}
166
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000167/*
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000168 * Converting single IAC into double on output
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000169 */
170static size_t iac_safe_write(int fd, const char *buf, size_t count)
171{
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000172 const char *IACptr;
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000173 size_t wr, rc, total;
174
175 total = 0;
176 while (1) {
177 if (count == 0)
178 return total;
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000179 if (*buf == (char)IAC) {
180 static const char IACIAC[] ALIGN1 = { IAC, IAC };
181 rc = safe_write(fd, IACIAC, 2);
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000182 if (rc != 2)
183 break;
184 buf++;
185 total++;
186 count--;
187 continue;
188 }
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000189 /* count != 0, *buf != IAC */
190 IACptr = memchr(buf, IAC, count);
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000191 wr = count;
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000192 if (IACptr)
193 wr = IACptr - buf;
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000194 rc = safe_write(fd, buf, wr);
195 if (rc != wr)
196 break;
197 buf += rc;
198 total += rc;
199 count -= rc;
200 }
201 /* here: rc - result of last short write */
202 if ((ssize_t)rc < 0) { /* error? */
203 if (total == 0)
204 return rc;
205 rc = 0;
206 }
207 return total + rc;
208}
Eric Andersen08a72202002-09-30 20:52:10 +0000209
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200210/* Must match getopt32 string */
211enum {
212 OPT_WATCHCHILD = (1 << 2), /* -K */
213 OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200214 OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p PORT */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200215 OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200216 OPT_SYSLOG = (1 << 7) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -S */
217 OPT_WAIT = (1 << 8) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -w SEC */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200218};
219
Eric Andersen08a72202002-09-30 20:52:10 +0000220static struct tsession *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000221make_new_session(
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200222 IF_FEATURE_TELNETD_STANDALONE(int sock)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000223 IF_NOT_FEATURE_TELNETD_STANDALONE(void)
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000224) {
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200225#if !ENABLE_FEATURE_TELNETD_STANDALONE
Denys Vlasenkoff0e8752010-05-10 04:16:43 +0200226 enum { sock = 0 };
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200227#endif
Denis Vlasenko88308fe2007-06-25 10:35:11 +0000228 const char *login_argv[2];
Eric Andersen08a72202002-09-30 20:52:10 +0000229 struct termios termbuf;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000230 int fd, pid;
Denis Vlasenko85c24712008-03-17 09:04:04 +0000231 char tty_name[GETPTY_BUFSIZE];
Rob Landley1ec5b292006-05-29 07:42:02 +0000232 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
Eric Andersen08a72202002-09-30 20:52:10 +0000233
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000234 /*ts->buf1 = (char *)(ts + 1);*/
235 /*ts->buf2 = ts->buf1 + BUFSIZE;*/
Eric Andersen08a72202002-09-30 20:52:10 +0000236
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200237 /* Got a new connection, set up a tty */
Bernhard Reutner-Fischerae4342c2008-05-19 08:18:50 +0000238 fd = xgetpty(tty_name);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200239 if (fd > G.maxfd)
240 G.maxfd = fd;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000241 ts->ptyfd = fd;
242 ndelay_on(fd);
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200243 close_on_exec_on(fd);
244
Denis Vlasenko6d044352008-11-09 00:44:40 +0000245 /* SO_KEEPALIVE by popular demand */
246 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200247#if ENABLE_FEATURE_TELNETD_STANDALONE
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200248 ts->sockfd_read = sock;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000249 ndelay_on(sock);
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200250 if (sock == 0) { /* We are called with fd 0 - we are in inetd mode */
Denis Vlasenko9e237672007-10-17 11:18:49 +0000251 sock++; /* so use fd 1 for output */
252 ndelay_on(sock);
253 }
Denis Vlasenkof472b232007-10-16 21:35:17 +0000254 ts->sockfd_write = sock;
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200255 if (sock > G.maxfd)
256 G.maxfd = sock;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000257#else
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000258 /* ts->sockfd_read = 0; - done by xzalloc */
Denis Vlasenkof472b232007-10-16 21:35:17 +0000259 ts->sockfd_write = 1;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000260 ndelay_on(0);
261 ndelay_on(1);
262#endif
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200263
Eric Andersen08a72202002-09-30 20:52:10 +0000264 /* Make the telnet client understand we will echo characters so it
265 * should not do it locally. We don't tell the client to run linemode,
266 * because we want to handle line editing and tab completion and other
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000267 * stuff that requires char-by-char support. */
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000268 {
269 static const char iacs_to_send[] ALIGN1 = {
270 IAC, DO, TELOPT_ECHO,
271 IAC, DO, TELOPT_NAWS,
Denys Vlasenkofb132e42010-10-29 11:46:52 +0200272 /* This requires telnetd.ctrlSQ.patch (incomplete) */
273 /*IAC, DO, TELOPT_LFLOW,*/
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000274 IAC, WILL, TELOPT_ECHO,
275 IAC, WILL, TELOPT_SGA
276 };
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000277 /* This confuses iac_safe_write(), it will try to duplicate
278 * each IAC... */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200279 //memcpy(TS_BUF2(ts), iacs_to_send, sizeof(iacs_to_send));
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000280 //ts->rdidx2 = sizeof(iacs_to_send);
281 //ts->size2 = sizeof(iacs_to_send);
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000282 /* So just stuff it into TCP stream! (no error check...) */
283#if ENABLE_FEATURE_TELNETD_STANDALONE
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000284 safe_write(sock, iacs_to_send, sizeof(iacs_to_send));
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000285#else
286 safe_write(1, iacs_to_send, sizeof(iacs_to_send));
287#endif
288 /*ts->rdidx2 = 0; - xzalloc did it */
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000289 /*ts->size2 = 0;*/
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000290 }
Eric Andersen08a72202002-09-30 20:52:10 +0000291
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100292 fflush_all();
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000293 pid = vfork(); /* NOMMU-friendly */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000294 if (pid < 0) {
295 free(ts);
296 close(fd);
Denis Vlasenko23c81282007-10-16 22:01:23 +0000297 /* sock will be closed by caller */
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000298 bb_perror_msg("vfork");
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000299 return NULL;
Eric Andersen08a72202002-09-30 20:52:10 +0000300 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000301 if (pid > 0) {
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000302 /* Parent */
Denis Vlasenko2450c452007-10-15 22:09:15 +0000303 ts->shell_pid = pid;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000304 return ts;
Eric Andersen08a72202002-09-30 20:52:10 +0000305 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000306
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000307 /* Child */
308 /* Careful - we are after vfork! */
Eric Andersen08a72202002-09-30 20:52:10 +0000309
Denis Vlasenkod814c982009-02-02 23:43:57 +0000310 /* Restore default signal handling ASAP */
Denis Vlasenko25591c32008-02-16 22:58:56 +0000311 bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000312
Denys Vlasenko58c3d212010-11-30 09:17:30 +0100313 pid = getpid();
314
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200315 if (ENABLE_FEATURE_UTMP) {
316 len_and_sockaddr *lsa = get_peer_lsa(sock);
317 char *hostname = NULL;
318 if (lsa) {
319 hostname = xmalloc_sockaddr2dotted(&lsa->u.sa);
320 free(lsa);
321 }
322 write_new_utmp(pid, LOGIN_PROCESS, tty_name, /*username:*/ "LOGIN", hostname);
323 free(hostname);
324 }
325
Denis Vlasenkod814c982009-02-02 23:43:57 +0000326 /* Make new session and process group */
327 setsid();
328
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200329 /* Open the child's side of the tty */
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +0000330 /* NB: setsid() disconnects from any previous ctty's. Therefore
331 * we must open child's side of the tty AFTER setsid! */
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200332 close(0);
Denis Vlasenko1101d1c2008-07-21 09:22:28 +0000333 xopen(tty_name, O_RDWR); /* becomes our ctty */
334 xdup2(0, 1);
335 xdup2(0, 2);
Denys Vlasenko3a416112010-04-05 22:10:38 +0200336 tcsetpgrp(0, pid); /* switch this tty's process group to us */
337
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200338 /* The pseudo-terminal allocated to the client is configured to operate
339 * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000340 tcgetattr(0, &termbuf);
341 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000342 termbuf.c_oflag |= ONLCR | XTABS;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000343 termbuf.c_iflag |= ICRNL;
344 termbuf.c_iflag &= ~IXOFF;
345 /*termbuf.c_lflag &= ~ICANON;*/
Denis Vlasenko202ac502008-11-05 13:20:58 +0000346 tcsetattr_stdin_TCSANOW(&termbuf);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000347
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100348 /* Uses FILE-based I/O to stdout, but does fflush_all(),
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000349 * so should be safe with vfork.
350 * I fear, though, that some users will have ridiculously big
Denis Vlasenko018b1552007-11-06 01:38:46 +0000351 * issue files, and they may block writing to fd 1,
352 * (parent is supposed to read it, but parent waits
353 * for vforked child to exec!) */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200354 print_login_issue(G.issuefile, tty_name);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000355
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000356 /* Exec shell / login / whatever */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200357 login_argv[0] = G.loginpath;
Denis Vlasenko88308fe2007-06-25 10:35:11 +0000358 login_argv[1] = NULL;
Denis Vlasenko018b1552007-11-06 01:38:46 +0000359 /* exec busybox applet (if PREFER_APPLETS=y), if that fails,
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200360 * exec external program.
361 * NB: sock is either 0 or has CLOEXEC set on it.
362 * fd has CLOEXEC set on it too. These two fds will be closed here.
363 */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200364 BB_EXECVP(G.loginpath, (char **)login_argv);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000365 /* _exit is safer with vfork, and we shouldn't send message
Denis Vlasenko2450c452007-10-15 22:09:15 +0000366 * to remote clients anyway */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200367 _exit(EXIT_FAILURE); /*bb_perror_msg_and_die("execv %s", G.loginpath);*/
Eric Andersen08a72202002-09-30 20:52:10 +0000368}
369
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000370#if ENABLE_FEATURE_TELNETD_STANDALONE
371
Eric Andersen08a72202002-09-30 20:52:10 +0000372static void
373free_session(struct tsession *ts)
374{
Denys Vlasenko3a416112010-04-05 22:10:38 +0200375 struct tsession *t;
Eric Andersen08a72202002-09-30 20:52:10 +0000376
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000377 if (option_mask32 & OPT_INETD)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000378 exit(EXIT_SUCCESS);
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000379
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000380 /* Unlink this telnet session from the session list */
Denys Vlasenko3a416112010-04-05 22:10:38 +0200381 t = G.sessions;
Mike Frysinger731f81c2006-05-10 15:23:12 +0000382 if (t == ts)
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200383 G.sessions = ts->next;
Eric Andersen08a72202002-09-30 20:52:10 +0000384 else {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000385 while (t->next != ts)
Eric Andersen08a72202002-09-30 20:52:10 +0000386 t = t->next;
387 t->next = ts->next;
388 }
389
Denis Vlasenkof472b232007-10-16 21:35:17 +0000390#if 0
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000391 /* It was said that "normal" telnetd just closes ptyfd,
392 * doesn't send SIGKILL. When we close ptyfd,
393 * kernel sends SIGHUP to processes having slave side opened. */
Denis Vlasenkof472b232007-10-16 21:35:17 +0000394 kill(ts->shell_pid, SIGKILL);
Denis Vlasenkod814c982009-02-02 23:43:57 +0000395 waitpid(ts->shell_pid, NULL, 0);
Denis Vlasenkof472b232007-10-16 21:35:17 +0000396#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000397 close(ts->ptyfd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000398 close(ts->sockfd_read);
Denis Vlasenkof472b232007-10-16 21:35:17 +0000399 /* We do not need to close(ts->sockfd_write), it's the same
400 * as sockfd_read unless we are in inetd mode. But in inetd mode
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000401 * we do not reach this */
Eric Andersen08a72202002-09-30 20:52:10 +0000402 free(ts);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000403
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000404 /* Scan all sessions and find new maxfd */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200405 G.maxfd = 0;
406 ts = G.sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000407 while (ts) {
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200408 if (G.maxfd < ts->ptyfd)
409 G.maxfd = ts->ptyfd;
410 if (G.maxfd < ts->sockfd_read)
411 G.maxfd = ts->sockfd_read;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000412#if 0
413 /* Again, sockfd_write == sockfd_read here */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200414 if (G.maxfd < ts->sockfd_write)
415 G.maxfd = ts->sockfd_write;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000416#endif
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000417 ts = ts->next;
418 }
Eric Andersen08a72202002-09-30 20:52:10 +0000419}
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000420
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000421#else /* !FEATURE_TELNETD_STANDALONE */
422
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000423/* Used in main() only, thus "return 0" actually is exit(EXIT_SUCCESS). */
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000424#define free_session(ts) return 0
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000425
426#endif
427
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000428static void handle_sigchld(int sig UNUSED_PARAM)
Denis Vlasenko2450c452007-10-15 22:09:15 +0000429{
430 pid_t pid;
431 struct tsession *ts;
Denys Vlasenko3a416112010-04-05 22:10:38 +0200432 int save_errno = errno;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000433
Denis Vlasenko018b1552007-11-06 01:38:46 +0000434 /* Looping: more than one child may have exited */
435 while (1) {
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000436 pid = wait_any_nohang(NULL);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000437 if (pid <= 0)
438 break;
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200439 ts = G.sessions;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000440 while (ts) {
441 if (ts->shell_pid == pid) {
442 ts->shell_pid = -1;
Denys Vlasenko3a416112010-04-05 22:10:38 +0200443// man utmp:
444// When init(8) finds that a process has exited, it locates its utmp entry
445// by ut_pid, sets ut_type to DEAD_PROCESS, and clears ut_user, ut_host
446// and ut_time with null bytes.
447// [same applies to other processes which maintain utmp entries, like telnetd]
448//
449// We do not bother actually clearing fields:
450// it might be interesting to know who was logged in and from where
451 update_utmp(pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000452 break;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000453 }
454 ts = ts->next;
455 }
456 }
Denys Vlasenko3a416112010-04-05 22:10:38 +0200457
458 errno = save_errno;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000459}
460
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000461int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000462int telnetd_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen08a72202002-09-30 20:52:10 +0000463{
Eric Andersen08a72202002-09-30 20:52:10 +0000464 fd_set rdfdset, wrfdset;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000465 unsigned opt;
Denis Vlasenko10916c52007-10-15 17:28:00 +0000466 int count;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000467 struct tsession *ts;
468#if ENABLE_FEATURE_TELNETD_STANDALONE
469#define IS_INETD (opt & OPT_INETD)
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200470 int master_fd = master_fd; /* for compiler */
471 int sec_linger = sec_linger;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000472 char *opt_bindaddr = NULL;
473 char *opt_portnbr;
474#else
475 enum {
476 IS_INETD = 1,
477 master_fd = -1,
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000478 };
Glenn L McGrathc2b91862003-09-12 11:27:15 +0000479#endif
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200480 INIT_G();
481
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200482 /* -w NUM, and implies -F. -w and -i don't mix */
483 IF_FEATURE_TELNETD_INETD_WAIT(opt_complementary = "wF:w+:i--w:w--i";)
Denis Vlasenko2450c452007-10-15 22:09:15 +0000484 /* Even if !STANDALONE, we accept (and ignore) -i, thus people
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000485 * don't need to guess whether it's ok to pass -i to us */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200486 opt = getopt32(argv, "f:l:Ki"
487 IF_FEATURE_TELNETD_STANDALONE("p:b:F")
488 IF_FEATURE_TELNETD_INETD_WAIT("Sw:"),
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200489 &G.issuefile, &G.loginpath
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200490 IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)
491 IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger)
492 );
Denis Vlasenko2450c452007-10-15 22:09:15 +0000493 if (!IS_INETD /*&& !re_execed*/) {
494 /* inform that we start in standalone mode?
495 * May be useful when people forget to give -i */
496 /*bb_error_msg("listening for connections");*/
497 if (!(opt & OPT_FOREGROUND)) {
498 /* DAEMON_CHDIR_ROOT was giving inconsistent
Denis Vlasenko008eda22007-10-16 10:47:27 +0000499 * behavior with/without -F, -i */
Denis Vlasenko018b1552007-11-06 01:38:46 +0000500 bb_daemonize_or_rexec(0 /*was DAEMON_CHDIR_ROOT*/, argv);
Denis Vlasenko2450c452007-10-15 22:09:15 +0000501 }
502 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000503 /* Redirect log to syslog early, if needed */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200504 if (IS_INETD || (opt & OPT_SYSLOG) || !(opt & OPT_FOREGROUND)) {
Denis Vlasenko5e4fda02009-03-08 23:46:48 +0000505 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000506 logmode = LOGMODE_SYSLOG;
507 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000508#if ENABLE_FEATURE_TELNETD_STANDALONE
509 if (IS_INETD) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200510 G.sessions = make_new_session(0);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200511 if (!G.sessions) /* pty opening or vfork problem, exit */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200512 return 1; /* make_new_session printed error message */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000513 } else {
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200514 master_fd = 0;
515 if (!(opt & OPT_WAIT)) {
516 unsigned portnbr = 23;
517 if (opt & OPT_PORT)
518 portnbr = xatou16(opt_portnbr);
519 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
520 xlisten(master_fd, 1);
521 }
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200522 close_on_exec_on(master_fd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000523 }
Rob Landley00e76cb2005-05-10 23:53:33 +0000524#else
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200525 G.sessions = make_new_session();
526 if (!G.sessions) /* pty opening or vfork problem, exit */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200527 return 1; /* make_new_session printed error message */
Rob Landley00e76cb2005-05-10 23:53:33 +0000528#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000529
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000530 /* We don't want to die if just one session is broken */
531 signal(SIGPIPE, SIG_IGN);
Eric Andersen08a72202002-09-30 20:52:10 +0000532
Denis Vlasenko2450c452007-10-15 22:09:15 +0000533 if (opt & OPT_WATCHCHILD)
534 signal(SIGCHLD, handle_sigchld);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000535 else /* prevent dead children from becoming zombies */
536 signal(SIGCHLD, SIG_IGN);
Denis Vlasenko2450c452007-10-15 22:09:15 +0000537
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000538/*
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200539 This is how the buffers are used. The arrows indicate data flow.
540
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000541 +-------+ wridx1++ +------+ rdidx1++ +----------+
542 | | <-------------- | buf1 | <-------------- | |
543 | | size1-- +------+ size1++ | |
544 | pty | | socket |
545 | | rdidx2++ +------+ wridx2++ | |
546 | | --------------> | buf2 | --------------> | |
547 +-------+ size2++ +------+ size2-- +----------+
548
549 size1: "how many bytes are buffered for pty between rdidx1 and wridx1?"
550 size2: "how many bytes are buffered for socket between rdidx2 and wridx2?"
551
552 Each session has got two buffers. Buffers are circular. If sizeN == 0,
553 buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases
554 rdidxN == wridxN.
555*/
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000556 again:
557 FD_ZERO(&rdfdset);
558 FD_ZERO(&wrfdset);
Eric Andersen08a72202002-09-30 20:52:10 +0000559
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000560 /* Select on the master socket, all telnet sockets and their
561 * ptys if there is room in their session buffers.
562 * NB: scalability problem: we recalculate entire bitmap
563 * before each select. Can be a problem with 500+ connections. */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200564 ts = G.sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000565 while (ts) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200566 struct tsession *next = ts->next; /* in case we free ts */
Denis Vlasenko2450c452007-10-15 22:09:15 +0000567 if (ts->shell_pid == -1) {
Denis Vlasenko018b1552007-11-06 01:38:46 +0000568 /* Child died and we detected that */
Denis Vlasenko2450c452007-10-15 22:09:15 +0000569 free_session(ts);
570 } else {
571 if (ts->size1 > 0) /* can write to pty */
572 FD_SET(ts->ptyfd, &wrfdset);
573 if (ts->size1 < BUFSIZE) /* can read from socket */
574 FD_SET(ts->sockfd_read, &rdfdset);
575 if (ts->size2 > 0) /* can write to socket */
576 FD_SET(ts->sockfd_write, &wrfdset);
577 if (ts->size2 < BUFSIZE) /* can read from pty */
578 FD_SET(ts->ptyfd, &rdfdset);
579 }
580 ts = next;
581 }
582 if (!IS_INETD) {
583 FD_SET(master_fd, &rdfdset);
584 /* This is needed because free_session() does not
Denis Vlasenko018b1552007-11-06 01:38:46 +0000585 * take master_fd into account when it finds new
Denis Vlasenko2450c452007-10-15 22:09:15 +0000586 * maxfd among remaining fd's */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200587 if (master_fd > G.maxfd)
588 G.maxfd = master_fd;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000589 }
590
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200591 {
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200592 struct timeval *tv_ptr = NULL;
Denys Vlasenko36df0482009-10-19 16:07:28 +0200593#if ENABLE_FEATURE_TELNETD_INETD_WAIT
594 struct timeval tv;
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200595 if ((opt & OPT_WAIT) && !G.sessions) {
596 tv.tv_sec = sec_linger;
597 tv.tv_usec = 0;
598 tv_ptr = &tv;
599 }
Denys Vlasenko36df0482009-10-19 16:07:28 +0200600#endif
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200601 count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, tv_ptr);
602 }
603 if (count == 0) /* "telnetd -w SEC" timed out */
604 return 0;
Denis Vlasenko10916c52007-10-15 17:28:00 +0000605 if (count < 0)
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000606 goto again; /* EINTR or ENOMEM */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000607
608#if ENABLE_FEATURE_TELNETD_STANDALONE
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200609 /* Check for and accept new sessions */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000610 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000611 int fd;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000612 struct tsession *new_ts;
613
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000614 fd = accept(master_fd, NULL, NULL);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000615 if (fd < 0)
616 goto again;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200617 close_on_exec_on(fd);
618
619 /* Create a new session and link it into active list */
620 new_ts = make_new_session(fd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000621 if (new_ts) {
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200622 new_ts->next = G.sessions;
623 G.sessions = new_ts;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000624 } else {
625 close(fd);
Eric Andersen08a72202002-09-30 20:52:10 +0000626 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000627 }
628#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000629
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200630 /* Then check for data tunneling */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200631 ts = G.sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000632 while (ts) { /* For all sessions... */
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200633 struct tsession *next = ts->next; /* in case we free ts */
Eric Andersen08a72202002-09-30 20:52:10 +0000634
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000635 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000636 int num_totty;
Denis Vlasenko10916c52007-10-15 17:28:00 +0000637 unsigned char *ptr;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200638 /* Write to pty from buffer 1 */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000639 ptr = remove_iacs(ts, &num_totty);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000640 count = safe_write(ts->ptyfd, ptr, num_totty);
641 if (count < 0) {
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000642 if (errno == EAGAIN)
643 goto skip1;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000644 goto kill_session;
Eric Andersen08a72202002-09-30 20:52:10 +0000645 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000646 ts->size1 -= count;
647 ts->wridx1 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000648 if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
Eric Andersen08a72202002-09-30 20:52:10 +0000649 ts->wridx1 = 0;
Eric Andersen08a72202002-09-30 20:52:10 +0000650 }
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000651 skip1:
652 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200653 /* Write to socket from buffer 2 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000654 count = MIN(BUFSIZE - ts->wridx2, ts->size2);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200655 count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000656 if (count < 0) {
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000657 if (errno == EAGAIN)
658 goto skip2;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000659 goto kill_session;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000660 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000661 ts->size2 -= count;
662 ts->wridx2 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000663 if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000664 ts->wridx2 = 0;
665 }
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000666 skip2:
Denis Vlasenko10916c52007-10-15 17:28:00 +0000667 /* Should not be needed, but... remove_iacs is actually buggy
668 * (it cannot process iacs which wrap around buffer's end)!
669 * Since properly fixing it requires writing bigger code,
Denis Vlasenkof472b232007-10-16 21:35:17 +0000670 * we rely instead on this code making it virtually impossible
Denis Vlasenko10916c52007-10-15 17:28:00 +0000671 * to have wrapped iac (people don't type at 2k/second).
672 * It also allows for bigger reads in common case. */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000673 if (ts->size1 == 0) {
674 ts->rdidx1 = 0;
675 ts->wridx1 = 0;
676 }
677 if (ts->size2 == 0) {
678 ts->rdidx2 = 0;
679 ts->wridx2 = 0;
680 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000681
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000682 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200683 /* Read from socket to buffer 1 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000684 count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200685 count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000686 if (count <= 0) {
687 if (count < 0 && errno == EAGAIN)
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000688 goto skip3;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000689 goto kill_session;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000690 }
691 /* Ignore trailing NUL if it is there */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200692 if (!TS_BUF1(ts)[ts->rdidx1 + count - 1]) {
Denis Vlasenkof472b232007-10-16 21:35:17 +0000693 --count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000694 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000695 ts->size1 += count;
696 ts->rdidx1 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000697 if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
698 ts->rdidx1 = 0;
699 }
700 skip3:
701 if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200702 /* Read from pty to buffer 2 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000703 count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200704 count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000705 if (count <= 0) {
706 if (count < 0 && errno == EAGAIN)
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000707 goto skip4;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000708 goto kill_session;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000709 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000710 ts->size2 += count;
711 ts->rdidx2 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000712 if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
713 ts->rdidx2 = 0;
714 }
715 skip4:
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000716 ts = next;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000717 continue;
718 kill_session:
Denys Vlasenko3a416112010-04-05 22:10:38 +0200719 if (ts->shell_pid > 0)
720 update_utmp(ts->shell_pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL);
Denis Vlasenkof472b232007-10-16 21:35:17 +0000721 free_session(ts);
722 ts = next;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000723 }
Denis Vlasenkof472b232007-10-16 21:35:17 +0000724
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000725 goto again;
Eric Andersen08a72202002-09-30 20:52:10 +0000726}