diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2009-06-10 13:38:08 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2009-06-10 13:38:08 +0200 |
commit | 1d77db8459e1948cdde407f5010f772b81048dbd (patch) | |
tree | 549eeafb42f220047f931237207851693d87c825 /networking/telnetd.c | |
parent | a4bcbd0e0416bb4965488ec1f16039aa302f8b91 (diff) | |
download | busybox-w32-1d77db8459e1948cdde407f5010f772b81048dbd.tar.gz busybox-w32-1d77db8459e1948cdde407f5010f772b81048dbd.tar.bz2 busybox-w32-1d77db8459e1948cdde407f5010f772b81048dbd.zip |
telnetd: more compact version of the fix for stray open fds
function old new delta
telnetd_main 1520 1527 +7
make_new_session 510 416 -94
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/1 up/down: 7/-94) Total: -87 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'networking/telnetd.c')
-rw-r--r-- | networking/telnetd.c | 90 |
1 files changed, 39 insertions, 51 deletions
diff --git a/networking/telnetd.c b/networking/telnetd.c index 6a8190b14..540387f1a 100644 --- a/networking/telnetd.c +++ b/networking/telnetd.c | |||
@@ -32,11 +32,12 @@ | |||
32 | #endif | 32 | #endif |
33 | #include <arpa/telnet.h> | 33 | #include <arpa/telnet.h> |
34 | 34 | ||
35 | /* Structure that describes a session */ | ||
36 | struct tsession { | 35 | struct tsession { |
37 | struct tsession *next; | 36 | struct tsession *next; |
38 | pid_t shell_pid; | 37 | pid_t shell_pid; |
39 | int sockfd_read, sockfd_write, ptyfd; | 38 | int sockfd_read; |
39 | int sockfd_write; | ||
40 | int ptyfd; | ||
40 | 41 | ||
41 | /* two circular buffers */ | 42 | /* two circular buffers */ |
42 | /*char *buf1, *buf2;*/ | 43 | /*char *buf1, *buf2;*/ |
@@ -125,9 +126,9 @@ remove_iacs(struct tsession *ts, int *pnum_totty) | |||
125 | * TELOPT_NAWS support! | 126 | * TELOPT_NAWS support! |
126 | */ | 127 | */ |
127 | if ((ptr+2) >= end) { | 128 | if ((ptr+2) >= end) { |
128 | /* only the beginning of the IAC is in the | 129 | /* Only the beginning of the IAC is in the |
129 | buffer we were asked to process, we can't | 130 | buffer we were asked to process, we can't |
130 | process this char. */ | 131 | process this char */ |
131 | break; | 132 | break; |
132 | } | 133 | } |
133 | /* | 134 | /* |
@@ -153,13 +154,13 @@ remove_iacs(struct tsession *ts, int *pnum_totty) | |||
153 | 154 | ||
154 | num_totty = totty - ptr0; | 155 | num_totty = totty - ptr0; |
155 | *pnum_totty = num_totty; | 156 | *pnum_totty = num_totty; |
156 | /* the difference between ptr and totty is number of iacs | 157 | /* The difference between ptr and totty is number of iacs |
157 | we removed from the stream. Adjust buf1 accordingly. */ | 158 | we removed from the stream. Adjust buf1 accordingly */ |
158 | if ((ptr - totty) == 0) /* 99.999% of cases */ | 159 | if ((ptr - totty) == 0) /* 99.999% of cases */ |
159 | return ptr0; | 160 | return ptr0; |
160 | ts->wridx1 += ptr - totty; | 161 | ts->wridx1 += ptr - totty; |
161 | ts->size1 -= ptr - totty; | 162 | ts->size1 -= ptr - totty; |
162 | /* move chars meant for the terminal towards the end of the buffer */ | 163 | /* Move chars meant for the terminal towards the end of the buffer */ |
163 | return memmove(ptr - num_totty, ptr0, num_totty); | 164 | return memmove(ptr - num_totty, ptr0, num_totty); |
164 | } | 165 | } |
165 | 166 | ||
@@ -216,7 +217,7 @@ enum { | |||
216 | 217 | ||
217 | static struct tsession * | 218 | static struct tsession * |
218 | make_new_session( | 219 | make_new_session( |
219 | IF_FEATURE_TELNETD_STANDALONE(int master_fd, int sock) | 220 | IF_FEATURE_TELNETD_STANDALONE(int sock) |
220 | IF_NOT_FEATURE_TELNETD_STANDALONE(void) | 221 | IF_NOT_FEATURE_TELNETD_STANDALONE(void) |
221 | ) { | 222 | ) { |
222 | const char *login_argv[2]; | 223 | const char *login_argv[2]; |
@@ -228,18 +229,20 @@ make_new_session( | |||
228 | /*ts->buf1 = (char *)(ts + 1);*/ | 229 | /*ts->buf1 = (char *)(ts + 1);*/ |
229 | /*ts->buf2 = ts->buf1 + BUFSIZE;*/ | 230 | /*ts->buf2 = ts->buf1 + BUFSIZE;*/ |
230 | 231 | ||
231 | /* Got a new connection, set up a tty. */ | 232 | /* Got a new connection, set up a tty */ |
232 | fd = xgetpty(tty_name); | 233 | fd = xgetpty(tty_name); |
233 | if (fd > G.maxfd) | 234 | if (fd > G.maxfd) |
234 | G.maxfd = fd; | 235 | G.maxfd = fd; |
235 | ts->ptyfd = fd; | 236 | ts->ptyfd = fd; |
236 | ndelay_on(fd); | 237 | ndelay_on(fd); |
238 | close_on_exec_on(fd); | ||
239 | |||
237 | #if ENABLE_FEATURE_TELNETD_STANDALONE | 240 | #if ENABLE_FEATURE_TELNETD_STANDALONE |
238 | ts->sockfd_read = sock; | ||
239 | /* SO_KEEPALIVE by popular demand */ | 241 | /* SO_KEEPALIVE by popular demand */ |
240 | setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); | 242 | setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); |
243 | ts->sockfd_read = sock; | ||
241 | ndelay_on(sock); | 244 | ndelay_on(sock); |
242 | if (!sock) { /* We are called with fd 0 - we are in inetd mode */ | 245 | if (sock == 0) { /* We are called with fd 0 - we are in inetd mode */ |
243 | sock++; /* so use fd 1 for output */ | 246 | sock++; /* so use fd 1 for output */ |
244 | ndelay_on(sock); | 247 | ndelay_on(sock); |
245 | } | 248 | } |
@@ -254,6 +257,7 @@ make_new_session( | |||
254 | ndelay_on(0); | 257 | ndelay_on(0); |
255 | ndelay_on(1); | 258 | ndelay_on(1); |
256 | #endif | 259 | #endif |
260 | |||
257 | /* Make the telnet client understand we will echo characters so it | 261 | /* Make the telnet client understand we will echo characters so it |
258 | * should not do it locally. We don't tell the client to run linemode, | 262 | * should not do it locally. We don't tell the client to run linemode, |
259 | * because we want to handle line editing and tab completion and other | 263 | * because we want to handle line editing and tab completion and other |
@@ -303,42 +307,20 @@ make_new_session( | |||
303 | /* Restore default signal handling ASAP */ | 307 | /* Restore default signal handling ASAP */ |
304 | bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL); | 308 | bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL); |
305 | 309 | ||
306 | #if ENABLE_FEATURE_TELNETD_STANDALONE | ||
307 | if (!(option_mask32 & OPT_INETD)) { | ||
308 | struct tsession *tp = G.sessions; | ||
309 | while (tp) { | ||
310 | close(tp->ptyfd); | ||
311 | close(tp->sockfd_read); | ||
312 | /* sockfd_write == sockfd_read for standalone telnetd */ | ||
313 | /*close(tp->sockfd_write);*/ | ||
314 | tp = tp->next; | ||
315 | } | ||
316 | } | ||
317 | #endif | ||
318 | |||
319 | /* Make new session and process group */ | 310 | /* Make new session and process group */ |
320 | setsid(); | 311 | setsid(); |
321 | 312 | ||
322 | close(fd); | 313 | /* Open the child's side of the tty */ |
323 | #if ENABLE_FEATURE_TELNETD_STANDALONE | ||
324 | if (master_fd >= 0) | ||
325 | close(master_fd); | ||
326 | close(sock); | ||
327 | #endif | ||
328 | /* Open the child's side of the tty. */ | ||
329 | /* NB: setsid() disconnects from any previous ctty's. Therefore | 314 | /* NB: setsid() disconnects from any previous ctty's. Therefore |
330 | * we must open child's side of the tty AFTER setsid! */ | 315 | * we must open child's side of the tty AFTER setsid! */ |
331 | #if ENABLE_FEATURE_TELNETD_STANDALONE | 316 | close(0); |
332 | if (sock != 0) /* if we did not close 0 already */ | ||
333 | #endif | ||
334 | close(0); | ||
335 | xopen(tty_name, O_RDWR); /* becomes our ctty */ | 317 | xopen(tty_name, O_RDWR); /* becomes our ctty */ |
336 | xdup2(0, 1); | 318 | xdup2(0, 1); |
337 | xdup2(0, 2); | 319 | xdup2(0, 2); |
338 | tcsetpgrp(0, getpid()); /* switch this tty's process group to us */ | 320 | tcsetpgrp(0, getpid()); /* switch this tty's process group to us */ |
339 | 321 | ||
340 | /* The pseudo-terminal allocated to the client is configured to operate in | 322 | /* The pseudo-terminal allocated to the client is configured to operate |
341 | * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */ | 323 | * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */ |
342 | tcgetattr(0, &termbuf); | 324 | tcgetattr(0, &termbuf); |
343 | termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */ | 325 | termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */ |
344 | termbuf.c_oflag |= ONLCR | XTABS; | 326 | termbuf.c_oflag |= ONLCR | XTABS; |
@@ -359,7 +341,10 @@ make_new_session( | |||
359 | login_argv[0] = G.loginpath; | 341 | login_argv[0] = G.loginpath; |
360 | login_argv[1] = NULL; | 342 | login_argv[1] = NULL; |
361 | /* exec busybox applet (if PREFER_APPLETS=y), if that fails, | 343 | /* exec busybox applet (if PREFER_APPLETS=y), if that fails, |
362 | * exec external program */ | 344 | * exec external program. |
345 | * NB: sock is either 0 or has CLOEXEC set on it. | ||
346 | * fd has CLOEXEC set on it too. These two fds will be closed here. | ||
347 | */ | ||
363 | BB_EXECVP(G.loginpath, (char **)login_argv); | 348 | BB_EXECVP(G.loginpath, (char **)login_argv); |
364 | /* _exit is safer with vfork, and we shouldn't send message | 349 | /* _exit is safer with vfork, and we shouldn't send message |
365 | * to remote clients anyway */ | 350 | * to remote clients anyway */ |
@@ -496,12 +481,13 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv) | |||
496 | 481 | ||
497 | #if ENABLE_FEATURE_TELNETD_STANDALONE | 482 | #if ENABLE_FEATURE_TELNETD_STANDALONE |
498 | if (IS_INETD) { | 483 | if (IS_INETD) { |
499 | G.sessions = make_new_session(-1, 0); | 484 | G.sessions = make_new_session(0); |
500 | if (!G.sessions) /* pty opening or vfork problem, exit */ | 485 | if (!G.sessions) /* pty opening or vfork problem, exit */ |
501 | return 1; /* make_new_session prints error message */ | 486 | return 1; /* make_new_session prints error message */ |
502 | } else { | 487 | } else { |
503 | master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr); | 488 | master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr); |
504 | xlisten(master_fd, 1); | 489 | xlisten(master_fd, 1); |
490 | close_on_exec_on(master_fd); | ||
505 | } | 491 | } |
506 | #else | 492 | #else |
507 | G.sessions = make_new_session(); | 493 | G.sessions = make_new_session(); |
@@ -518,8 +504,8 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv) | |||
518 | signal(SIGCHLD, SIG_IGN); | 504 | signal(SIGCHLD, SIG_IGN); |
519 | 505 | ||
520 | /* | 506 | /* |
521 | This is how the buffers are used. The arrows indicate the movement | 507 | This is how the buffers are used. The arrows indicate data flow. |
522 | of data. | 508 | |
523 | +-------+ wridx1++ +------+ rdidx1++ +----------+ | 509 | +-------+ wridx1++ +------+ rdidx1++ +----------+ |
524 | | | <-------------- | buf1 | <-------------- | | | 510 | | | <-------------- | buf1 | <-------------- | | |
525 | | | size1-- +------+ size1++ | | | 511 | | | size1-- +------+ size1++ | | |
@@ -545,7 +531,7 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv) | |||
545 | * before each select. Can be a problem with 500+ connections. */ | 531 | * before each select. Can be a problem with 500+ connections. */ |
546 | ts = G.sessions; | 532 | ts = G.sessions; |
547 | while (ts) { | 533 | while (ts) { |
548 | struct tsession *next = ts->next; /* in case we free ts. */ | 534 | struct tsession *next = ts->next; /* in case we free ts */ |
549 | if (ts->shell_pid == -1) { | 535 | if (ts->shell_pid == -1) { |
550 | /* Child died and we detected that */ | 536 | /* Child died and we detected that */ |
551 | free_session(ts); | 537 | free_session(ts); |
@@ -575,7 +561,7 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv) | |||
575 | goto again; /* EINTR or ENOMEM */ | 561 | goto again; /* EINTR or ENOMEM */ |
576 | 562 | ||
577 | #if ENABLE_FEATURE_TELNETD_STANDALONE | 563 | #if ENABLE_FEATURE_TELNETD_STANDALONE |
578 | /* First check for and accept new sessions. */ | 564 | /* Check for and accept new sessions */ |
579 | if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) { | 565 | if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) { |
580 | int fd; | 566 | int fd; |
581 | struct tsession *new_ts; | 567 | struct tsession *new_ts; |
@@ -583,8 +569,10 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv) | |||
583 | fd = accept(master_fd, NULL, NULL); | 569 | fd = accept(master_fd, NULL, NULL); |
584 | if (fd < 0) | 570 | if (fd < 0) |
585 | goto again; | 571 | goto again; |
586 | /* Create a new session and link it into our active list */ | 572 | close_on_exec_on(fd); |
587 | new_ts = make_new_session(master_fd, fd); | 573 | |
574 | /* Create a new session and link it into active list */ | ||
575 | new_ts = make_new_session(fd); | ||
588 | if (new_ts) { | 576 | if (new_ts) { |
589 | new_ts->next = G.sessions; | 577 | new_ts->next = G.sessions; |
590 | G.sessions = new_ts; | 578 | G.sessions = new_ts; |
@@ -594,15 +582,15 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv) | |||
594 | } | 582 | } |
595 | #endif | 583 | #endif |
596 | 584 | ||
597 | /* Then check for data tunneling. */ | 585 | /* Then check for data tunneling */ |
598 | ts = G.sessions; | 586 | ts = G.sessions; |
599 | while (ts) { /* For all sessions... */ | 587 | while (ts) { /* For all sessions... */ |
600 | struct tsession *next = ts->next; /* in case we free ts. */ | 588 | struct tsession *next = ts->next; /* in case we free ts */ |
601 | 589 | ||
602 | if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) { | 590 | if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) { |
603 | int num_totty; | 591 | int num_totty; |
604 | unsigned char *ptr; | 592 | unsigned char *ptr; |
605 | /* Write to pty from buffer 1. */ | 593 | /* Write to pty from buffer 1 */ |
606 | ptr = remove_iacs(ts, &num_totty); | 594 | ptr = remove_iacs(ts, &num_totty); |
607 | count = safe_write(ts->ptyfd, ptr, num_totty); | 595 | count = safe_write(ts->ptyfd, ptr, num_totty); |
608 | if (count < 0) { | 596 | if (count < 0) { |
@@ -617,7 +605,7 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv) | |||
617 | } | 605 | } |
618 | skip1: | 606 | skip1: |
619 | if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) { | 607 | if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) { |
620 | /* Write to socket from buffer 2. */ | 608 | /* Write to socket from buffer 2 */ |
621 | count = MIN(BUFSIZE - ts->wridx2, ts->size2); | 609 | count = MIN(BUFSIZE - ts->wridx2, ts->size2); |
622 | count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count); | 610 | count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count); |
623 | if (count < 0) { | 611 | if (count < 0) { |
@@ -647,7 +635,7 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv) | |||
647 | } | 635 | } |
648 | 636 | ||
649 | if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) { | 637 | if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) { |
650 | /* Read from socket to buffer 1. */ | 638 | /* Read from socket to buffer 1 */ |
651 | count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1); | 639 | count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1); |
652 | count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count); | 640 | count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count); |
653 | if (count <= 0) { | 641 | if (count <= 0) { |
@@ -666,7 +654,7 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv) | |||
666 | } | 654 | } |
667 | skip3: | 655 | skip3: |
668 | if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) { | 656 | if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) { |
669 | /* Read from pty to buffer 2. */ | 657 | /* Read from pty to buffer 2 */ |
670 | count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2); | 658 | count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2); |
671 | count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count); | 659 | count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count); |
672 | if (count <= 0) { | 660 | if (count <= 0) { |