From ed1667e8ee91243b77b02cc4f160fe4b80a30385 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 4 Sep 2009 02:21:13 +0200 Subject: [PATCH] telnetd: add -w ("inetd wait") option. Can be configured off. gcc fils to fully optimize it out when it's off: function old new delta telnetd_main 1527 1548 +21 packed_usage 26596 26587 -9 but nevertheless it's a useful (and so far single) example how to write "inetd wait" tcp service. Signed-off-by: Denys Vlasenko --- include/usage.h | 20 +++++++++------- networking/Config.in | 21 +++++++++++++++++ networking/telnetd.c | 56 ++++++++++++++++++++++++++++---------------- 3 files changed, 69 insertions(+), 28 deletions(-) diff --git a/include/usage.h b/include/usage.h index ba77490cb..9fa1b9ef8 100644 --- a/include/usage.h +++ b/include/usage.h @@ -1468,11 +1468,11 @@ "\n -m Get baud rate from modem's CONNECT status message" \ "\n -w Wait for a CR or LF before sending /etc/issue" \ "\n -n Do not prompt the user for a login name" \ - "\n -f issue_file Display issue_file instead of /etc/issue" \ - "\n -l login_app Invoke login_app instead of /bin/login" \ - "\n -t timeout Terminate after timeout if no username is read" \ - "\n -I initstring Init string to send before anything else" \ - "\n -H login_host Log login_host into the utmp file as the hostname" \ + "\n -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue" \ + "\n -l LOGIN Invoke LOGIN instead of /bin/login" \ + "\n -t SEC Terminate after SEC if no username is read" \ + "\n -I INITSTR Send INITSTR before anything else" \ + "\n -H HOST Log HOST into the utmp file as the hostname" \ #define grep_trivial_usage \ "[-HhrilLnqvso" \ @@ -4415,14 +4415,18 @@ IF_NOT_FEATURE_TELNETD_STANDALONE(" via inetd") "\n" \ "\nOptions:" \ "\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)" \ IF_FEATURE_TELNETD_STANDALONE( \ "\n -p PORT Port to listen on" \ - "\n -b ADDR Address to bind to" \ + "\n -b ADDR[:PORT] Address to bind to" \ "\n -F Run in foreground" \ - "\n -i Run as inetd subservice" \ + "\n -i Run as inetd service" \ + IF_FEATURE_TELNETD_INETD_WAIT( \ + "\n -w SEC Run as inetd service in wait mode, linger time SEC" \ + "\n -S Log to syslog (implied by -i or without -F and -w)" \ + ) \ ) /* "test --help" does not print help (POSIX compat), only "[ --help" does. diff --git a/networking/Config.in b/networking/Config.in index 83522ffb1..59e88e016 100644 --- a/networking/Config.in +++ b/networking/Config.in @@ -788,6 +788,27 @@ config FEATURE_TELNETD_STANDALONE help Selecting this will make telnetd able to run standalone. +config FEATURE_TELNETD_INETD_WAIT + bool "Support -w SEC option (inetd wait mode)" + default n + depends on FEATURE_TELNETD_STANDALONE + help + This option allows you to run telnetd in "inet wait" mode. + Example inetd.conf line (note "wait", not usual "nowait"): + + telnet stream tcp wait root /bin/telnetd telnetd -w10 + + In this example, inetd passes _listening_ socket_ as fd 0 + to telnetd when connection appears. + telnetd will wait for connections until all existing + connections are closed, and no new connections + appear during 10 seconds. Then it exits, and inetd continues + to listen for new connections. + + This option is rarely used. "tcp nowait" is much more usual + way of running tcp services, including telnetd. + You most probably want to say N here. + config TFTP bool "tftp" default n diff --git a/networking/telnetd.c b/networking/telnetd.c index 540387f1a..2a0ace5cb 100644 --- a/networking/telnetd.c +++ b/networking/telnetd.c @@ -211,8 +211,10 @@ static size_t iac_safe_write(int fd, const char *buf, size_t count) enum { OPT_WATCHCHILD = (1 << 2), /* -K */ OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */ - OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p */ + OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p PORT */ OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */ + OPT_SYSLOG = (1 << 7) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -S */ + OPT_WAIT = (1 << 8) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -w SEC */ }; static struct tsession * @@ -438,24 +440,29 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv) struct tsession *ts; #if ENABLE_FEATURE_TELNETD_STANDALONE #define IS_INETD (opt & OPT_INETD) - int master_fd = master_fd; /* be happy, gcc */ - unsigned portnbr = 23; + int master_fd = master_fd; /* for compiler */ + int sec_linger = sec_linger; char *opt_bindaddr = NULL; char *opt_portnbr; #else enum { IS_INETD = 1, master_fd = -1, - portnbr = 23, }; #endif INIT_G(); + /* -w NUM, and implies -F. -w and -i don't mix */ + IF_FEATURE_TELNETD_INETD_WAIT(opt_complementary = "wF:w+:i--w:w--i";) /* Even if !STANDALONE, we accept (and ignore) -i, thus people * don't need to guess whether it's ok to pass -i to us */ - opt = getopt32(argv, "f:l:Ki" IF_FEATURE_TELNETD_STANDALONE("p:b:F"), + opt = getopt32(argv, "f:l:Ki" + IF_FEATURE_TELNETD_STANDALONE("p:b:F") + IF_FEATURE_TELNETD_INETD_WAIT("Sw:"), &G.issuefile, &G.loginpath - IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)); + IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr) + IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger) + ); if (!IS_INETD /*&& !re_execed*/) { /* inform that we start in standalone mode? * May be useful when people forget to give -i */ @@ -467,32 +474,30 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv) } } /* Redirect log to syslog early, if needed */ - if (IS_INETD || !(opt & OPT_FOREGROUND)) { + if (IS_INETD || (opt & OPT_SYSLOG) || !(opt & OPT_FOREGROUND)) { openlog(applet_name, LOG_PID, LOG_DAEMON); logmode = LOGMODE_SYSLOG; } - IF_FEATURE_TELNETD_STANDALONE( - if (opt & OPT_PORT) - portnbr = xatou16(opt_portnbr); - ); - - /* Used to check access(G.loginpath, X_OK) here. Pointless. - * exec will do this for us for free later. */ - #if ENABLE_FEATURE_TELNETD_STANDALONE if (IS_INETD) { G.sessions = make_new_session(0); if (!G.sessions) /* pty opening or vfork problem, exit */ - return 1; /* make_new_session prints error message */ + return 1; /* make_new_session printed error message */ } else { - master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr); - xlisten(master_fd, 1); + master_fd = 0; + if (!(opt & OPT_WAIT)) { + unsigned portnbr = 23; + if (opt & OPT_PORT) + portnbr = xatou16(opt_portnbr); + master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr); + xlisten(master_fd, 1); + } close_on_exec_on(master_fd); } #else G.sessions = make_new_session(); if (!G.sessions) /* pty opening or vfork problem, exit */ - return 1; /* make_new_session prints error message */ + return 1; /* make_new_session printed error message */ #endif /* We don't want to die if just one session is broken */ @@ -556,7 +561,18 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv) G.maxfd = master_fd; } - count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, NULL); + { + struct timeval tv; + struct timeval *tv_ptr = NULL; + if ((opt & OPT_WAIT) && !G.sessions) { + tv.tv_sec = sec_linger; + tv.tv_usec = 0; + tv_ptr = &tv; + } + count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, tv_ptr); + } + if (count == 0) /* "telnetd -w SEC" timed out */ + return 0; if (count < 0) goto again; /* EINTR or ENOMEM */