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>
This commit is contained in:
Denys Vlasenko 2009-06-10 13:38:08 +02:00
parent a4bcbd0e04
commit 1d77db8459

View File

@ -32,11 +32,12 @@
#endif #endif
#include <arpa/telnet.h> #include <arpa/telnet.h>
/* Structure that describes a session */
struct tsession { struct tsession {
struct tsession *next; struct tsession *next;
pid_t shell_pid; pid_t shell_pid;
int sockfd_read, sockfd_write, ptyfd; int sockfd_read;
int sockfd_write;
int ptyfd;
/* two circular buffers */ /* two circular buffers */
/*char *buf1, *buf2;*/ /*char *buf1, *buf2;*/
@ -125,9 +126,9 @@ remove_iacs(struct tsession *ts, int *pnum_totty)
* TELOPT_NAWS support! * TELOPT_NAWS support!
*/ */
if ((ptr+2) >= end) { if ((ptr+2) >= end) {
/* only the beginning of the IAC is in the /* Only the beginning of the IAC is in the
buffer we were asked to process, we can't buffer we were asked to process, we can't
process this char. */ process this char */
break; break;
} }
/* /*
@ -153,13 +154,13 @@ remove_iacs(struct tsession *ts, int *pnum_totty)
num_totty = totty - ptr0; num_totty = totty - ptr0;
*pnum_totty = num_totty; *pnum_totty = num_totty;
/* the difference between ptr and totty is number of iacs /* The difference between ptr and totty is number of iacs
we removed from the stream. Adjust buf1 accordingly. */ we removed from the stream. Adjust buf1 accordingly */
if ((ptr - totty) == 0) /* 99.999% of cases */ if ((ptr - totty) == 0) /* 99.999% of cases */
return ptr0; return ptr0;
ts->wridx1 += ptr - totty; ts->wridx1 += ptr - totty;
ts->size1 -= ptr - totty; ts->size1 -= ptr - totty;
/* move chars meant for the terminal towards the end of the buffer */ /* Move chars meant for the terminal towards the end of the buffer */
return memmove(ptr - num_totty, ptr0, num_totty); return memmove(ptr - num_totty, ptr0, num_totty);
} }
@ -216,7 +217,7 @@ enum {
static struct tsession * static struct tsession *
make_new_session( make_new_session(
IF_FEATURE_TELNETD_STANDALONE(int master_fd, int sock) IF_FEATURE_TELNETD_STANDALONE(int sock)
IF_NOT_FEATURE_TELNETD_STANDALONE(void) IF_NOT_FEATURE_TELNETD_STANDALONE(void)
) { ) {
const char *login_argv[2]; const char *login_argv[2];
@ -228,18 +229,20 @@ make_new_session(
/*ts->buf1 = (char *)(ts + 1);*/ /*ts->buf1 = (char *)(ts + 1);*/
/*ts->buf2 = ts->buf1 + BUFSIZE;*/ /*ts->buf2 = ts->buf1 + BUFSIZE;*/
/* Got a new connection, set up a tty. */ /* Got a new connection, set up a tty */
fd = xgetpty(tty_name); fd = xgetpty(tty_name);
if (fd > G.maxfd) if (fd > G.maxfd)
G.maxfd = fd; G.maxfd = fd;
ts->ptyfd = fd; ts->ptyfd = fd;
ndelay_on(fd); ndelay_on(fd);
close_on_exec_on(fd);
#if ENABLE_FEATURE_TELNETD_STANDALONE #if ENABLE_FEATURE_TELNETD_STANDALONE
ts->sockfd_read = sock;
/* SO_KEEPALIVE by popular demand */ /* SO_KEEPALIVE by popular demand */
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
ts->sockfd_read = sock;
ndelay_on(sock); ndelay_on(sock);
if (!sock) { /* We are called with fd 0 - we are in inetd mode */ if (sock == 0) { /* We are called with fd 0 - we are in inetd mode */
sock++; /* so use fd 1 for output */ sock++; /* so use fd 1 for output */
ndelay_on(sock); ndelay_on(sock);
} }
@ -254,6 +257,7 @@ make_new_session(
ndelay_on(0); ndelay_on(0);
ndelay_on(1); ndelay_on(1);
#endif #endif
/* Make the telnet client understand we will echo characters so it /* Make the telnet client understand we will echo characters so it
* should not do it locally. We don't tell the client to run linemode, * should not do it locally. We don't tell the client to run linemode,
* because we want to handle line editing and tab completion and other * because we want to handle line editing and tab completion and other
@ -303,42 +307,20 @@ make_new_session(
/* Restore default signal handling ASAP */ /* Restore default signal handling ASAP */
bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL); bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
#if ENABLE_FEATURE_TELNETD_STANDALONE
if (!(option_mask32 & OPT_INETD)) {
struct tsession *tp = G.sessions;
while (tp) {
close(tp->ptyfd);
close(tp->sockfd_read);
/* sockfd_write == sockfd_read for standalone telnetd */
/*close(tp->sockfd_write);*/
tp = tp->next;
}
}
#endif
/* Make new session and process group */ /* Make new session and process group */
setsid(); setsid();
close(fd); /* Open the child's side of the tty */
#if ENABLE_FEATURE_TELNETD_STANDALONE
if (master_fd >= 0)
close(master_fd);
close(sock);
#endif
/* Open the child's side of the tty. */
/* NB: setsid() disconnects from any previous ctty's. Therefore /* NB: setsid() disconnects from any previous ctty's. Therefore
* we must open child's side of the tty AFTER setsid! */ * we must open child's side of the tty AFTER setsid! */
#if ENABLE_FEATURE_TELNETD_STANDALONE close(0);
if (sock != 0) /* if we did not close 0 already */
#endif
close(0);
xopen(tty_name, O_RDWR); /* becomes our ctty */ xopen(tty_name, O_RDWR); /* becomes our ctty */
xdup2(0, 1); xdup2(0, 1);
xdup2(0, 2); xdup2(0, 2);
tcsetpgrp(0, getpid()); /* switch this tty's process group to us */ tcsetpgrp(0, getpid()); /* switch this tty's process group to us */
/* The pseudo-terminal allocated to the client is configured to operate in /* The pseudo-terminal allocated to the client is configured to operate
* cooked mode, and with XTABS CRMOD enabled (see tty(4)). */ * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */
tcgetattr(0, &termbuf); tcgetattr(0, &termbuf);
termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */ termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
termbuf.c_oflag |= ONLCR | XTABS; termbuf.c_oflag |= ONLCR | XTABS;
@ -359,7 +341,10 @@ make_new_session(
login_argv[0] = G.loginpath; login_argv[0] = G.loginpath;
login_argv[1] = NULL; login_argv[1] = NULL;
/* exec busybox applet (if PREFER_APPLETS=y), if that fails, /* exec busybox applet (if PREFER_APPLETS=y), if that fails,
* exec external program */ * exec external program.
* NB: sock is either 0 or has CLOEXEC set on it.
* fd has CLOEXEC set on it too. These two fds will be closed here.
*/
BB_EXECVP(G.loginpath, (char **)login_argv); BB_EXECVP(G.loginpath, (char **)login_argv);
/* _exit is safer with vfork, and we shouldn't send message /* _exit is safer with vfork, and we shouldn't send message
* to remote clients anyway */ * to remote clients anyway */
@ -496,12 +481,13 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
#if ENABLE_FEATURE_TELNETD_STANDALONE #if ENABLE_FEATURE_TELNETD_STANDALONE
if (IS_INETD) { if (IS_INETD) {
G.sessions = make_new_session(-1, 0); G.sessions = make_new_session(0);
if (!G.sessions) /* pty opening or vfork problem, exit */ if (!G.sessions) /* pty opening or vfork problem, exit */
return 1; /* make_new_session prints error message */ return 1; /* make_new_session prints error message */
} else { } else {
master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr); master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
xlisten(master_fd, 1); xlisten(master_fd, 1);
close_on_exec_on(master_fd);
} }
#else #else
G.sessions = make_new_session(); G.sessions = make_new_session();
@ -518,8 +504,8 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
signal(SIGCHLD, SIG_IGN); signal(SIGCHLD, SIG_IGN);
/* /*
This is how the buffers are used. The arrows indicate the movement This is how the buffers are used. The arrows indicate data flow.
of data.
+-------+ wridx1++ +------+ rdidx1++ +----------+ +-------+ wridx1++ +------+ rdidx1++ +----------+
| | <-------------- | buf1 | <-------------- | | | | <-------------- | buf1 | <-------------- | |
| | size1-- +------+ size1++ | | | | size1-- +------+ size1++ | |
@ -545,7 +531,7 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
* before each select. Can be a problem with 500+ connections. */ * before each select. Can be a problem with 500+ connections. */
ts = G.sessions; ts = G.sessions;
while (ts) { while (ts) {
struct tsession *next = ts->next; /* in case we free ts. */ struct tsession *next = ts->next; /* in case we free ts */
if (ts->shell_pid == -1) { if (ts->shell_pid == -1) {
/* Child died and we detected that */ /* Child died and we detected that */
free_session(ts); free_session(ts);
@ -575,7 +561,7 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
goto again; /* EINTR or ENOMEM */ goto again; /* EINTR or ENOMEM */
#if ENABLE_FEATURE_TELNETD_STANDALONE #if ENABLE_FEATURE_TELNETD_STANDALONE
/* First check for and accept new sessions. */ /* Check for and accept new sessions */
if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) { if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
int fd; int fd;
struct tsession *new_ts; struct tsession *new_ts;
@ -583,8 +569,10 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
fd = accept(master_fd, NULL, NULL); fd = accept(master_fd, NULL, NULL);
if (fd < 0) if (fd < 0)
goto again; goto again;
/* Create a new session and link it into our active list */ close_on_exec_on(fd);
new_ts = make_new_session(master_fd, fd);
/* Create a new session and link it into active list */
new_ts = make_new_session(fd);
if (new_ts) { if (new_ts) {
new_ts->next = G.sessions; new_ts->next = G.sessions;
G.sessions = new_ts; G.sessions = new_ts;
@ -594,15 +582,15 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
} }
#endif #endif
/* Then check for data tunneling. */ /* Then check for data tunneling */
ts = G.sessions; ts = G.sessions;
while (ts) { /* For all sessions... */ while (ts) { /* For all sessions... */
struct tsession *next = ts->next; /* in case we free ts. */ struct tsession *next = ts->next; /* in case we free ts */
if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) { if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
int num_totty; int num_totty;
unsigned char *ptr; unsigned char *ptr;
/* Write to pty from buffer 1. */ /* Write to pty from buffer 1 */
ptr = remove_iacs(ts, &num_totty); ptr = remove_iacs(ts, &num_totty);
count = safe_write(ts->ptyfd, ptr, num_totty); count = safe_write(ts->ptyfd, ptr, num_totty);
if (count < 0) { if (count < 0) {
@ -617,7 +605,7 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
} }
skip1: skip1:
if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) { if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
/* Write to socket from buffer 2. */ /* Write to socket from buffer 2 */
count = MIN(BUFSIZE - ts->wridx2, ts->size2); count = MIN(BUFSIZE - ts->wridx2, ts->size2);
count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count); count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count);
if (count < 0) { if (count < 0) {
@ -647,7 +635,7 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
} }
if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) { if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
/* Read from socket to buffer 1. */ /* Read from socket to buffer 1 */
count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1); count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count); count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count);
if (count <= 0) { if (count <= 0) {
@ -666,7 +654,7 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
} }
skip3: skip3:
if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) { if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
/* Read from pty to buffer 2. */ /* Read from pty to buffer 2 */
count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2); count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count); count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count);
if (count <= 0) { if (count <= 0) {