blob: 07c6a6a73861441790067c771b745105e538b6eb [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#if ENABLE_FEATURE_UTMP
35# include <utmp.h> /* LOGIN_PROCESS */
36#endif
37
38
Eric Andersen08a72202002-09-30 20:52:10 +000039struct tsession {
40 struct tsession *next;
Denis Vlasenkod814c982009-02-02 23:43:57 +000041 pid_t shell_pid;
Denys Vlasenko1d77db82009-06-10 13:38:08 +020042 int sockfd_read;
43 int sockfd_write;
44 int ptyfd;
Denis Vlasenko59d7c432007-10-15 15:19:36 +000045
Eric Andersen08a72202002-09-30 20:52:10 +000046 /* two circular buffers */
Denis Vlasenko59d7c432007-10-15 15:19:36 +000047 /*char *buf1, *buf2;*/
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020048/*#define TS_BUF1(ts) ts->buf1*/
49/*#define TS_BUF2(ts) TS_BUF2(ts)*/
50#define TS_BUF1(ts) ((unsigned char*)(ts + 1))
51#define TS_BUF2(ts) (((unsigned char*)(ts + 1)) + BUFSIZE)
Eric Andersen08a72202002-09-30 20:52:10 +000052 int rdidx1, wridx1, size1;
53 int rdidx2, wridx2, size2;
54};
55
Denis Vlasenko88308fe2007-06-25 10:35:11 +000056/* Two buffers are directly after tsession in malloced memory.
57 * Make whole thing fit in 4k */
Denis Vlasenko59d7c432007-10-15 15:19:36 +000058enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 };
Denis Vlasenko88308fe2007-06-25 10:35:11 +000059
Eric Andersen08a72202002-09-30 20:52:10 +000060
Denis Vlasenko59d7c432007-10-15 15:19:36 +000061/* Globals */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020062struct globals {
63 struct tsession *sessions;
64 const char *loginpath;
65 const char *issuefile;
66 int maxfd;
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +010067} FIX_ALIASING;
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020068#define G (*(struct globals*)&bb_common_bufsiz1)
69#define INIT_G() do { \
70 G.loginpath = "/bin/login"; \
71 G.issuefile = "/etc/issue.net"; \
72} while (0)
Eric Andersen08a72202002-09-30 20:52:10 +000073
74
75/*
Denis Vlasenko59d7c432007-10-15 15:19:36 +000076 Remove all IAC's from buf1 (received IACs are ignored and must be removed
77 so as to not be interpreted by the terminal). Make an uninterrupted
78 string of characters fit for the terminal. Do this by packing
79 all characters meant for the terminal sequentially towards the end of buf.
Eric Andersen08a72202002-09-30 20:52:10 +000080
Denys Vlasenko4c721042010-04-04 23:45:09 +020081 Return a pointer to the beginning of the characters meant for the terminal
Eric Andersen08a72202002-09-30 20:52:10 +000082 and make *num_totty the number of characters that should be sent to
83 the terminal.
84
Denys Vlasenko4c721042010-04-04 23:45:09 +020085 Note - if an IAC (3 byte quantity) starts before (bf + len) but extends
Denis Vlasenko59d7c432007-10-15 15:19:36 +000086 past (bf + len) then that IAC will be left unprocessed and *processed
87 will be less than len.
Eric Andersen08a72202002-09-30 20:52:10 +000088
Denis Vlasenko10916c52007-10-15 17:28:00 +000089 CR-LF ->'s CR mapping is also done here, for convenience.
90
91 NB: may fail to remove iacs which wrap around buffer!
Denis Vlasenko75f8d082006-11-22 15:54:52 +000092 */
Denis Vlasenko10916c52007-10-15 17:28:00 +000093static unsigned char *
Denis Vlasenko75f8d082006-11-22 15:54:52 +000094remove_iacs(struct tsession *ts, int *pnum_totty)
95{
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020096 unsigned char *ptr0 = TS_BUF1(ts) + ts->wridx1;
Eric Andersen08a72202002-09-30 20:52:10 +000097 unsigned char *ptr = ptr0;
98 unsigned char *totty = ptr;
99 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
Eric Andersen08a72202002-09-30 20:52:10 +0000100 int num_totty;
101
102 while (ptr < end) {
103 if (*ptr != IAC) {
Denis Vlasenko10916c52007-10-15 17:28:00 +0000104 char c = *ptr;
105
106 *totty++ = c;
107 ptr++;
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000108 /* We map \r\n ==> \r for pragmatic reasons.
Eric Andersen3752d332003-12-19 11:30:13 +0000109 * Many client implementations send \r\n when
110 * the user hits the CarriageReturn key.
111 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000112 if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0'))
Eric Andersen3752d332003-12-19 11:30:13 +0000113 ptr++;
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000114 continue;
Eric Andersen08a72202002-09-30 20:52:10 +0000115 }
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000116
117 if ((ptr+1) >= end)
118 break;
119 if (ptr[1] == NOP) { /* Ignore? (putty keepalive, etc.) */
120 ptr += 2;
121 continue;
122 }
123 if (ptr[1] == IAC) { /* Literal IAC? (emacs M-DEL) */
124 *totty++ = ptr[1];
125 ptr += 2;
126 continue;
127 }
128
129 /*
130 * TELOPT_NAWS support!
131 */
132 if ((ptr+2) >= end) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200133 /* Only the beginning of the IAC is in the
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000134 buffer we were asked to process, we can't
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200135 process this char */
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000136 break;
137 }
138 /*
139 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
140 */
141 if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
142 struct winsize ws;
143 if ((ptr+8) >= end)
Denys Vlasenkofb132e42010-10-29 11:46:52 +0200144 break; /* incomplete, can't process */
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000145 ws.ws_col = (ptr[3] << 8) | ptr[4];
146 ws.ws_row = (ptr[5] << 8) | ptr[6];
147 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
148 ptr += 9;
149 continue;
150 }
151 /* skip 3-byte IAC non-SB cmd */
152#if DEBUG
153 fprintf(stderr, "Ignoring IAC %s,%s\n",
154 TELCMD(ptr[1]), TELOPT(ptr[2]));
155#endif
156 ptr += 3;
Eric Andersen08a72202002-09-30 20:52:10 +0000157 }
158
Denis Vlasenko0de37e12007-10-17 11:08:53 +0000159 num_totty = totty - ptr0;
Eric Andersen08a72202002-09-30 20:52:10 +0000160 *pnum_totty = num_totty;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200161 /* The difference between ptr and totty is number of iacs
162 we removed from the stream. Adjust buf1 accordingly */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000163 if ((ptr - totty) == 0) /* 99.999% of cases */
164 return ptr0;
165 ts->wridx1 += ptr - totty;
166 ts->size1 -= ptr - totty;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200167 /* Move chars meant for the terminal towards the end of the buffer */
Eric Andersen08a72202002-09-30 20:52:10 +0000168 return memmove(ptr - num_totty, ptr0, num_totty);
169}
170
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000171/*
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000172 * Converting single IAC into double on output
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000173 */
174static size_t iac_safe_write(int fd, const char *buf, size_t count)
175{
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000176 const char *IACptr;
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000177 size_t wr, rc, total;
178
179 total = 0;
180 while (1) {
181 if (count == 0)
182 return total;
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000183 if (*buf == (char)IAC) {
184 static const char IACIAC[] ALIGN1 = { IAC, IAC };
185 rc = safe_write(fd, IACIAC, 2);
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000186 if (rc != 2)
187 break;
188 buf++;
189 total++;
190 count--;
191 continue;
192 }
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000193 /* count != 0, *buf != IAC */
194 IACptr = memchr(buf, IAC, count);
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000195 wr = count;
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000196 if (IACptr)
197 wr = IACptr - buf;
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000198 rc = safe_write(fd, buf, wr);
199 if (rc != wr)
200 break;
201 buf += rc;
202 total += rc;
203 count -= rc;
204 }
205 /* here: rc - result of last short write */
206 if ((ssize_t)rc < 0) { /* error? */
207 if (total == 0)
208 return rc;
209 rc = 0;
210 }
211 return total + rc;
212}
Eric Andersen08a72202002-09-30 20:52:10 +0000213
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200214/* Must match getopt32 string */
215enum {
216 OPT_WATCHCHILD = (1 << 2), /* -K */
217 OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200218 OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p PORT */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200219 OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200220 OPT_SYSLOG = (1 << 7) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -S */
221 OPT_WAIT = (1 << 8) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -w SEC */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200222};
223
Eric Andersen08a72202002-09-30 20:52:10 +0000224static struct tsession *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000225make_new_session(
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200226 IF_FEATURE_TELNETD_STANDALONE(int sock)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000227 IF_NOT_FEATURE_TELNETD_STANDALONE(void)
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000228) {
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200229#if !ENABLE_FEATURE_TELNETD_STANDALONE
Denys Vlasenkoff0e8752010-05-10 04:16:43 +0200230 enum { sock = 0 };
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200231#endif
Denis Vlasenko88308fe2007-06-25 10:35:11 +0000232 const char *login_argv[2];
Eric Andersen08a72202002-09-30 20:52:10 +0000233 struct termios termbuf;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000234 int fd, pid;
Denis Vlasenko85c24712008-03-17 09:04:04 +0000235 char tty_name[GETPTY_BUFSIZE];
Rob Landley1ec5b292006-05-29 07:42:02 +0000236 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
Eric Andersen08a72202002-09-30 20:52:10 +0000237
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000238 /*ts->buf1 = (char *)(ts + 1);*/
239 /*ts->buf2 = ts->buf1 + BUFSIZE;*/
Eric Andersen08a72202002-09-30 20:52:10 +0000240
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200241 /* Got a new connection, set up a tty */
Bernhard Reutner-Fischerae4342c2008-05-19 08:18:50 +0000242 fd = xgetpty(tty_name);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200243 if (fd > G.maxfd)
244 G.maxfd = fd;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000245 ts->ptyfd = fd;
246 ndelay_on(fd);
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200247 close_on_exec_on(fd);
248
Denis Vlasenko6d044352008-11-09 00:44:40 +0000249 /* SO_KEEPALIVE by popular demand */
250 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200251#if ENABLE_FEATURE_TELNETD_STANDALONE
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200252 ts->sockfd_read = sock;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000253 ndelay_on(sock);
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200254 if (sock == 0) { /* We are called with fd 0 - we are in inetd mode */
Denis Vlasenko9e237672007-10-17 11:18:49 +0000255 sock++; /* so use fd 1 for output */
256 ndelay_on(sock);
257 }
Denis Vlasenkof472b232007-10-16 21:35:17 +0000258 ts->sockfd_write = sock;
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200259 if (sock > G.maxfd)
260 G.maxfd = sock;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000261#else
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000262 /* ts->sockfd_read = 0; - done by xzalloc */
Denis Vlasenkof472b232007-10-16 21:35:17 +0000263 ts->sockfd_write = 1;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000264 ndelay_on(0);
265 ndelay_on(1);
266#endif
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200267
Eric Andersen08a72202002-09-30 20:52:10 +0000268 /* Make the telnet client understand we will echo characters so it
269 * should not do it locally. We don't tell the client to run linemode,
270 * because we want to handle line editing and tab completion and other
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000271 * stuff that requires char-by-char support. */
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000272 {
273 static const char iacs_to_send[] ALIGN1 = {
274 IAC, DO, TELOPT_ECHO,
275 IAC, DO, TELOPT_NAWS,
Denys Vlasenkofb132e42010-10-29 11:46:52 +0200276 /* This requires telnetd.ctrlSQ.patch (incomplete) */
277 /*IAC, DO, TELOPT_LFLOW,*/
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000278 IAC, WILL, TELOPT_ECHO,
279 IAC, WILL, TELOPT_SGA
280 };
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000281 /* This confuses iac_safe_write(), it will try to duplicate
282 * each IAC... */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200283 //memcpy(TS_BUF2(ts), iacs_to_send, sizeof(iacs_to_send));
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000284 //ts->rdidx2 = sizeof(iacs_to_send);
285 //ts->size2 = sizeof(iacs_to_send);
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000286 /* So just stuff it into TCP stream! (no error check...) */
287#if ENABLE_FEATURE_TELNETD_STANDALONE
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000288 safe_write(sock, iacs_to_send, sizeof(iacs_to_send));
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000289#else
290 safe_write(1, iacs_to_send, sizeof(iacs_to_send));
291#endif
292 /*ts->rdidx2 = 0; - xzalloc did it */
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000293 /*ts->size2 = 0;*/
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000294 }
Eric Andersen08a72202002-09-30 20:52:10 +0000295
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100296 fflush_all();
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000297 pid = vfork(); /* NOMMU-friendly */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000298 if (pid < 0) {
299 free(ts);
300 close(fd);
Denis Vlasenko23c81282007-10-16 22:01:23 +0000301 /* sock will be closed by caller */
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000302 bb_perror_msg("vfork");
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000303 return NULL;
Eric Andersen08a72202002-09-30 20:52:10 +0000304 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000305 if (pid > 0) {
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000306 /* Parent */
Denis Vlasenko2450c452007-10-15 22:09:15 +0000307 ts->shell_pid = pid;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000308 return ts;
Eric Andersen08a72202002-09-30 20:52:10 +0000309 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000310
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000311 /* Child */
312 /* Careful - we are after vfork! */
Eric Andersen08a72202002-09-30 20:52:10 +0000313
Denis Vlasenkod814c982009-02-02 23:43:57 +0000314 /* Restore default signal handling ASAP */
Denis Vlasenko25591c32008-02-16 22:58:56 +0000315 bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000316
Denys Vlasenko58c3d212010-11-30 09:17:30 +0100317 pid = getpid();
318
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200319 if (ENABLE_FEATURE_UTMP) {
320 len_and_sockaddr *lsa = get_peer_lsa(sock);
321 char *hostname = NULL;
322 if (lsa) {
323 hostname = xmalloc_sockaddr2dotted(&lsa->u.sa);
324 free(lsa);
325 }
326 write_new_utmp(pid, LOGIN_PROCESS, tty_name, /*username:*/ "LOGIN", hostname);
327 free(hostname);
328 }
329
Denis Vlasenkod814c982009-02-02 23:43:57 +0000330 /* Make new session and process group */
331 setsid();
332
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200333 /* Open the child's side of the tty */
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +0000334 /* NB: setsid() disconnects from any previous ctty's. Therefore
335 * we must open child's side of the tty AFTER setsid! */
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200336 close(0);
Denis Vlasenko1101d1c2008-07-21 09:22:28 +0000337 xopen(tty_name, O_RDWR); /* becomes our ctty */
338 xdup2(0, 1);
339 xdup2(0, 2);
Denys Vlasenko3a416112010-04-05 22:10:38 +0200340 tcsetpgrp(0, pid); /* switch this tty's process group to us */
341
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200342 /* The pseudo-terminal allocated to the client is configured to operate
343 * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000344 tcgetattr(0, &termbuf);
345 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000346 termbuf.c_oflag |= ONLCR | XTABS;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000347 termbuf.c_iflag |= ICRNL;
348 termbuf.c_iflag &= ~IXOFF;
349 /*termbuf.c_lflag &= ~ICANON;*/
Denis Vlasenko202ac502008-11-05 13:20:58 +0000350 tcsetattr_stdin_TCSANOW(&termbuf);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000351
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100352 /* Uses FILE-based I/O to stdout, but does fflush_all(),
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000353 * so should be safe with vfork.
354 * I fear, though, that some users will have ridiculously big
Denis Vlasenko018b1552007-11-06 01:38:46 +0000355 * issue files, and they may block writing to fd 1,
356 * (parent is supposed to read it, but parent waits
357 * for vforked child to exec!) */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200358 print_login_issue(G.issuefile, tty_name);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000359
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000360 /* Exec shell / login / whatever */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200361 login_argv[0] = G.loginpath;
Denis Vlasenko88308fe2007-06-25 10:35:11 +0000362 login_argv[1] = NULL;
Denis Vlasenko018b1552007-11-06 01:38:46 +0000363 /* exec busybox applet (if PREFER_APPLETS=y), if that fails,
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200364 * exec external program.
365 * NB: sock is either 0 or has CLOEXEC set on it.
366 * fd has CLOEXEC set on it too. These two fds will be closed here.
367 */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200368 BB_EXECVP(G.loginpath, (char **)login_argv);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000369 /* _exit is safer with vfork, and we shouldn't send message
Denis Vlasenko2450c452007-10-15 22:09:15 +0000370 * to remote clients anyway */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200371 _exit(EXIT_FAILURE); /*bb_perror_msg_and_die("execv %s", G.loginpath);*/
Eric Andersen08a72202002-09-30 20:52:10 +0000372}
373
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000374#if ENABLE_FEATURE_TELNETD_STANDALONE
375
Eric Andersen08a72202002-09-30 20:52:10 +0000376static void
377free_session(struct tsession *ts)
378{
Denys Vlasenko3a416112010-04-05 22:10:38 +0200379 struct tsession *t;
Eric Andersen08a72202002-09-30 20:52:10 +0000380
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000381 if (option_mask32 & OPT_INETD)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000382 exit(EXIT_SUCCESS);
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000383
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000384 /* Unlink this telnet session from the session list */
Denys Vlasenko3a416112010-04-05 22:10:38 +0200385 t = G.sessions;
Mike Frysinger731f81c2006-05-10 15:23:12 +0000386 if (t == ts)
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200387 G.sessions = ts->next;
Eric Andersen08a72202002-09-30 20:52:10 +0000388 else {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000389 while (t->next != ts)
Eric Andersen08a72202002-09-30 20:52:10 +0000390 t = t->next;
391 t->next = ts->next;
392 }
393
Denis Vlasenkof472b232007-10-16 21:35:17 +0000394#if 0
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000395 /* It was said that "normal" telnetd just closes ptyfd,
396 * doesn't send SIGKILL. When we close ptyfd,
397 * kernel sends SIGHUP to processes having slave side opened. */
Denis Vlasenkof472b232007-10-16 21:35:17 +0000398 kill(ts->shell_pid, SIGKILL);
Denis Vlasenkod814c982009-02-02 23:43:57 +0000399 waitpid(ts->shell_pid, NULL, 0);
Denis Vlasenkof472b232007-10-16 21:35:17 +0000400#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000401 close(ts->ptyfd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000402 close(ts->sockfd_read);
Denis Vlasenkof472b232007-10-16 21:35:17 +0000403 /* We do not need to close(ts->sockfd_write), it's the same
404 * as sockfd_read unless we are in inetd mode. But in inetd mode
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000405 * we do not reach this */
Eric Andersen08a72202002-09-30 20:52:10 +0000406 free(ts);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000407
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000408 /* Scan all sessions and find new maxfd */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200409 G.maxfd = 0;
410 ts = G.sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000411 while (ts) {
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200412 if (G.maxfd < ts->ptyfd)
413 G.maxfd = ts->ptyfd;
414 if (G.maxfd < ts->sockfd_read)
415 G.maxfd = ts->sockfd_read;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000416#if 0
417 /* Again, sockfd_write == sockfd_read here */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200418 if (G.maxfd < ts->sockfd_write)
419 G.maxfd = ts->sockfd_write;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000420#endif
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000421 ts = ts->next;
422 }
Eric Andersen08a72202002-09-30 20:52:10 +0000423}
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000424
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000425#else /* !FEATURE_TELNETD_STANDALONE */
426
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000427/* Used in main() only, thus "return 0" actually is exit(EXIT_SUCCESS). */
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000428#define free_session(ts) return 0
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000429
430#endif
431
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000432static void handle_sigchld(int sig UNUSED_PARAM)
Denis Vlasenko2450c452007-10-15 22:09:15 +0000433{
434 pid_t pid;
435 struct tsession *ts;
Denys Vlasenko3a416112010-04-05 22:10:38 +0200436 int save_errno = errno;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000437
Denis Vlasenko018b1552007-11-06 01:38:46 +0000438 /* Looping: more than one child may have exited */
439 while (1) {
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000440 pid = wait_any_nohang(NULL);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000441 if (pid <= 0)
442 break;
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200443 ts = G.sessions;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000444 while (ts) {
445 if (ts->shell_pid == pid) {
446 ts->shell_pid = -1;
Denys Vlasenko3a416112010-04-05 22:10:38 +0200447// man utmp:
448// When init(8) finds that a process has exited, it locates its utmp entry
449// by ut_pid, sets ut_type to DEAD_PROCESS, and clears ut_user, ut_host
450// and ut_time with null bytes.
451// [same applies to other processes which maintain utmp entries, like telnetd]
452//
453// We do not bother actually clearing fields:
454// it might be interesting to know who was logged in and from where
455 update_utmp(pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000456 break;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000457 }
458 ts = ts->next;
459 }
460 }
Denys Vlasenko3a416112010-04-05 22:10:38 +0200461
462 errno = save_errno;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000463}
464
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000465int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000466int telnetd_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen08a72202002-09-30 20:52:10 +0000467{
Eric Andersen08a72202002-09-30 20:52:10 +0000468 fd_set rdfdset, wrfdset;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000469 unsigned opt;
Denis Vlasenko10916c52007-10-15 17:28:00 +0000470 int count;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000471 struct tsession *ts;
472#if ENABLE_FEATURE_TELNETD_STANDALONE
473#define IS_INETD (opt & OPT_INETD)
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200474 int master_fd = master_fd; /* for compiler */
475 int sec_linger = sec_linger;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000476 char *opt_bindaddr = NULL;
477 char *opt_portnbr;
478#else
479 enum {
480 IS_INETD = 1,
481 master_fd = -1,
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000482 };
Glenn L McGrathc2b91862003-09-12 11:27:15 +0000483#endif
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200484 INIT_G();
485
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200486 /* -w NUM, and implies -F. -w and -i don't mix */
487 IF_FEATURE_TELNETD_INETD_WAIT(opt_complementary = "wF:w+:i--w:w--i";)
Denis Vlasenko2450c452007-10-15 22:09:15 +0000488 /* Even if !STANDALONE, we accept (and ignore) -i, thus people
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000489 * don't need to guess whether it's ok to pass -i to us */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200490 opt = getopt32(argv, "f:l:Ki"
491 IF_FEATURE_TELNETD_STANDALONE("p:b:F")
492 IF_FEATURE_TELNETD_INETD_WAIT("Sw:"),
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200493 &G.issuefile, &G.loginpath
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200494 IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)
495 IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger)
496 );
Denis Vlasenko2450c452007-10-15 22:09:15 +0000497 if (!IS_INETD /*&& !re_execed*/) {
498 /* inform that we start in standalone mode?
499 * May be useful when people forget to give -i */
500 /*bb_error_msg("listening for connections");*/
501 if (!(opt & OPT_FOREGROUND)) {
502 /* DAEMON_CHDIR_ROOT was giving inconsistent
Denis Vlasenko008eda22007-10-16 10:47:27 +0000503 * behavior with/without -F, -i */
Denis Vlasenko018b1552007-11-06 01:38:46 +0000504 bb_daemonize_or_rexec(0 /*was DAEMON_CHDIR_ROOT*/, argv);
Denis Vlasenko2450c452007-10-15 22:09:15 +0000505 }
506 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000507 /* Redirect log to syslog early, if needed */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200508 if (IS_INETD || (opt & OPT_SYSLOG) || !(opt & OPT_FOREGROUND)) {
Denis Vlasenko5e4fda02009-03-08 23:46:48 +0000509 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000510 logmode = LOGMODE_SYSLOG;
511 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000512#if ENABLE_FEATURE_TELNETD_STANDALONE
513 if (IS_INETD) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200514 G.sessions = make_new_session(0);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200515 if (!G.sessions) /* pty opening or vfork problem, exit */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200516 return 1; /* make_new_session printed error message */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000517 } else {
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200518 master_fd = 0;
519 if (!(opt & OPT_WAIT)) {
520 unsigned portnbr = 23;
521 if (opt & OPT_PORT)
522 portnbr = xatou16(opt_portnbr);
523 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
524 xlisten(master_fd, 1);
525 }
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200526 close_on_exec_on(master_fd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000527 }
Rob Landley00e76cb2005-05-10 23:53:33 +0000528#else
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200529 G.sessions = make_new_session();
530 if (!G.sessions) /* pty opening or vfork problem, exit */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200531 return 1; /* make_new_session printed error message */
Rob Landley00e76cb2005-05-10 23:53:33 +0000532#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000533
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000534 /* We don't want to die if just one session is broken */
535 signal(SIGPIPE, SIG_IGN);
Eric Andersen08a72202002-09-30 20:52:10 +0000536
Denis Vlasenko2450c452007-10-15 22:09:15 +0000537 if (opt & OPT_WATCHCHILD)
538 signal(SIGCHLD, handle_sigchld);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000539 else /* prevent dead children from becoming zombies */
540 signal(SIGCHLD, SIG_IGN);
Denis Vlasenko2450c452007-10-15 22:09:15 +0000541
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000542/*
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200543 This is how the buffers are used. The arrows indicate data flow.
544
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000545 +-------+ wridx1++ +------+ rdidx1++ +----------+
546 | | <-------------- | buf1 | <-------------- | |
547 | | size1-- +------+ size1++ | |
548 | pty | | socket |
549 | | rdidx2++ +------+ wridx2++ | |
550 | | --------------> | buf2 | --------------> | |
551 +-------+ size2++ +------+ size2-- +----------+
552
553 size1: "how many bytes are buffered for pty between rdidx1 and wridx1?"
554 size2: "how many bytes are buffered for socket between rdidx2 and wridx2?"
555
556 Each session has got two buffers. Buffers are circular. If sizeN == 0,
557 buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases
558 rdidxN == wridxN.
559*/
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000560 again:
561 FD_ZERO(&rdfdset);
562 FD_ZERO(&wrfdset);
Eric Andersen08a72202002-09-30 20:52:10 +0000563
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000564 /* Select on the master socket, all telnet sockets and their
565 * ptys if there is room in their session buffers.
566 * NB: scalability problem: we recalculate entire bitmap
567 * before each select. Can be a problem with 500+ connections. */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200568 ts = G.sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000569 while (ts) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200570 struct tsession *next = ts->next; /* in case we free ts */
Denis Vlasenko2450c452007-10-15 22:09:15 +0000571 if (ts->shell_pid == -1) {
Denis Vlasenko018b1552007-11-06 01:38:46 +0000572 /* Child died and we detected that */
Denis Vlasenko2450c452007-10-15 22:09:15 +0000573 free_session(ts);
574 } else {
575 if (ts->size1 > 0) /* can write to pty */
576 FD_SET(ts->ptyfd, &wrfdset);
577 if (ts->size1 < BUFSIZE) /* can read from socket */
578 FD_SET(ts->sockfd_read, &rdfdset);
579 if (ts->size2 > 0) /* can write to socket */
580 FD_SET(ts->sockfd_write, &wrfdset);
581 if (ts->size2 < BUFSIZE) /* can read from pty */
582 FD_SET(ts->ptyfd, &rdfdset);
583 }
584 ts = next;
585 }
586 if (!IS_INETD) {
587 FD_SET(master_fd, &rdfdset);
588 /* This is needed because free_session() does not
Denis Vlasenko018b1552007-11-06 01:38:46 +0000589 * take master_fd into account when it finds new
Denis Vlasenko2450c452007-10-15 22:09:15 +0000590 * maxfd among remaining fd's */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200591 if (master_fd > G.maxfd)
592 G.maxfd = master_fd;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000593 }
594
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200595 {
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200596 struct timeval *tv_ptr = NULL;
Denys Vlasenko36df0482009-10-19 16:07:28 +0200597#if ENABLE_FEATURE_TELNETD_INETD_WAIT
598 struct timeval tv;
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200599 if ((opt & OPT_WAIT) && !G.sessions) {
600 tv.tv_sec = sec_linger;
601 tv.tv_usec = 0;
602 tv_ptr = &tv;
603 }
Denys Vlasenko36df0482009-10-19 16:07:28 +0200604#endif
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200605 count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, tv_ptr);
606 }
607 if (count == 0) /* "telnetd -w SEC" timed out */
608 return 0;
Denis Vlasenko10916c52007-10-15 17:28:00 +0000609 if (count < 0)
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000610 goto again; /* EINTR or ENOMEM */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000611
612#if ENABLE_FEATURE_TELNETD_STANDALONE
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200613 /* Check for and accept new sessions */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000614 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000615 int fd;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000616 struct tsession *new_ts;
617
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000618 fd = accept(master_fd, NULL, NULL);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000619 if (fd < 0)
620 goto again;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200621 close_on_exec_on(fd);
622
623 /* Create a new session and link it into active list */
624 new_ts = make_new_session(fd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000625 if (new_ts) {
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200626 new_ts->next = G.sessions;
627 G.sessions = new_ts;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000628 } else {
629 close(fd);
Eric Andersen08a72202002-09-30 20:52:10 +0000630 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000631 }
632#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000633
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200634 /* Then check for data tunneling */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200635 ts = G.sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000636 while (ts) { /* For all sessions... */
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200637 struct tsession *next = ts->next; /* in case we free ts */
Eric Andersen08a72202002-09-30 20:52:10 +0000638
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000639 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000640 int num_totty;
Denis Vlasenko10916c52007-10-15 17:28:00 +0000641 unsigned char *ptr;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200642 /* Write to pty from buffer 1 */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000643 ptr = remove_iacs(ts, &num_totty);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000644 count = safe_write(ts->ptyfd, ptr, num_totty);
645 if (count < 0) {
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000646 if (errno == EAGAIN)
647 goto skip1;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000648 goto kill_session;
Eric Andersen08a72202002-09-30 20:52:10 +0000649 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000650 ts->size1 -= count;
651 ts->wridx1 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000652 if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
Eric Andersen08a72202002-09-30 20:52:10 +0000653 ts->wridx1 = 0;
Eric Andersen08a72202002-09-30 20:52:10 +0000654 }
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000655 skip1:
656 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200657 /* Write to socket from buffer 2 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000658 count = MIN(BUFSIZE - ts->wridx2, ts->size2);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200659 count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000660 if (count < 0) {
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000661 if (errno == EAGAIN)
662 goto skip2;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000663 goto kill_session;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000664 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000665 ts->size2 -= count;
666 ts->wridx2 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000667 if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000668 ts->wridx2 = 0;
669 }
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000670 skip2:
Denis Vlasenko10916c52007-10-15 17:28:00 +0000671 /* Should not be needed, but... remove_iacs is actually buggy
672 * (it cannot process iacs which wrap around buffer's end)!
673 * Since properly fixing it requires writing bigger code,
Denis Vlasenkof472b232007-10-16 21:35:17 +0000674 * we rely instead on this code making it virtually impossible
Denis Vlasenko10916c52007-10-15 17:28:00 +0000675 * to have wrapped iac (people don't type at 2k/second).
676 * It also allows for bigger reads in common case. */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000677 if (ts->size1 == 0) {
678 ts->rdidx1 = 0;
679 ts->wridx1 = 0;
680 }
681 if (ts->size2 == 0) {
682 ts->rdidx2 = 0;
683 ts->wridx2 = 0;
684 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000685
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000686 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200687 /* Read from socket to buffer 1 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000688 count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200689 count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000690 if (count <= 0) {
691 if (count < 0 && errno == EAGAIN)
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000692 goto skip3;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000693 goto kill_session;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000694 }
695 /* Ignore trailing NUL if it is there */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200696 if (!TS_BUF1(ts)[ts->rdidx1 + count - 1]) {
Denis Vlasenkof472b232007-10-16 21:35:17 +0000697 --count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000698 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000699 ts->size1 += count;
700 ts->rdidx1 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000701 if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
702 ts->rdidx1 = 0;
703 }
704 skip3:
705 if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200706 /* Read from pty to buffer 2 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000707 count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200708 count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000709 if (count <= 0) {
710 if (count < 0 && errno == EAGAIN)
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000711 goto skip4;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000712 goto kill_session;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000713 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000714 ts->size2 += count;
715 ts->rdidx2 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000716 if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
717 ts->rdidx2 = 0;
718 }
719 skip4:
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000720 ts = next;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000721 continue;
722 kill_session:
Denys Vlasenko3a416112010-04-05 22:10:38 +0200723 if (ts->shell_pid > 0)
724 update_utmp(ts->shell_pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL);
Denis Vlasenkof472b232007-10-16 21:35:17 +0000725 free_session(ts);
726 ts = next;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000727 }
Denis Vlasenkof472b232007-10-16 21:35:17 +0000728
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000729 goto again;
Eric Andersen08a72202002-09-30 20:52:10 +0000730}