aboutsummaryrefslogtreecommitdiff
path: root/networking/telnetd.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-10-15 15:19:36 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-10-15 15:19:36 +0000
commit59d7c43dbe502ef7425f6828bb43528c5adfc9a9 (patch)
tree0b9a9cb8fd39f35fa361aaa380f3024385faa5e3 /networking/telnetd.c
parentd898b8600cd64310dfb60e4ffb236b64a00d1cc2 (diff)
downloadbusybox-w32-59d7c43dbe502ef7425f6828bb43528c5adfc9a9.tar.gz
busybox-w32-59d7c43dbe502ef7425f6828bb43528c5adfc9a9.tar.bz2
busybox-w32-59d7c43dbe502ef7425f6828bb43528c5adfc9a9.zip
telnetd: some simplifications and better error hadling.
telnetd: don't SIGKILL child when closing the session. kernel will seng SIGHUP for us. static.iacs_to_send - 15 +15 .rodata 123418 123429 +11 make_new_session 549 525 -24 send_iac 26 - -26 free_session 144 118 -26 telnetd_main 1303 1261 -42 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 1/3 up/down: 26/-118) Total: -92 bytes text data bss dec hex filename 676341 2538 12104 690983 a8b27 busybox_old 676234 2538 12104 690876 a8abc busybox_unstripped
Diffstat (limited to 'networking/telnetd.c')
-rw-r--r--networking/telnetd.c270
1 files changed, 150 insertions, 120 deletions
diff --git a/networking/telnetd.c b/networking/telnetd.c
index 27dde1ae4..9ce793fb7 100644
--- a/networking/telnetd.c
+++ b/networking/telnetd.c
@@ -33,63 +33,51 @@
33#include <sys/syslog.h> 33#include <sys/syslog.h>
34 34
35 35
36#if ENABLE_LOGIN 36/* Structure that describes a session */
37static const char *loginpath = "/bin/login";
38#else
39static const char *loginpath = DEFAULT_SHELL;
40#endif
41
42static const char *issuefile = "/etc/issue.net";
43
44/* structure that describes a session */
45
46struct tsession { 37struct tsession {
47 struct tsession *next; 38 struct tsession *next;
48 int sockfd_read, sockfd_write, ptyfd; 39 int sockfd_read, sockfd_write, ptyfd;
49 int shell_pid; 40 /*int shell_pid;*/
41
50 /* two circular buffers */ 42 /* two circular buffers */
51 char *buf1, *buf2; 43 /*char *buf1, *buf2;*/
44/*#define TS_BUF1 ts->buf1*/
45/*#define TS_BUF2 TS_BUF2*/
46#define TS_BUF1 ((char*)(ts + 1))
47#define TS_BUF2 (((char*)(ts + 1)) + BUFSIZE)
52 int rdidx1, wridx1, size1; 48 int rdidx1, wridx1, size1;
53 int rdidx2, wridx2, size2; 49 int rdidx2, wridx2, size2;
54}; 50};
55 51
56/* Two buffers are directly after tsession in malloced memory. 52/* Two buffers are directly after tsession in malloced memory.
57 * Make whole thing fit in 4k */ 53 * Make whole thing fit in 4k */
58enum { BUFSIZE = (4*1024 - sizeof(struct tsession)) / 2 }; 54enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 };
59
60/*
61 This is how the buffers are used. The arrows indicate the movement
62 of data.
63
64 +-------+ wridx1++ +------+ rdidx1++ +----------+
65 | | <-------------- | buf1 | <-------------- | |
66 | | size1-- +------+ size1++ | |
67 | pty | | socket |
68 | | rdidx2++ +------+ wridx2++ | |
69 | | --------------> | buf2 | --------------> | |
70 +-------+ size2++ +------+ size2-- +----------+
71 55
72 Each session has got two buffers.
73*/
74 56
57/* Globals */
75static int maxfd; 58static int maxfd;
76
77static struct tsession *sessions; 59static struct tsession *sessions;
60#if ENABLE_LOGIN
61static const char *loginpath = "/bin/login";
62#else
63static const char *loginpath = DEFAULT_SHELL;
64#endif
65static const char *issuefile = "/etc/issue.net";
78 66
79 67
80/* 68/*
81 Remove all IAC's from the buffer pointed to by bf (received IACs are ignored 69 Remove all IAC's from buf1 (received IACs are ignored and must be removed
82 and must be removed so as to not be interpreted by the terminal). Make an 70 so as to not be interpreted by the terminal). Make an uninterrupted
83 uninterrupted string of characters fit for the terminal. Do this by packing 71 string of characters fit for the terminal. Do this by packing
84 all characters meant for the terminal sequentially towards the end of bf. 72 all characters meant for the terminal sequentially towards the end of buf.
85 73
86 Return a pointer to the beginning of the characters meant for the terminal. 74 Return a pointer to the beginning of the characters meant for the terminal.
87 and make *num_totty the number of characters that should be sent to 75 and make *num_totty the number of characters that should be sent to
88 the terminal. 76 the terminal.
89 77
90 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends 78 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
91 past (bf + len) then that IAC will be left unprocessed and *processed will be 79 past (bf + len) then that IAC will be left unprocessed and *processed
92 less than len. 80 will be less than len.
93 81
94 FIXME - if we mean to send 0xFF to the terminal then it will be escaped, 82 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
95 what is the escape character? We aren't handling that situation here. 83 what is the escape character? We aren't handling that situation here.
@@ -99,7 +87,7 @@ static struct tsession *sessions;
99static char * 87static char *
100remove_iacs(struct tsession *ts, int *pnum_totty) 88remove_iacs(struct tsession *ts, int *pnum_totty)
101{ 89{
102 unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1; 90 unsigned char *ptr0 = (unsigned char *)TS_BUF1 + ts->wridx1;
103 unsigned char *ptr = ptr0; 91 unsigned char *ptr = ptr0;
104 unsigned char *totty = ptr; 92 unsigned char *totty = ptr;
105 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1); 93 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
@@ -210,19 +198,6 @@ getpty(char *line, int size)
210} 198}
211 199
212 200
213static void
214send_iac(struct tsession *ts, unsigned char command, int option)
215{
216 /* We rely on that there is space in the buffer for now. */
217 char *b = ts->buf2 + ts->rdidx2;
218 *b++ = IAC;
219 *b++ = command;
220 *b++ = option;
221 ts->rdidx2 += 3;
222 ts->size2 += 3;
223}
224
225
226static struct tsession * 201static struct tsession *
227make_new_session( 202make_new_session(
228 USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w) 203 USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w)
@@ -234,25 +209,28 @@ make_new_session(
234 char tty_name[32]; 209 char tty_name[32];
235 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2); 210 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
236 211
237 ts->buf1 = (char *)(&ts[1]); 212 /*ts->buf1 = (char *)(ts + 1);*/
238 ts->buf2 = ts->buf1 + BUFSIZE; 213 /*ts->buf2 = ts->buf1 + BUFSIZE;*/
239 214
240 /* Got a new connection, set up a tty. */ 215 /* Got a new connection, set up a tty. */
241 fd = getpty(tty_name, 32); 216 fd = getpty(tty_name, 32);
242 if (fd < 0) { 217 if (fd < 0) {
243 bb_error_msg("all terminals in use"); 218 bb_error_msg("can't create pty");
244 return NULL; 219 return NULL;
245 } 220 }
246 if (fd > maxfd) maxfd = fd; 221 if (fd > maxfd) maxfd = fd;
247 ndelay_on(ts->ptyfd = fd); 222 ts->ptyfd = fd;
223 ndelay_on(fd);
248#if ENABLE_FEATURE_TELNETD_STANDALONE 224#if ENABLE_FEATURE_TELNETD_STANDALONE
249 if (sock_w > maxfd) maxfd = sock_w; 225 if (sock_w > maxfd) maxfd = sock_w;
226 ts->sockfd_write = sock_w;
227 ndelay_on(sock_w);
250 if (sock_r > maxfd) maxfd = sock_r; 228 if (sock_r > maxfd) maxfd = sock_r;
251 ndelay_on(ts->sockfd_write = sock_w); 229 ts->sockfd_read = sock_r;
252 ndelay_on(ts->sockfd_read = sock_r); 230 ndelay_on(sock_r);
253#else 231#else
254 ts->sockfd_write = 1; 232 ts->sockfd_write = 1;
255 /* xzalloc: ts->sockfd_read = 0; */ 233 /* ts->sockfd_read = 0; - done by xzalloc */
256 ndelay_on(0); 234 ndelay_on(0);
257 ndelay_on(1); 235 ndelay_on(1);
258#endif 236#endif
@@ -260,26 +238,36 @@ make_new_session(
260 * should not do it locally. We don't tell the client to run linemode, 238 * should not do it locally. We don't tell the client to run linemode,
261 * because we want to handle line editing and tab completion and other 239 * because we want to handle line editing and tab completion and other
262 * stuff that requires char-by-char support. */ 240 * stuff that requires char-by-char support. */
263 send_iac(ts, DO, TELOPT_ECHO); 241 {
264 send_iac(ts, DO, TELOPT_NAWS); 242 static const char iacs_to_send[] ALIGN1 = {
265 send_iac(ts, DO, TELOPT_LFLOW); 243 IAC, DO, TELOPT_ECHO,
266 send_iac(ts, WILL, TELOPT_ECHO); 244 IAC, DO, TELOPT_NAWS,
267 send_iac(ts, WILL, TELOPT_SGA); 245 IAC, DO, TELOPT_LFLOW,
246 IAC, WILL, TELOPT_ECHO,
247 IAC, WILL, TELOPT_SGA
248 };
249 memcpy(TS_BUF2, iacs_to_send, sizeof(iacs_to_send));
250 ts->rdidx2 = sizeof(iacs_to_send);
251 ts->size2 = sizeof(iacs_to_send);
252 }
268 253
269 pid = fork(); 254 fflush(NULL); /* flush all streams */
255 pid = vfork(); /* NOMMU-friendly */
270 if (pid < 0) { 256 if (pid < 0) {
271 free(ts); 257 free(ts);
272 close(fd); 258 close(fd);
273 bb_perror_msg("fork"); 259 /* sock_r and sock_w will be closed by caller */
260 bb_perror_msg("vfork");
274 return NULL; 261 return NULL;
275 } 262 }
276 if (pid > 0) { 263 if (pid > 0) {
277 /* parent */ 264 /* Parent */
278 ts->shell_pid = pid; 265 /*ts->shell_pid = pid;*/
279 return ts; 266 return ts;
280 } 267 }
281 268
282 /* child */ 269 /* Child */
270 /* Careful - we are after vfork! */
283 271
284 /* make new session and process group */ 272 /* make new session and process group */
285 setsid(); 273 setsid();
@@ -298,20 +286,24 @@ make_new_session(
298 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */ 286 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
299 tcgetattr(0, &termbuf); 287 tcgetattr(0, &termbuf);
300 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */ 288 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
301 termbuf.c_oflag |= ONLCR|XTABS; 289 termbuf.c_oflag |= ONLCR | XTABS;
302 termbuf.c_iflag |= ICRNL; 290 termbuf.c_iflag |= ICRNL;
303 termbuf.c_iflag &= ~IXOFF; 291 termbuf.c_iflag &= ~IXOFF;
304 /*termbuf.c_lflag &= ~ICANON;*/ 292 /*termbuf.c_lflag &= ~ICANON;*/
305 tcsetattr(0, TCSANOW, &termbuf); 293 tcsetattr(0, TCSANOW, &termbuf);
306 294
295 /* Uses FILE-based I/O to stdout, but does fflush(stdout),
296 * so should be safe with vfork.
297 * I fear, though, that some users will have ridiculously big
298 * issue files, and they may block writing to fd 1. */
307 print_login_issue(issuefile, NULL); 299 print_login_issue(issuefile, NULL);
308 300
309 /* exec shell / login /whatever */ 301 /* Exec shell / login / whatever */
310 login_argv[0] = loginpath; 302 login_argv[0] = loginpath;
311 login_argv[1] = NULL; 303 login_argv[1] = NULL;
312 execv(loginpath, (char **)login_argv); 304 execvp(loginpath, (char **)login_argv);
313 /* Hmmm... this gets sent to the client thru fd#2! Is it ok?? */ 305 /* Safer with vfork, and we shouldn't send this to the client anyway */
314 bb_perror_msg_and_die("execv %s", loginpath); 306 _exit(1); /*bb_perror_msg_and_die("execv %s", loginpath);*/
315} 307}
316 308
317#if ENABLE_FEATURE_TELNETD_STANDALONE 309#if ENABLE_FEATURE_TELNETD_STANDALONE
@@ -321,7 +313,7 @@ free_session(struct tsession *ts)
321{ 313{
322 struct tsession *t = sessions; 314 struct tsession *t = sessions;
323 315
324 /* unlink this telnet session from the session list */ 316 /* Unlink this telnet session from the session list */
325 if (t == ts) 317 if (t == ts)
326 sessions = ts->next; 318 sessions = ts->next;
327 else { 319 else {
@@ -330,15 +322,18 @@ free_session(struct tsession *ts)
330 t->next = ts->next; 322 t->next = ts->next;
331 } 323 }
332 324
333 kill(ts->shell_pid, SIGKILL); 325 /* It was said that "normal" telnetd just closes ptyfd,
334 wait4(ts->shell_pid, NULL, 0, NULL); 326 * doesn't send SIGKILL. When we close ptyfd,
327 * kernel sends SIGHUP to processes having slave side opened. */
328 /*kill(ts->shell_pid, SIGKILL);
329 wait4(ts->shell_pid, NULL, 0, NULL);*/
335 close(ts->ptyfd); 330 close(ts->ptyfd);
336 close(ts->sockfd_read); 331 close(ts->sockfd_read);
337 /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */ 332 /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */
338 close(ts->sockfd_write); 333 close(ts->sockfd_write);
339 free(ts); 334 free(ts);
340 335
341 /* scan all sessions and find new maxfd */ 336 /* Scan all sessions and find new maxfd */
342 ts = sessions; 337 ts = sessions;
343 maxfd = 0; 338 maxfd = 0;
344 while (ts) { 339 while (ts) {
@@ -369,7 +364,7 @@ int telnetd_main(int argc, char **argv)
369 struct tsession *ts; 364 struct tsession *ts;
370#if ENABLE_FEATURE_TELNETD_STANDALONE 365#if ENABLE_FEATURE_TELNETD_STANDALONE
371#define IS_INETD (opt & OPT_INETD) 366#define IS_INETD (opt & OPT_INETD)
372 int master_fd = -1; /* be happy, gcc */ 367 int master_fd = master_fd; /* be happy, gcc */
373 unsigned portnbr = 23; 368 unsigned portnbr = 23;
374 char *opt_bindaddr = NULL; 369 char *opt_bindaddr = NULL;
375 char *opt_portnbr; 370 char *opt_portnbr;
@@ -381,12 +376,14 @@ int telnetd_main(int argc, char **argv)
381 }; 376 };
382#endif 377#endif
383 enum { 378 enum {
384 OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE, 379 OPT_INETD = (1 << 2) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
385 OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE, 380 OPT_PORT = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p */
386 OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE, 381 OPT_FOREGROUND = (1 << 5) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
387 }; 382 };
388 383
389 opt = getopt32(argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"), 384 /* If !STANDALONE, we accept (and ignore) -i, thus people
385 * don't need to guess whether it's ok to pass -i to us */
386 opt = getopt32(argv, "f:l:i" USE_FEATURE_TELNETD_STANDALONE("p:b:F"),
390 &issuefile, &loginpath 387 &issuefile, &loginpath
391 USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)); 388 USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
392 /* Redirect log to syslog early, if needed */ 389 /* Redirect log to syslog early, if needed */
@@ -410,19 +407,43 @@ int telnetd_main(int argc, char **argv)
410#if ENABLE_FEATURE_TELNETD_STANDALONE 407#if ENABLE_FEATURE_TELNETD_STANDALONE
411 if (IS_INETD) { 408 if (IS_INETD) {
412 sessions = make_new_session(0, 1); 409 sessions = make_new_session(0, 1);
410 if (!sessions) /* pty opening or vfork problem, exit */
411 return 1; /* make_new_session prints error message */
413 } else { 412 } else {
413//vda: inform that we start in standalone mode?
414 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr); 414 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
415 xlisten(master_fd, 1); 415 xlisten(master_fd, 1);
416 if (!(opt & OPT_FOREGROUND)) 416 if (!(opt & OPT_FOREGROUND))
417//vda: NOMMU?
417 bb_daemonize(DAEMON_CHDIR_ROOT); 418 bb_daemonize(DAEMON_CHDIR_ROOT);
418 } 419 }
419#else 420#else
420 sessions = make_new_session(); 421 sessions = make_new_session();
422 if (!sessions) /* pty opening or vfork problem, exit */
423 return 1; /* make_new_session prints error message */
421#endif 424#endif
422 425
423 /* We don't want to die if just one session is broken */ 426 /* We don't want to die if just one session is broken */
424 signal(SIGPIPE, SIG_IGN); 427 signal(SIGPIPE, SIG_IGN);
425 428
429/*
430 This is how the buffers are used. The arrows indicate the movement
431 of data.
432 +-------+ wridx1++ +------+ rdidx1++ +----------+
433 | | <-------------- | buf1 | <-------------- | |
434 | | size1-- +------+ size1++ | |
435 | pty | | socket |
436 | | rdidx2++ +------+ wridx2++ | |
437 | | --------------> | buf2 | --------------> | |
438 +-------+ size2++ +------+ size2-- +----------+
439
440 size1: "how many bytes are buffered for pty between rdidx1 and wridx1?"
441 size2: "how many bytes are buffered for socket between rdidx2 and wridx2?"
442
443 Each session has got two buffers. Buffers are circular. If sizeN == 0,
444 buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases
445 rdidxN == wridxN.
446*/
426 again: 447 again:
427 FD_ZERO(&rdfdset); 448 FD_ZERO(&rdfdset);
428 FD_ZERO(&wrfdset); 449 FD_ZERO(&wrfdset);
@@ -435,12 +456,12 @@ int telnetd_main(int argc, char **argv)
435 maxfd = master_fd; 456 maxfd = master_fd;
436 } 457 }
437 458
438 /* select on the master socket, all telnet sockets and their 459 /* Select on the master socket, all telnet sockets and their
439 * ptys if there is room in their session buffers. */ 460 * ptys if there is room in their session buffers.
461 * NB: scalability problem: we recalculate entire bitmap
462 * before each select. Can be a problem with 500+ connections. */
440 ts = sessions; 463 ts = sessions;
441 while (ts) { 464 while (ts) {
442 /* buf1 is used from socket to pty
443 * buf2 is used from pty to socket */
444 if (ts->size1 > 0) /* can write to pty */ 465 if (ts->size1 > 0) /* can write to pty */
445 FD_SET(ts->ptyfd, &wrfdset); 466 FD_SET(ts->ptyfd, &wrfdset);
446 if (ts->size1 < BUFSIZE) /* can read from socket */ 467 if (ts->size1 < BUFSIZE) /* can read from socket */
@@ -452,9 +473,9 @@ int telnetd_main(int argc, char **argv)
452 ts = ts->next; 473 ts = ts->next;
453 } 474 }
454 475
455 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0); 476 selret = select(maxfd + 1, &rdfdset, &wrfdset, NULL, NULL);
456 if (!selret) 477 if (selret < 0)
457 return 0; 478 goto again; /* EINTR or ENOMEM */
458 479
459#if ENABLE_FEATURE_TELNETD_STANDALONE 480#if ENABLE_FEATURE_TELNETD_STANDALONE
460 /* First check for and accept new sessions. */ 481 /* First check for and accept new sessions. */
@@ -462,7 +483,7 @@ int telnetd_main(int argc, char **argv)
462 int fd; 483 int fd;
463 struct tsession *new_ts; 484 struct tsession *new_ts;
464 485
465 fd = accept(master_fd, NULL, 0); 486 fd = accept(master_fd, NULL, NULL);
466 if (fd < 0) 487 if (fd < 0)
467 goto again; 488 goto again;
468 /* Create a new session and link it into our active list */ 489 /* Create a new session and link it into our active list */
@@ -481,91 +502,100 @@ int telnetd_main(int argc, char **argv)
481 while (ts) { /* For all sessions... */ 502 while (ts) { /* For all sessions... */
482 struct tsession *next = ts->next; /* in case we free ts. */ 503 struct tsession *next = ts->next; /* in case we free ts. */
483 504
484 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) { 505 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
485 int num_totty; 506 int num_totty;
486 char *ptr; 507 char *ptr;
487 /* Write to pty from buffer 1. */ 508 /* Write to pty from buffer 1. */
488 ptr = remove_iacs(ts, &num_totty); 509 ptr = remove_iacs(ts, &num_totty);
489 w = safe_write(ts->ptyfd, ptr, num_totty); 510 w = safe_write(ts->ptyfd, ptr, num_totty);
490 /* needed? if (w < 0 && errno == EAGAIN) continue; */
491 if (w < 0) { 511 if (w < 0) {
512 if (errno == EAGAIN)
513 goto skip1;
492 if (IS_INETD) 514 if (IS_INETD)
493 return 0; 515 return 0;
494 free_session(ts); 516 free_session(ts);
495 ts = next; 517 ts = next;
496 continue; 518 continue;
497 } 519 }
498 ts->wridx1 += w;
499 ts->size1 -= w; 520 ts->size1 -= w;
500 if (ts->wridx1 == BUFSIZE) 521 ts->wridx1 += w;
522 if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
501 ts->wridx1 = 0; 523 ts->wridx1 = 0;
502 } 524 }
503 525 skip1:
504 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) { 526 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
505 /* Write to socket from buffer 2. */ 527 /* Write to socket from buffer 2. */
506 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2); 528 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
507 w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen); 529 w = safe_write(ts->sockfd_write, TS_BUF2 + ts->wridx2, maxlen);
508 /* needed? if (w < 0 && errno == EAGAIN) continue; */
509 if (w < 0) { 530 if (w < 0) {
531 if (errno == EAGAIN)
532 goto skip2;
510 if (IS_INETD) 533 if (IS_INETD)
511 return 0; 534 return 0;
512 free_session(ts); 535 free_session(ts);
513 ts = next; 536 ts = next;
514 continue; 537 continue;
515 } 538 }
516 ts->wridx2 += w;
517 ts->size2 -= w; 539 ts->size2 -= w;
518 if (ts->wridx2 == BUFSIZE) 540 ts->wridx2 += w;
541 if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
519 ts->wridx2 = 0; 542 ts->wridx2 = 0;
520 } 543 }
521 544 skip2:
522 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) { 545#if 0
546 /* Not strictly needed, but allows for bigger reads in common case */
547 if (ts->size1 == 0) {
548 ts->rdidx1 = 0;
549 ts->wridx1 = 0;
550 }
551 if (ts->size2 == 0) {
552 ts->rdidx2 = 0;
553 ts->wridx2 = 0;
554 }
555#endif
556 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
523 /* Read from socket to buffer 1. */ 557 /* Read from socket to buffer 1. */
524 maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1); 558 maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
525 r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen); 559 r = safe_read(ts->sockfd_read, TS_BUF1 + ts->rdidx1, maxlen);
526 if (r < 0 && errno == EAGAIN) continue;
527 if (r <= 0) { 560 if (r <= 0) {
561 if (r < 0 && errno == EAGAIN)
562 goto skip3;
528 if (IS_INETD) 563 if (IS_INETD)
529 return 0; 564 return 0;
530 free_session(ts); 565 free_session(ts);
531 ts = next; 566 ts = next;
532 continue; 567 continue;
533 } 568 }
534 if (!ts->buf1[ts->rdidx1 + r - 1]) 569 /* Ignore trailing NUL if it is there */
570 if (!TS_BUF1[ts->rdidx1 + r - 1]) {
535 if (!--r) 571 if (!--r)
536 continue; 572 goto skip3;
537 ts->rdidx1 += r; 573 }
538 ts->size1 += r; 574 ts->size1 += r;
539 if (ts->rdidx1 == BUFSIZE) 575 ts->rdidx1 += r;
576 if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
540 ts->rdidx1 = 0; 577 ts->rdidx1 = 0;
541 } 578 }
542 579 skip3:
543 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) { 580 if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
544 /* Read from pty to buffer 2. */ 581 /* Read from pty to buffer 2. */
545 maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2); 582 maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
546 r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen); 583 r = safe_read(ts->ptyfd, TS_BUF2 + ts->rdidx2, maxlen);
547 if (r < 0 && errno == EAGAIN) continue;
548 if (r <= 0) { 584 if (r <= 0) {
585 if (r < 0 && errno == EAGAIN)
586 goto skip4;
549 if (IS_INETD) 587 if (IS_INETD)
550 return 0; 588 return 0;
551 free_session(ts); 589 free_session(ts);
552 ts = next; 590 ts = next;
553 continue; 591 continue;
554 } 592 }
555 ts->rdidx2 += r;
556 ts->size2 += r; 593 ts->size2 += r;
557 if (ts->rdidx2 == BUFSIZE) 594 ts->rdidx2 += r;
595 if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
558 ts->rdidx2 = 0; 596 ts->rdidx2 = 0;
559 } 597 }
560 598 skip4:
561 if (ts->size1 == 0) {
562 ts->rdidx1 = 0;
563 ts->wridx1 = 0;
564 }
565 if (ts->size2 == 0) {
566 ts->rdidx2 = 0;
567 ts->wridx2 = 0;
568 }
569 ts = next; 599 ts = next;
570 } 600 }
571 goto again; 601 goto again;