telnetd: at Alexander Kriegisch <Alexander@kriegisch.name> insistence
add an option to close sessions as soon as child exits. Maybe it should be a CONFIG option. OTOH, maybe it should be always on, as it mimics, say, getty's behaviour. function old new delta handle_sigchld - 49 +49 telnetd_main 1312 1355 +43 .rodata 123429 123466 +37 packed_usage 22770 22806 +36 make_new_session 525 532 +7 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 4/0 up/down: 172/0) Total: 172 bytes text data bss dec hex filename 676285 2538 12104 690927 a8aef busybox_old 676421 2538 12104 691063 a8b77 busybox_unstripped
This commit is contained in:
parent
10916c5c6b
commit
2450c450ab
@ -3517,6 +3517,8 @@ USE_FEATURE_RUN_PARTS_FANCY("\n -l Prints names of all matching files even when
|
|||||||
"\n\nOptions:" \
|
"\n\nOptions:" \
|
||||||
"\n -l LOGIN Exec LOGIN on connect" \
|
"\n -l LOGIN Exec LOGIN on connect" \
|
||||||
"\n -f issue_file Display issue_file instead of /etc/issue" \
|
"\n -f issue_file Display issue_file instead of /etc/issue" \
|
||||||
|
"\n -K Close connection as soon as login exits" \
|
||||||
|
"\n (normally wait until all programs close slave pty)" \
|
||||||
USE_FEATURE_TELNETD_STANDALONE( \
|
USE_FEATURE_TELNETD_STANDALONE( \
|
||||||
"\n -p PORT Port to listen to" \
|
"\n -p PORT Port to listen to" \
|
||||||
"\n -b ADDR Address to bind to" \
|
"\n -b ADDR Address to bind to" \
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
struct tsession {
|
struct tsession {
|
||||||
struct tsession *next;
|
struct tsession *next;
|
||||||
int sockfd_read, sockfd_write, ptyfd;
|
int sockfd_read, sockfd_write, ptyfd;
|
||||||
/*int shell_pid;*/
|
int shell_pid;
|
||||||
|
|
||||||
/* two circular buffers */
|
/* two circular buffers */
|
||||||
/*char *buf1, *buf2;*/
|
/*char *buf1, *buf2;*/
|
||||||
@ -265,7 +265,7 @@ make_new_session(
|
|||||||
}
|
}
|
||||||
if (pid > 0) {
|
if (pid > 0) {
|
||||||
/* Parent */
|
/* Parent */
|
||||||
/*ts->shell_pid = pid;*/
|
ts->shell_pid = pid;
|
||||||
return ts;
|
return ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,7 +305,8 @@ make_new_session(
|
|||||||
login_argv[0] = loginpath;
|
login_argv[0] = loginpath;
|
||||||
login_argv[1] = NULL;
|
login_argv[1] = NULL;
|
||||||
execvp(loginpath, (char **)login_argv);
|
execvp(loginpath, (char **)login_argv);
|
||||||
/* Safer with vfork, and we shouldn't send this to the client anyway */
|
/* Safer with vfork, and we shouldn't send message
|
||||||
|
* to remote clients anyway */
|
||||||
_exit(1); /*bb_perror_msg_and_die("execv %s", loginpath);*/
|
_exit(1); /*bb_perror_msg_and_die("execv %s", loginpath);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,6 +358,24 @@ void free_session(struct tsession *ts);
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void handle_sigchld(int sig)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
struct tsession *ts;
|
||||||
|
|
||||||
|
pid = waitpid(-1, &sig, WNOHANG);
|
||||||
|
if (pid > 0) {
|
||||||
|
ts = sessions;
|
||||||
|
while (ts) {
|
||||||
|
if (ts->shell_pid == pid) {
|
||||||
|
ts->shell_pid = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ts = ts->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
||||||
int telnetd_main(int argc, char **argv)
|
int telnetd_main(int argc, char **argv)
|
||||||
@ -379,29 +398,35 @@ int telnetd_main(int argc, char **argv)
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
enum {
|
enum {
|
||||||
OPT_INETD = (1 << 2) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
|
OPT_WATCHCHILD = (1 << 2), /* -K */
|
||||||
OPT_PORT = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p */
|
OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
|
||||||
OPT_FOREGROUND = (1 << 5) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
|
OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p */
|
||||||
|
OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* If !STANDALONE, we accept (and ignore) -i, thus people
|
/* Even if !STANDALONE, we accept (and ignore) -i, thus people
|
||||||
* don't need to guess whether it's ok to pass -i to us */
|
* don't need to guess whether it's ok to pass -i to us */
|
||||||
opt = getopt32(argv, "f:l:i" USE_FEATURE_TELNETD_STANDALONE("p:b:F"),
|
opt = getopt32(argv, "f:l:Ki" USE_FEATURE_TELNETD_STANDALONE("p:b:F"),
|
||||||
&issuefile, &loginpath
|
&issuefile, &loginpath
|
||||||
USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
|
USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
|
||||||
|
if (!IS_INETD /*&& !re_execed*/) {
|
||||||
|
/* inform that we start in standalone mode?
|
||||||
|
* May be useful when people forget to give -i */
|
||||||
|
/*bb_error_msg("listening for connections");*/
|
||||||
|
if (!(opt & OPT_FOREGROUND)) {
|
||||||
|
/* DAEMON_CHDIR_ROOT was giving inconsistent
|
||||||
|
* behavior with/wthout -F, -i */
|
||||||
|
bb_daemonize_or_rexec(0 /*DAEMON_CHDIR_ROOT*/, argv);
|
||||||
|
}
|
||||||
|
}
|
||||||
/* Redirect log to syslog early, if needed */
|
/* Redirect log to syslog early, if needed */
|
||||||
if (IS_INETD || !(opt & OPT_FOREGROUND)) {
|
if (IS_INETD || !(opt & OPT_FOREGROUND)) {
|
||||||
openlog(applet_name, 0, LOG_USER);
|
openlog(applet_name, 0, LOG_USER);
|
||||||
logmode = LOGMODE_SYSLOG;
|
logmode = LOGMODE_SYSLOG;
|
||||||
}
|
}
|
||||||
//if (opt & 1) // -f
|
|
||||||
//if (opt & 2) // -l
|
|
||||||
USE_FEATURE_TELNETD_STANDALONE(
|
USE_FEATURE_TELNETD_STANDALONE(
|
||||||
if (opt & OPT_PORT) // -p
|
if (opt & OPT_PORT)
|
||||||
portnbr = xatou16(opt_portnbr);
|
portnbr = xatou16(opt_portnbr);
|
||||||
//if (opt & 8) // -b
|
|
||||||
//if (opt & 0x10) // -F
|
|
||||||
//if (opt & 0x20) // -i
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Used to check access(loginpath, X_OK) here. Pointless.
|
/* Used to check access(loginpath, X_OK) here. Pointless.
|
||||||
@ -413,12 +438,8 @@ int telnetd_main(int argc, char **argv)
|
|||||||
if (!sessions) /* pty opening or vfork problem, exit */
|
if (!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 {
|
||||||
//vda: inform that we start in standalone mode?
|
|
||||||
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);
|
||||||
if (!(opt & OPT_FOREGROUND))
|
|
||||||
//vda: NOMMU?
|
|
||||||
bb_daemonize(DAEMON_CHDIR_ROOT);
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
sessions = make_new_session();
|
sessions = make_new_session();
|
||||||
@ -429,6 +450,9 @@ int telnetd_main(int argc, char **argv)
|
|||||||
/* We don't want to die if just one session is broken */
|
/* We don't want to die if just one session is broken */
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
|
if (opt & OPT_WATCHCHILD)
|
||||||
|
signal(SIGCHLD, handle_sigchld);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This is how the buffers are used. The arrows indicate the movement
|
This is how the buffers are used. The arrows indicate the movement
|
||||||
of data.
|
of data.
|
||||||
@ -450,14 +474,6 @@ int telnetd_main(int argc, char **argv)
|
|||||||
again:
|
again:
|
||||||
FD_ZERO(&rdfdset);
|
FD_ZERO(&rdfdset);
|
||||||
FD_ZERO(&wrfdset);
|
FD_ZERO(&wrfdset);
|
||||||
if (!IS_INETD) {
|
|
||||||
FD_SET(master_fd, &rdfdset);
|
|
||||||
/* This is needed because free_session() does not
|
|
||||||
* take into account master_fd when it finds new
|
|
||||||
* maxfd among remaining fd's: */
|
|
||||||
if (master_fd > maxfd)
|
|
||||||
maxfd = master_fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Select on the master socket, all telnet sockets and their
|
/* Select on the master socket, all telnet sockets and their
|
||||||
* ptys if there is room in their session buffers.
|
* ptys if there is room in their session buffers.
|
||||||
@ -465,15 +481,28 @@ int telnetd_main(int argc, char **argv)
|
|||||||
* before each select. Can be a problem with 500+ connections. */
|
* before each select. Can be a problem with 500+ connections. */
|
||||||
ts = sessions;
|
ts = sessions;
|
||||||
while (ts) {
|
while (ts) {
|
||||||
if (ts->size1 > 0) /* can write to pty */
|
struct tsession *next = ts->next; /* in case we free ts. */
|
||||||
FD_SET(ts->ptyfd, &wrfdset);
|
if (ts->shell_pid == -1) {
|
||||||
if (ts->size1 < BUFSIZE) /* can read from socket */
|
free_session(ts);
|
||||||
FD_SET(ts->sockfd_read, &rdfdset);
|
} else {
|
||||||
if (ts->size2 > 0) /* can write to socket */
|
if (ts->size1 > 0) /* can write to pty */
|
||||||
FD_SET(ts->sockfd_write, &wrfdset);
|
FD_SET(ts->ptyfd, &wrfdset);
|
||||||
if (ts->size2 < BUFSIZE) /* can read from pty */
|
if (ts->size1 < BUFSIZE) /* can read from socket */
|
||||||
FD_SET(ts->ptyfd, &rdfdset);
|
FD_SET(ts->sockfd_read, &rdfdset);
|
||||||
ts = ts->next;
|
if (ts->size2 > 0) /* can write to socket */
|
||||||
|
FD_SET(ts->sockfd_write, &wrfdset);
|
||||||
|
if (ts->size2 < BUFSIZE) /* can read from pty */
|
||||||
|
FD_SET(ts->ptyfd, &rdfdset);
|
||||||
|
}
|
||||||
|
ts = next;
|
||||||
|
}
|
||||||
|
if (!IS_INETD) {
|
||||||
|
FD_SET(master_fd, &rdfdset);
|
||||||
|
/* This is needed because free_session() does not
|
||||||
|
* take into account master_fd when it finds new
|
||||||
|
* maxfd among remaining fd's */
|
||||||
|
if (master_fd > maxfd)
|
||||||
|
maxfd = master_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
count = select(maxfd + 1, &rdfdset, &wrfdset, NULL, NULL);
|
count = select(maxfd + 1, &rdfdset, &wrfdset, NULL, NULL);
|
||||||
|
Loading…
Reference in New Issue
Block a user