getty: wait up to 5 seconds for the output buffer to drain
authorPeter Korsgaard <jacmet@sunsite.dk>
Wed, 26 Jan 2011 14:15:19 +0000 (15:15 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Wed, 26 Jan 2011 14:15:19 +0000 (15:15 +0100)
Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
loginutils/getty.c

index 2d2184a76b2cd02328ce54add0e8a74089facabe..035534157eccd7b4a4edb13db7f20914547d83ff 100644 (file)
@@ -224,6 +224,12 @@ static void open_tty(void)
        }
 }
 
+static void set_termios(void)
+{
+       if (tcsetattr_stdin_TCSANOW(&G.termios) < 0)
+               bb_perror_msg_and_die("tcsetattr");
+}
+
 /* We manipulate termios this way:
  * - first, we read existing termios settings
  * - termios_init modifies some parts and sets it
@@ -233,25 +239,31 @@ static void open_tty(void)
  */
 static void termios_init(int speed)
 {
-       /* Flush input and output queues, important for modems!
-        * Users report losing previously queued output chars, and I hesitate
-        * to use tcdrain here instead of tcflush - I imagine it can block.
-        * Using small sleep instead.
+       /* Try to drain output buffer, with 5 sec timeout.
+        * Added on request from users of ~600 baud serial interface
+        * with biggish buffer on a 90MHz CPU.
+        * They were losing hundreds of bytes of buffered output
+        * on tcflush.
         */
-       usleep(100*1000); /* 0.1 sec */
+       signal_no_SA_RESTART_empty_mask(SIGALRM, record_signo);
+       alarm(5);
+       tcdrain(STDIN_FILENO);
+       alarm(0);
+       signal(SIGALRM, SIG_DFL); /* do not break -t TIMEOUT! */
+
+       /* Flush input and output queues, important for modems! */
        tcflush(STDIN_FILENO, TCIOFLUSH);
 
        /* Set speed if it wasn't specified as "0" on command line */
        if (speed != B0)
                cfsetspeed(&G.termios, speed);
 
-       /*
-        * Initial termios settings: 8-bit characters, raw-mode, blocking i/o.
+       /* Initial termios settings: 8-bit characters, raw-mode, blocking i/o.
         * Special characters are set after we have read the login name; all
         * reads will be done in raw mode anyway. Errors will be dealt with
         * later on.
         */
-       /* 8 bits; hangup (drop DTR) on last close; enable receive */
+       /* 8 bits; hang up (drop DTR) on last close; enable receive */
        G.termios.c_cflag = CS8 | HUPCL | CREAD;
        if (option_mask32 & F_LOCAL) {
                /* ignore Carrier Detect pin:
@@ -266,9 +278,8 @@ static void termios_init(int speed)
 #endif
        /* Other bits in c_cflag:
         * CSTOPB 2 stop bits (1 otherwise)
-        * PARENB Enable parity bit
-        * PARODD Use odd parity (else even)
-        * LOBLK  Block job control output (??)
+        * PARENB Enable parity bit (both on input and output)
+        * PARODD Odd parity (else even)
         */
        G.termios.c_iflag = 0;
        G.termios.c_lflag = 0;
@@ -281,7 +292,7 @@ static void termios_init(int speed)
        G.termios.c_line = 0;
 #endif
 
-       tcsetattr_stdin_TCSANOW(&G.termios);
+       set_termios();
 
        debug("term_io 2\n");
 }
@@ -309,8 +320,8 @@ static void termios_final(void)
         * ICRNL   Map CR to NL
         * IUCLC   Map uppercase to lowercase
         * IMAXBEL Echo BEL on input line too long
-        * IUTF8   [Appears to affect tty's idea of char widths,
-        *         observed to improve backspacing through Unicode chars]
+        * IUTF8   Appears to affect tty's idea of char widths,
+        *         observed to improve backspacing through Unicode chars
          */
 
        /* line buffered input (NL or EOL or EOF chars end a line);
@@ -324,16 +335,16 @@ static void termios_final(void)
        /* Other bits in c_lflag:
         * XCASE   Map uppercase to \lowercase [tried, doesn't work]
         * ECHONL  Echo NL even if ECHO is not set
+        * ECHOCTL Echo ctrl chars as ^c (else don't echo) - maybe set this?
+        * ECHOPRT On erase, echo erased chars
+        *         [qwe<BS><BS><BS> input looks like "qwe\ewq/" on screen]
         * NOFLSH  Don't flush input buffer after interrupt or quit chars
         * IEXTEN  Enable extended functions (??)
         *         [glibc says it enables c_cc[LNEXT] "enter literal char"
         *         and c_cc[VDISCARD] "toggle discard buffered output" chars]
-        * ECHOCTL Echo ctrl chars as ^c (else don't echo) - maybe set this?
-        * ECHOPRT On erase, echo erased chars
-        *         [qwe<BS><BS><BS> input looks like "qwe\ewq/" on screen]
         * FLUSHO  Output being flushed (c_cc[VDISCARD] is in effect)
         * PENDIN  Retype pending input at next read or input char
-        *         (c_cc[VREPRINT] is being processes)
+        *         (c_cc[VREPRINT] is being processed)
         * TOSTOP  Send SIGTTOU for background output
         *         (why "stty sane" unsets this bit?)
         */
@@ -358,8 +369,7 @@ static void termios_final(void)
         * VSTART, VSTOP - chars used for IXON/IXOFF
         */
 
-       if (tcsetattr_stdin_TCSANOW(&G.termios) < 0)
-               bb_perror_msg_and_die("tcsetattr");
+       set_termios();
 }
 
 /* extract baud rate from modem status message */
@@ -383,7 +393,7 @@ static void auto_baud(void)
         */
 
        G.termios.c_cc[VMIN] = 0; /* don't block reads (min read is 0 chars) */
-       tcsetattr_stdin_TCSANOW(&G.termios);
+       set_termios();
 
        /*
         * Wait for a while, then read everything the modem has said so far and
@@ -407,7 +417,7 @@ static void auto_baud(void)
 
        /* Restore terminal settings. Errors will be dealt with later on */
        G.termios.c_cc[VMIN] = 1; /* restore to value set by termios_init */
-       tcsetattr_stdin_TCSANOW(&G.termios);
+       set_termios();
 }
 
 /* get user name, establish parity, speed, erase, kill, eol;
@@ -614,7 +624,7 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
                        /* We are here only if G.numspeed > 1 */
                        baud_index = (baud_index + 1) % G.numspeed;
                        cfsetspeed(&G.termios, G.speeds[baud_index]);
-                       tcsetattr_stdin_TCSANOW(&G.termios);
+                       set_termios();
                }
        }