blob: 33020f1b416436d6ad1315459704637401ca4a40 [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 */
Pere Orga5bc8c002011-04-11 03:29:49 +020023
24//usage:#define telnetd_trivial_usage
25//usage: "[OPTIONS]"
26//usage:#define telnetd_full_usage "\n\n"
27//usage: "Handle incoming telnet connections"
28//usage: IF_NOT_FEATURE_TELNETD_STANDALONE(" via inetd") "\n"
Pere Orga5bc8c002011-04-11 03:29:49 +020029//usage: "\n -l LOGIN Exec LOGIN on connect"
30//usage: "\n -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue"
31//usage: "\n -K Close connection as soon as login exits"
32//usage: "\n (normally wait until all programs close slave pty)"
33//usage: IF_FEATURE_TELNETD_STANDALONE(
34//usage: "\n -p PORT Port to listen on"
35//usage: "\n -b ADDR[:PORT] Address to bind to"
36//usage: "\n -F Run in foreground"
37//usage: "\n -i Inetd mode"
38//usage: IF_FEATURE_TELNETD_INETD_WAIT(
39//usage: "\n -w SEC Inetd 'wait' mode, linger time SEC"
40//usage: "\n -S Log to syslog (implied by -i or without -F and -w)"
41//usage: )
42//usage: )
43
Denis Vlasenko75f8d082006-11-22 15:54:52 +000044#define DEBUG 0
Eric Andersen08a72202002-09-30 20:52:10 +000045
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000046#include "libbb.h"
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +000047#include <syslog.h>
Rob Landley099ed502006-08-28 09:41:49 +000048
Denis Vlasenko75f8d082006-11-22 15:54:52 +000049#if DEBUG
Denys Vlasenko3a416112010-04-05 22:10:38 +020050# define TELCMDS
51# define TELOPTS
Eric Andersen08a72202002-09-30 20:52:10 +000052#endif
53#include <arpa/telnet.h>
Eric Andersen08a72202002-09-30 20:52:10 +000054
Denys Vlasenko3a416112010-04-05 22:10:38 +020055
Eric Andersen08a72202002-09-30 20:52:10 +000056struct tsession {
57 struct tsession *next;
Denis Vlasenkod814c982009-02-02 23:43:57 +000058 pid_t shell_pid;
Denys Vlasenko1d77db82009-06-10 13:38:08 +020059 int sockfd_read;
60 int sockfd_write;
61 int ptyfd;
Denis Vlasenko59d7c432007-10-15 15:19:36 +000062
Eric Andersen08a72202002-09-30 20:52:10 +000063 /* two circular buffers */
Denis Vlasenko59d7c432007-10-15 15:19:36 +000064 /*char *buf1, *buf2;*/
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020065/*#define TS_BUF1(ts) ts->buf1*/
66/*#define TS_BUF2(ts) TS_BUF2(ts)*/
67#define TS_BUF1(ts) ((unsigned char*)(ts + 1))
68#define TS_BUF2(ts) (((unsigned char*)(ts + 1)) + BUFSIZE)
Eric Andersen08a72202002-09-30 20:52:10 +000069 int rdidx1, wridx1, size1;
70 int rdidx2, wridx2, size2;
71};
72
Denis Vlasenko88308fe2007-06-25 10:35:11 +000073/* Two buffers are directly after tsession in malloced memory.
74 * Make whole thing fit in 4k */
Denis Vlasenko59d7c432007-10-15 15:19:36 +000075enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 };
Denis Vlasenko88308fe2007-06-25 10:35:11 +000076
Eric Andersen08a72202002-09-30 20:52:10 +000077
Denis Vlasenko59d7c432007-10-15 15:19:36 +000078/* Globals */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020079struct globals {
80 struct tsession *sessions;
81 const char *loginpath;
82 const char *issuefile;
83 int maxfd;
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +010084} FIX_ALIASING;
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020085#define G (*(struct globals*)&bb_common_bufsiz1)
86#define INIT_G() do { \
87 G.loginpath = "/bin/login"; \
88 G.issuefile = "/etc/issue.net"; \
89} while (0)
Eric Andersen08a72202002-09-30 20:52:10 +000090
91
92/*
Denis Vlasenko59d7c432007-10-15 15:19:36 +000093 Remove all IAC's from buf1 (received IACs are ignored and must be removed
94 so as to not be interpreted by the terminal). Make an uninterrupted
95 string of characters fit for the terminal. Do this by packing
96 all characters meant for the terminal sequentially towards the end of buf.
Eric Andersen08a72202002-09-30 20:52:10 +000097
Denys Vlasenko4c721042010-04-04 23:45:09 +020098 Return a pointer to the beginning of the characters meant for the terminal
Eric Andersen08a72202002-09-30 20:52:10 +000099 and make *num_totty the number of characters that should be sent to
100 the terminal.
101
Denys Vlasenko4c721042010-04-04 23:45:09 +0200102 Note - if an IAC (3 byte quantity) starts before (bf + len) but extends
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000103 past (bf + len) then that IAC will be left unprocessed and *processed
104 will be less than len.
Eric Andersen08a72202002-09-30 20:52:10 +0000105
Denis Vlasenko10916c52007-10-15 17:28:00 +0000106 CR-LF ->'s CR mapping is also done here, for convenience.
107
108 NB: may fail to remove iacs which wrap around buffer!
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000109 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000110static unsigned char *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000111remove_iacs(struct tsession *ts, int *pnum_totty)
112{
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200113 unsigned char *ptr0 = TS_BUF1(ts) + ts->wridx1;
Eric Andersen08a72202002-09-30 20:52:10 +0000114 unsigned char *ptr = ptr0;
115 unsigned char *totty = ptr;
116 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
Eric Andersen08a72202002-09-30 20:52:10 +0000117 int num_totty;
118
119 while (ptr < end) {
120 if (*ptr != IAC) {
Denis Vlasenko10916c52007-10-15 17:28:00 +0000121 char c = *ptr;
122
123 *totty++ = c;
124 ptr++;
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000125 /* We map \r\n ==> \r for pragmatic reasons.
Eric Andersen3752d332003-12-19 11:30:13 +0000126 * Many client implementations send \r\n when
127 * the user hits the CarriageReturn key.
128 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000129 if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0'))
Eric Andersen3752d332003-12-19 11:30:13 +0000130 ptr++;
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000131 continue;
Eric Andersen08a72202002-09-30 20:52:10 +0000132 }
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000133
134 if ((ptr+1) >= end)
135 break;
136 if (ptr[1] == NOP) { /* Ignore? (putty keepalive, etc.) */
137 ptr += 2;
138 continue;
139 }
140 if (ptr[1] == IAC) { /* Literal IAC? (emacs M-DEL) */
141 *totty++ = ptr[1];
142 ptr += 2;
143 continue;
144 }
145
146 /*
147 * TELOPT_NAWS support!
148 */
149 if ((ptr+2) >= end) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200150 /* Only the beginning of the IAC is in the
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000151 buffer we were asked to process, we can't
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200152 process this char */
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000153 break;
154 }
155 /*
156 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
157 */
158 if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
159 struct winsize ws;
160 if ((ptr+8) >= end)
Denys Vlasenkofb132e42010-10-29 11:46:52 +0200161 break; /* incomplete, can't process */
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000162 ws.ws_col = (ptr[3] << 8) | ptr[4];
163 ws.ws_row = (ptr[5] << 8) | ptr[6];
164 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
165 ptr += 9;
166 continue;
167 }
168 /* skip 3-byte IAC non-SB cmd */
169#if DEBUG
170 fprintf(stderr, "Ignoring IAC %s,%s\n",
171 TELCMD(ptr[1]), TELOPT(ptr[2]));
172#endif
173 ptr += 3;
Eric Andersen08a72202002-09-30 20:52:10 +0000174 }
175
Denis Vlasenko0de37e12007-10-17 11:08:53 +0000176 num_totty = totty - ptr0;
Eric Andersen08a72202002-09-30 20:52:10 +0000177 *pnum_totty = num_totty;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200178 /* The difference between ptr and totty is number of iacs
179 we removed from the stream. Adjust buf1 accordingly */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000180 if ((ptr - totty) == 0) /* 99.999% of cases */
181 return ptr0;
182 ts->wridx1 += ptr - totty;
183 ts->size1 -= ptr - totty;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200184 /* Move chars meant for the terminal towards the end of the buffer */
Eric Andersen08a72202002-09-30 20:52:10 +0000185 return memmove(ptr - num_totty, ptr0, num_totty);
186}
187
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000188/*
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000189 * Converting single IAC into double on output
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000190 */
191static size_t iac_safe_write(int fd, const char *buf, size_t count)
192{
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000193 const char *IACptr;
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000194 size_t wr, rc, total;
195
196 total = 0;
197 while (1) {
198 if (count == 0)
199 return total;
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000200 if (*buf == (char)IAC) {
201 static const char IACIAC[] ALIGN1 = { IAC, IAC };
202 rc = safe_write(fd, IACIAC, 2);
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000203 if (rc != 2)
204 break;
205 buf++;
206 total++;
207 count--;
208 continue;
209 }
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000210 /* count != 0, *buf != IAC */
211 IACptr = memchr(buf, IAC, count);
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000212 wr = count;
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000213 if (IACptr)
214 wr = IACptr - buf;
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000215 rc = safe_write(fd, buf, wr);
216 if (rc != wr)
217 break;
218 buf += rc;
219 total += rc;
220 count -= rc;
221 }
222 /* here: rc - result of last short write */
223 if ((ssize_t)rc < 0) { /* error? */
224 if (total == 0)
225 return rc;
226 rc = 0;
227 }
228 return total + rc;
229}
Eric Andersen08a72202002-09-30 20:52:10 +0000230
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200231/* Must match getopt32 string */
232enum {
233 OPT_WATCHCHILD = (1 << 2), /* -K */
234 OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200235 OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p PORT */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200236 OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200237 OPT_SYSLOG = (1 << 7) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -S */
238 OPT_WAIT = (1 << 8) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -w SEC */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200239};
240
Eric Andersen08a72202002-09-30 20:52:10 +0000241static struct tsession *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000242make_new_session(
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200243 IF_FEATURE_TELNETD_STANDALONE(int sock)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000244 IF_NOT_FEATURE_TELNETD_STANDALONE(void)
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000245) {
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200246#if !ENABLE_FEATURE_TELNETD_STANDALONE
Denys Vlasenkoff0e8752010-05-10 04:16:43 +0200247 enum { sock = 0 };
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200248#endif
Denis Vlasenko88308fe2007-06-25 10:35:11 +0000249 const char *login_argv[2];
Eric Andersen08a72202002-09-30 20:52:10 +0000250 struct termios termbuf;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000251 int fd, pid;
Denis Vlasenko85c24712008-03-17 09:04:04 +0000252 char tty_name[GETPTY_BUFSIZE];
Rob Landley1ec5b292006-05-29 07:42:02 +0000253 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
Eric Andersen08a72202002-09-30 20:52:10 +0000254
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000255 /*ts->buf1 = (char *)(ts + 1);*/
256 /*ts->buf2 = ts->buf1 + BUFSIZE;*/
Eric Andersen08a72202002-09-30 20:52:10 +0000257
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200258 /* Got a new connection, set up a tty */
Bernhard Reutner-Fischerae4342c2008-05-19 08:18:50 +0000259 fd = xgetpty(tty_name);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200260 if (fd > G.maxfd)
261 G.maxfd = fd;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000262 ts->ptyfd = fd;
263 ndelay_on(fd);
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200264 close_on_exec_on(fd);
265
Denis Vlasenko6d044352008-11-09 00:44:40 +0000266 /* SO_KEEPALIVE by popular demand */
267 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200268#if ENABLE_FEATURE_TELNETD_STANDALONE
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200269 ts->sockfd_read = sock;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000270 ndelay_on(sock);
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200271 if (sock == 0) { /* We are called with fd 0 - we are in inetd mode */
Denis Vlasenko9e237672007-10-17 11:18:49 +0000272 sock++; /* so use fd 1 for output */
273 ndelay_on(sock);
274 }
Denis Vlasenkof472b232007-10-16 21:35:17 +0000275 ts->sockfd_write = sock;
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200276 if (sock > G.maxfd)
277 G.maxfd = sock;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000278#else
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000279 /* ts->sockfd_read = 0; - done by xzalloc */
Denis Vlasenkof472b232007-10-16 21:35:17 +0000280 ts->sockfd_write = 1;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000281 ndelay_on(0);
282 ndelay_on(1);
283#endif
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200284
Eric Andersen08a72202002-09-30 20:52:10 +0000285 /* Make the telnet client understand we will echo characters so it
286 * should not do it locally. We don't tell the client to run linemode,
287 * because we want to handle line editing and tab completion and other
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000288 * stuff that requires char-by-char support. */
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000289 {
290 static const char iacs_to_send[] ALIGN1 = {
291 IAC, DO, TELOPT_ECHO,
292 IAC, DO, TELOPT_NAWS,
Denys Vlasenkofb132e42010-10-29 11:46:52 +0200293 /* This requires telnetd.ctrlSQ.patch (incomplete) */
294 /*IAC, DO, TELOPT_LFLOW,*/
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000295 IAC, WILL, TELOPT_ECHO,
296 IAC, WILL, TELOPT_SGA
297 };
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000298 /* This confuses iac_safe_write(), it will try to duplicate
299 * each IAC... */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200300 //memcpy(TS_BUF2(ts), iacs_to_send, sizeof(iacs_to_send));
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000301 //ts->rdidx2 = sizeof(iacs_to_send);
302 //ts->size2 = sizeof(iacs_to_send);
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000303 /* So just stuff it into TCP stream! (no error check...) */
304#if ENABLE_FEATURE_TELNETD_STANDALONE
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000305 safe_write(sock, iacs_to_send, sizeof(iacs_to_send));
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000306#else
307 safe_write(1, iacs_to_send, sizeof(iacs_to_send));
308#endif
309 /*ts->rdidx2 = 0; - xzalloc did it */
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000310 /*ts->size2 = 0;*/
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000311 }
Eric Andersen08a72202002-09-30 20:52:10 +0000312
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100313 fflush_all();
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000314 pid = vfork(); /* NOMMU-friendly */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000315 if (pid < 0) {
316 free(ts);
317 close(fd);
Denis Vlasenko23c81282007-10-16 22:01:23 +0000318 /* sock will be closed by caller */
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000319 bb_perror_msg("vfork");
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000320 return NULL;
Eric Andersen08a72202002-09-30 20:52:10 +0000321 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000322 if (pid > 0) {
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000323 /* Parent */
Denis Vlasenko2450c452007-10-15 22:09:15 +0000324 ts->shell_pid = pid;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000325 return ts;
Eric Andersen08a72202002-09-30 20:52:10 +0000326 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000327
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000328 /* Child */
329 /* Careful - we are after vfork! */
Eric Andersen08a72202002-09-30 20:52:10 +0000330
Denis Vlasenkod814c982009-02-02 23:43:57 +0000331 /* Restore default signal handling ASAP */
Denis Vlasenko25591c32008-02-16 22:58:56 +0000332 bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000333
Denys Vlasenko58c3d212010-11-30 09:17:30 +0100334 pid = getpid();
335
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200336 if (ENABLE_FEATURE_UTMP) {
337 len_and_sockaddr *lsa = get_peer_lsa(sock);
338 char *hostname = NULL;
339 if (lsa) {
340 hostname = xmalloc_sockaddr2dotted(&lsa->u.sa);
341 free(lsa);
342 }
343 write_new_utmp(pid, LOGIN_PROCESS, tty_name, /*username:*/ "LOGIN", hostname);
344 free(hostname);
345 }
346
Denis Vlasenkod814c982009-02-02 23:43:57 +0000347 /* Make new session and process group */
348 setsid();
349
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200350 /* Open the child's side of the tty */
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +0000351 /* NB: setsid() disconnects from any previous ctty's. Therefore
352 * we must open child's side of the tty AFTER setsid! */
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200353 close(0);
Denis Vlasenko1101d1c2008-07-21 09:22:28 +0000354 xopen(tty_name, O_RDWR); /* becomes our ctty */
355 xdup2(0, 1);
356 xdup2(0, 2);
Denys Vlasenko3a416112010-04-05 22:10:38 +0200357 tcsetpgrp(0, pid); /* switch this tty's process group to us */
358
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200359 /* The pseudo-terminal allocated to the client is configured to operate
360 * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000361 tcgetattr(0, &termbuf);
362 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000363 termbuf.c_oflag |= ONLCR | XTABS;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000364 termbuf.c_iflag |= ICRNL;
365 termbuf.c_iflag &= ~IXOFF;
366 /*termbuf.c_lflag &= ~ICANON;*/
Denis Vlasenko202ac502008-11-05 13:20:58 +0000367 tcsetattr_stdin_TCSANOW(&termbuf);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000368
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100369 /* Uses FILE-based I/O to stdout, but does fflush_all(),
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000370 * so should be safe with vfork.
371 * I fear, though, that some users will have ridiculously big
Denis Vlasenko018b1552007-11-06 01:38:46 +0000372 * issue files, and they may block writing to fd 1,
373 * (parent is supposed to read it, but parent waits
374 * for vforked child to exec!) */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200375 print_login_issue(G.issuefile, tty_name);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000376
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000377 /* Exec shell / login / whatever */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200378 login_argv[0] = G.loginpath;
Denis Vlasenko88308fe2007-06-25 10:35:11 +0000379 login_argv[1] = NULL;
Denis Vlasenko018b1552007-11-06 01:38:46 +0000380 /* exec busybox applet (if PREFER_APPLETS=y), if that fails,
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200381 * exec external program.
382 * NB: sock is either 0 or has CLOEXEC set on it.
383 * fd has CLOEXEC set on it too. These two fds will be closed here.
384 */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200385 BB_EXECVP(G.loginpath, (char **)login_argv);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000386 /* _exit is safer with vfork, and we shouldn't send message
Denis Vlasenko2450c452007-10-15 22:09:15 +0000387 * to remote clients anyway */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200388 _exit(EXIT_FAILURE); /*bb_perror_msg_and_die("execv %s", G.loginpath);*/
Eric Andersen08a72202002-09-30 20:52:10 +0000389}
390
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000391#if ENABLE_FEATURE_TELNETD_STANDALONE
392
Eric Andersen08a72202002-09-30 20:52:10 +0000393static void
394free_session(struct tsession *ts)
395{
Denys Vlasenko3a416112010-04-05 22:10:38 +0200396 struct tsession *t;
Eric Andersen08a72202002-09-30 20:52:10 +0000397
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000398 if (option_mask32 & OPT_INETD)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000399 exit(EXIT_SUCCESS);
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000400
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000401 /* Unlink this telnet session from the session list */
Denys Vlasenko3a416112010-04-05 22:10:38 +0200402 t = G.sessions;
Mike Frysinger731f81c2006-05-10 15:23:12 +0000403 if (t == ts)
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200404 G.sessions = ts->next;
Eric Andersen08a72202002-09-30 20:52:10 +0000405 else {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000406 while (t->next != ts)
Eric Andersen08a72202002-09-30 20:52:10 +0000407 t = t->next;
408 t->next = ts->next;
409 }
410
Denis Vlasenkof472b232007-10-16 21:35:17 +0000411#if 0
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000412 /* It was said that "normal" telnetd just closes ptyfd,
413 * doesn't send SIGKILL. When we close ptyfd,
414 * kernel sends SIGHUP to processes having slave side opened. */
Denis Vlasenkof472b232007-10-16 21:35:17 +0000415 kill(ts->shell_pid, SIGKILL);
Denis Vlasenkod814c982009-02-02 23:43:57 +0000416 waitpid(ts->shell_pid, NULL, 0);
Denis Vlasenkof472b232007-10-16 21:35:17 +0000417#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000418 close(ts->ptyfd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000419 close(ts->sockfd_read);
Denis Vlasenkof472b232007-10-16 21:35:17 +0000420 /* We do not need to close(ts->sockfd_write), it's the same
421 * as sockfd_read unless we are in inetd mode. But in inetd mode
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000422 * we do not reach this */
Eric Andersen08a72202002-09-30 20:52:10 +0000423 free(ts);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000424
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000425 /* Scan all sessions and find new maxfd */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200426 G.maxfd = 0;
427 ts = G.sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000428 while (ts) {
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200429 if (G.maxfd < ts->ptyfd)
430 G.maxfd = ts->ptyfd;
431 if (G.maxfd < ts->sockfd_read)
432 G.maxfd = ts->sockfd_read;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000433#if 0
434 /* Again, sockfd_write == sockfd_read here */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200435 if (G.maxfd < ts->sockfd_write)
436 G.maxfd = ts->sockfd_write;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000437#endif
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000438 ts = ts->next;
439 }
Eric Andersen08a72202002-09-30 20:52:10 +0000440}
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000441
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000442#else /* !FEATURE_TELNETD_STANDALONE */
443
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000444/* Used in main() only, thus "return 0" actually is exit(EXIT_SUCCESS). */
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000445#define free_session(ts) return 0
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000446
447#endif
448
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000449static void handle_sigchld(int sig UNUSED_PARAM)
Denis Vlasenko2450c452007-10-15 22:09:15 +0000450{
451 pid_t pid;
452 struct tsession *ts;
Denys Vlasenko3a416112010-04-05 22:10:38 +0200453 int save_errno = errno;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000454
Denis Vlasenko018b1552007-11-06 01:38:46 +0000455 /* Looping: more than one child may have exited */
456 while (1) {
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000457 pid = wait_any_nohang(NULL);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000458 if (pid <= 0)
459 break;
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200460 ts = G.sessions;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000461 while (ts) {
462 if (ts->shell_pid == pid) {
463 ts->shell_pid = -1;
Denys Vlasenko3a416112010-04-05 22:10:38 +0200464// man utmp:
465// When init(8) finds that a process has exited, it locates its utmp entry
466// by ut_pid, sets ut_type to DEAD_PROCESS, and clears ut_user, ut_host
467// and ut_time with null bytes.
468// [same applies to other processes which maintain utmp entries, like telnetd]
469//
470// We do not bother actually clearing fields:
471// it might be interesting to know who was logged in and from where
472 update_utmp(pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000473 break;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000474 }
475 ts = ts->next;
476 }
477 }
Denys Vlasenko3a416112010-04-05 22:10:38 +0200478
479 errno = save_errno;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000480}
481
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000482int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000483int telnetd_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen08a72202002-09-30 20:52:10 +0000484{
Eric Andersen08a72202002-09-30 20:52:10 +0000485 fd_set rdfdset, wrfdset;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000486 unsigned opt;
Denis Vlasenko10916c52007-10-15 17:28:00 +0000487 int count;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000488 struct tsession *ts;
489#if ENABLE_FEATURE_TELNETD_STANDALONE
490#define IS_INETD (opt & OPT_INETD)
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200491 int master_fd = master_fd; /* for compiler */
492 int sec_linger = sec_linger;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000493 char *opt_bindaddr = NULL;
494 char *opt_portnbr;
495#else
496 enum {
497 IS_INETD = 1,
498 master_fd = -1,
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000499 };
Glenn L McGrathc2b91862003-09-12 11:27:15 +0000500#endif
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200501 INIT_G();
502
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200503 /* -w NUM, and implies -F. -w and -i don't mix */
504 IF_FEATURE_TELNETD_INETD_WAIT(opt_complementary = "wF:w+:i--w:w--i";)
Denis Vlasenko2450c452007-10-15 22:09:15 +0000505 /* Even if !STANDALONE, we accept (and ignore) -i, thus people
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000506 * don't need to guess whether it's ok to pass -i to us */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200507 opt = getopt32(argv, "f:l:Ki"
508 IF_FEATURE_TELNETD_STANDALONE("p:b:F")
509 IF_FEATURE_TELNETD_INETD_WAIT("Sw:"),
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200510 &G.issuefile, &G.loginpath
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200511 IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)
512 IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger)
513 );
Denis Vlasenko2450c452007-10-15 22:09:15 +0000514 if (!IS_INETD /*&& !re_execed*/) {
515 /* inform that we start in standalone mode?
516 * May be useful when people forget to give -i */
517 /*bb_error_msg("listening for connections");*/
518 if (!(opt & OPT_FOREGROUND)) {
519 /* DAEMON_CHDIR_ROOT was giving inconsistent
Denis Vlasenko008eda22007-10-16 10:47:27 +0000520 * behavior with/without -F, -i */
Denis Vlasenko018b1552007-11-06 01:38:46 +0000521 bb_daemonize_or_rexec(0 /*was DAEMON_CHDIR_ROOT*/, argv);
Denis Vlasenko2450c452007-10-15 22:09:15 +0000522 }
523 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000524 /* Redirect log to syslog early, if needed */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200525 if (IS_INETD || (opt & OPT_SYSLOG) || !(opt & OPT_FOREGROUND)) {
Denis Vlasenko5e4fda02009-03-08 23:46:48 +0000526 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000527 logmode = LOGMODE_SYSLOG;
528 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000529#if ENABLE_FEATURE_TELNETD_STANDALONE
530 if (IS_INETD) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200531 G.sessions = make_new_session(0);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200532 if (!G.sessions) /* pty opening or vfork problem, exit */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200533 return 1; /* make_new_session printed error message */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000534 } else {
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200535 master_fd = 0;
536 if (!(opt & OPT_WAIT)) {
537 unsigned portnbr = 23;
538 if (opt & OPT_PORT)
539 portnbr = xatou16(opt_portnbr);
540 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
541 xlisten(master_fd, 1);
542 }
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200543 close_on_exec_on(master_fd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000544 }
Rob Landley00e76cb2005-05-10 23:53:33 +0000545#else
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200546 G.sessions = make_new_session();
547 if (!G.sessions) /* pty opening or vfork problem, exit */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200548 return 1; /* make_new_session printed error message */
Rob Landley00e76cb2005-05-10 23:53:33 +0000549#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000550
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000551 /* We don't want to die if just one session is broken */
552 signal(SIGPIPE, SIG_IGN);
Eric Andersen08a72202002-09-30 20:52:10 +0000553
Denis Vlasenko2450c452007-10-15 22:09:15 +0000554 if (opt & OPT_WATCHCHILD)
555 signal(SIGCHLD, handle_sigchld);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000556 else /* prevent dead children from becoming zombies */
557 signal(SIGCHLD, SIG_IGN);
Denis Vlasenko2450c452007-10-15 22:09:15 +0000558
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000559/*
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200560 This is how the buffers are used. The arrows indicate data flow.
561
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000562 +-------+ wridx1++ +------+ rdidx1++ +----------+
563 | | <-------------- | buf1 | <-------------- | |
564 | | size1-- +------+ size1++ | |
565 | pty | | socket |
566 | | rdidx2++ +------+ wridx2++ | |
567 | | --------------> | buf2 | --------------> | |
568 +-------+ size2++ +------+ size2-- +----------+
569
570 size1: "how many bytes are buffered for pty between rdidx1 and wridx1?"
571 size2: "how many bytes are buffered for socket between rdidx2 and wridx2?"
572
573 Each session has got two buffers. Buffers are circular. If sizeN == 0,
574 buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases
575 rdidxN == wridxN.
576*/
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000577 again:
578 FD_ZERO(&rdfdset);
579 FD_ZERO(&wrfdset);
Eric Andersen08a72202002-09-30 20:52:10 +0000580
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000581 /* Select on the master socket, all telnet sockets and their
582 * ptys if there is room in their session buffers.
583 * NB: scalability problem: we recalculate entire bitmap
584 * before each select. Can be a problem with 500+ connections. */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200585 ts = G.sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000586 while (ts) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200587 struct tsession *next = ts->next; /* in case we free ts */
Denis Vlasenko2450c452007-10-15 22:09:15 +0000588 if (ts->shell_pid == -1) {
Denis Vlasenko018b1552007-11-06 01:38:46 +0000589 /* Child died and we detected that */
Denis Vlasenko2450c452007-10-15 22:09:15 +0000590 free_session(ts);
591 } else {
592 if (ts->size1 > 0) /* can write to pty */
593 FD_SET(ts->ptyfd, &wrfdset);
594 if (ts->size1 < BUFSIZE) /* can read from socket */
595 FD_SET(ts->sockfd_read, &rdfdset);
596 if (ts->size2 > 0) /* can write to socket */
597 FD_SET(ts->sockfd_write, &wrfdset);
598 if (ts->size2 < BUFSIZE) /* can read from pty */
599 FD_SET(ts->ptyfd, &rdfdset);
600 }
601 ts = next;
602 }
603 if (!IS_INETD) {
604 FD_SET(master_fd, &rdfdset);
605 /* This is needed because free_session() does not
Denis Vlasenko018b1552007-11-06 01:38:46 +0000606 * take master_fd into account when it finds new
Denis Vlasenko2450c452007-10-15 22:09:15 +0000607 * maxfd among remaining fd's */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200608 if (master_fd > G.maxfd)
609 G.maxfd = master_fd;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000610 }
611
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200612 {
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200613 struct timeval *tv_ptr = NULL;
Denys Vlasenko36df0482009-10-19 16:07:28 +0200614#if ENABLE_FEATURE_TELNETD_INETD_WAIT
615 struct timeval tv;
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200616 if ((opt & OPT_WAIT) && !G.sessions) {
617 tv.tv_sec = sec_linger;
618 tv.tv_usec = 0;
619 tv_ptr = &tv;
620 }
Denys Vlasenko36df0482009-10-19 16:07:28 +0200621#endif
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200622 count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, tv_ptr);
623 }
624 if (count == 0) /* "telnetd -w SEC" timed out */
625 return 0;
Denis Vlasenko10916c52007-10-15 17:28:00 +0000626 if (count < 0)
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000627 goto again; /* EINTR or ENOMEM */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000628
629#if ENABLE_FEATURE_TELNETD_STANDALONE
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200630 /* Check for and accept new sessions */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000631 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000632 int fd;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000633 struct tsession *new_ts;
634
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000635 fd = accept(master_fd, NULL, NULL);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000636 if (fd < 0)
637 goto again;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200638 close_on_exec_on(fd);
639
640 /* Create a new session and link it into active list */
641 new_ts = make_new_session(fd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000642 if (new_ts) {
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200643 new_ts->next = G.sessions;
644 G.sessions = new_ts;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000645 } else {
646 close(fd);
Eric Andersen08a72202002-09-30 20:52:10 +0000647 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000648 }
649#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000650
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200651 /* Then check for data tunneling */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200652 ts = G.sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000653 while (ts) { /* For all sessions... */
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200654 struct tsession *next = ts->next; /* in case we free ts */
Eric Andersen08a72202002-09-30 20:52:10 +0000655
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000656 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000657 int num_totty;
Denis Vlasenko10916c52007-10-15 17:28:00 +0000658 unsigned char *ptr;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200659 /* Write to pty from buffer 1 */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000660 ptr = remove_iacs(ts, &num_totty);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000661 count = safe_write(ts->ptyfd, ptr, num_totty);
662 if (count < 0) {
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000663 if (errno == EAGAIN)
664 goto skip1;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000665 goto kill_session;
Eric Andersen08a72202002-09-30 20:52:10 +0000666 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000667 ts->size1 -= count;
668 ts->wridx1 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000669 if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
Eric Andersen08a72202002-09-30 20:52:10 +0000670 ts->wridx1 = 0;
Eric Andersen08a72202002-09-30 20:52:10 +0000671 }
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000672 skip1:
673 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200674 /* Write to socket from buffer 2 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000675 count = MIN(BUFSIZE - ts->wridx2, ts->size2);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200676 count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000677 if (count < 0) {
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000678 if (errno == EAGAIN)
679 goto skip2;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000680 goto kill_session;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000681 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000682 ts->size2 -= count;
683 ts->wridx2 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000684 if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000685 ts->wridx2 = 0;
686 }
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000687 skip2:
Denis Vlasenko10916c52007-10-15 17:28:00 +0000688 /* Should not be needed, but... remove_iacs is actually buggy
689 * (it cannot process iacs which wrap around buffer's end)!
690 * Since properly fixing it requires writing bigger code,
Denis Vlasenkof472b232007-10-16 21:35:17 +0000691 * we rely instead on this code making it virtually impossible
Denis Vlasenko10916c52007-10-15 17:28:00 +0000692 * to have wrapped iac (people don't type at 2k/second).
693 * It also allows for bigger reads in common case. */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000694 if (ts->size1 == 0) {
695 ts->rdidx1 = 0;
696 ts->wridx1 = 0;
697 }
698 if (ts->size2 == 0) {
699 ts->rdidx2 = 0;
700 ts->wridx2 = 0;
701 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000702
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000703 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200704 /* Read from socket to buffer 1 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000705 count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200706 count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000707 if (count <= 0) {
708 if (count < 0 && errno == EAGAIN)
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000709 goto skip3;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000710 goto kill_session;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000711 }
712 /* Ignore trailing NUL if it is there */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200713 if (!TS_BUF1(ts)[ts->rdidx1 + count - 1]) {
Denis Vlasenkof472b232007-10-16 21:35:17 +0000714 --count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000715 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000716 ts->size1 += count;
717 ts->rdidx1 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000718 if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
719 ts->rdidx1 = 0;
720 }
721 skip3:
722 if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200723 /* Read from pty to buffer 2 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000724 count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200725 count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000726 if (count <= 0) {
727 if (count < 0 && errno == EAGAIN)
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000728 goto skip4;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000729 goto kill_session;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000730 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000731 ts->size2 += count;
732 ts->rdidx2 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000733 if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
734 ts->rdidx2 = 0;
735 }
736 skip4:
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000737 ts = next;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000738 continue;
739 kill_session:
Denys Vlasenko3a416112010-04-05 22:10:38 +0200740 if (ts->shell_pid > 0)
741 update_utmp(ts->shell_pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL);
Denis Vlasenkof472b232007-10-16 21:35:17 +0000742 free_session(ts);
743 ts = next;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000744 }
Denis Vlasenkof472b232007-10-16 21:35:17 +0000745
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000746 goto again;
Eric Andersen08a72202002-09-30 20:52:10 +0000747}