telnetd: add -w ("inetd wait") option. Can be configured off.
authorDenys Vlasenko <vda.linux@googlemail.com>
Fri, 4 Sep 2009 00:21:13 +0000 (02:21 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Fri, 4 Sep 2009 00:21:13 +0000 (02:21 +0200)
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 <vda.linux@googlemail.com>
include/usage.h
networking/Config.in
networking/telnetd.c

index ba77490cb5018e2cfa7ff81e61d65856566b0535..9fa1b9ef8aaa71e30b5e5ac07388840bbe0f6596 100644 (file)
      "\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" \
        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.
index 83522ffb10555920ce05eac0ea289c90fd778e60..59e88e01688d35240777c51de34d14584ea06dc5 100644 (file)
@@ -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
index 540387f1a985a4ad3b4fd5656174ac54b2c986c4..2a0ace5cb79ce4dc50f7733af86e5d8fcc9c16b1 100644 (file)
@@ -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 */