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 | |
| 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>
| -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) { |
