From faaf8cb3faf413485f2ea1bb3656ac40cb48b785 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 24 Jan 2011 14:35:09 +0100 Subject: getty: more simplifications; explain how we treat parity now function old new delta getty_main 1471 1434 -37 Signed-off-by: Denys Vlasenko --- loginutils/getty.c | 188 ++++++++++++++++++++++++++--------------------------- 1 file changed, 93 insertions(+), 95 deletions(-) (limited to 'loginutils') diff --git a/loginutils/getty.c b/loginutils/getty.c index bf66f22..402e1c0 100644 --- a/loginutils/getty.c +++ b/loginutils/getty.c @@ -1,15 +1,23 @@ /* vi: set sw=4 ts=4: */ -/* Based on agetty - another getty program for Linux. By W. Z. Venema 1989 +/* + * Based on agetty - another getty program for Linux. By W. Z. Venema 1989 * Ported to Linux by Peter Orbaek * This program is freely distributable. * * option added by Eric Rasmussen - 12/28/95 * * 1999-02-22 Arkadiusz Mickiewicz - * - added Native Language Support + * - Added Native Language Support * * 1999-05-05 Thorsten Kranzkowski - * - enable hardware flow control before displaying /etc/issue + * - Enabled hardware flow control before displaying /etc/issue + * + * 2011-01 Venys Vlasenko + * - Removed parity detection code. It can't work reliably: + * if all chars received have bit 7 cleared and odd (or even) parity, + * it is impossible to determine whether other side is 8-bit,no-parity + * or 7-bit,odd(even)-parity. It also interferes with non-ASCII usernames. + * - From now on, we assume that parity is correctly set. * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ @@ -23,13 +31,7 @@ # define IUCLC 0 #endif -/* - * Some heuristics to find out what environment we are in: if it is not - * System V, assume it is SunOS 4. - */ -#ifdef LOGIN_PROCESS /* defined in System V utmp.h */ -# include -#else /* if !sysV style, wtmp/utmp code is off */ +#ifndef LOGIN_PROCESS # undef ENABLE_FEATURE_UTMP # undef ENABLE_FEATURE_WTMP # define ENABLE_FEATURE_UTMP 0 @@ -37,7 +39,7 @@ #endif -/* The following is used for understandable diagnostics. */ +/* The following is used for understandable diagnostics */ #ifdef DEBUGGING static FILE *dbf; # define DEBUGTERM "/dev/ttyp0" @@ -64,14 +66,14 @@ static FILE *dbf; */ #define ISSUE "/etc/issue" -/* Some shorthands for control characters. */ +/* Some shorthands for control characters */ #define CTL(x) ((x) ^ 0100) /* Assumes ASCII dialect */ #define CR CTL('M') /* carriage return */ #define NL CTL('J') /* line feed */ #define BS CTL('H') /* back space */ #define DEL CTL('?') /* delete */ -/* Defaults for line-editing etc. characters; you may want to change this. */ +/* Defaults for line-editing etc. characters; you may want to change this */ #define DEF_ERASE DEL /* default erase character */ #define DEF_INTR CTL('C') /* default interrupt character */ #define DEF_QUIT CTL('\\') /* default quit char */ @@ -81,23 +83,22 @@ static FILE *dbf; #define DEF_SWITCH 0 /* default switch char */ /* - * When multiple baud rates are specified on the command line, the first one - * we will try is the first one specified. + * When multiple baud rates are specified on the command line, + * the first one we will try is the first one specified. */ #define MAX_SPEED 10 /* max. nr. of baud rates */ struct globals { unsigned timeout; /* time-out period */ const char *login; /* login program */ + const char *fakehost; const char *tty; /* name of tty */ const char *initstring; /* modem init string */ const char *issue; /* alternative issue file */ int numspeed; /* number of baud rates to try */ int speeds[MAX_SPEED]; /* baud rates to be tried */ + unsigned char eol; /* end-of-line char seen (CR or NL) */ struct termios termios; /* terminal mode bits */ - /* Storage for things detected while the login name was read. */ - unsigned char erase; /* erase character */ - unsigned char eol; /* end-of-line character */ char line_buf[128]; }; @@ -168,14 +169,14 @@ static void parse_speeds(char *arg) } /* parse command-line arguments */ -static void parse_args(char **argv, char **fakehost_p) +static void parse_args(char **argv) { char *ts; int flags; opt_complementary = "-2:t+"; /* at least 2 args; -t N */ flags = getopt32(argv, opt_string, - &G.initstring, fakehost_p, &G.issue, + &G.initstring, &G.fakehost, &G.issue, &G.login, &G.timeout ); if (flags & F_INITSTRING) { @@ -206,17 +207,17 @@ static void parse_args(char **argv, char **fakehost_p) /* set up tty as standard input, output, error */ static void open_tty(void) { - /* Set up new standard input, unless we are given an already opened port. */ + /* Set up new standard input, unless we are given an already opened port */ if (NOT_LONE_DASH(G.tty)) { if (G.tty[0] != '/') G.tty = xasprintf("/dev/%s", G.tty); /* will leak it */ - /* Open the tty as standard input. */ + /* Open the tty as standard input */ debug("open(2)\n"); close(0); xopen(G.tty, O_RDWR | O_NONBLOCK); /* uses fd 0 */ - /* Set proper protections and ownership. */ + /* Set proper protections and ownership */ fchown(0, 0, 0); /* 0:0 */ fchmod(0, 0620); /* crw--w---- */ } else { @@ -229,7 +230,13 @@ static void open_tty(void) } } -/* initialize termios settings */ +/* We manipulate termios this way: + * - first, we read existing termios settings + * - termios_init modifies some parts and sets it + * - auto_baud and/or BREAK processing can set different speed and set termios + * - termios_final again modifies some parts and sets termios before + * execing login + */ static void termios_init(int speed) { /* Flush input and output queues, important for modems! @@ -240,7 +247,7 @@ static void termios_init(int speed) usleep(100*1000); /* 0.1 sec */ tcflush(STDIN_FILENO, TCIOFLUSH); - /* Set speed if it wasn't specified as "0" on command line. */ + /* Set speed if it wasn't specified as "0" on command line */ if (speed != B0) cfsetspeed(&G.termios, speed); @@ -271,13 +278,44 @@ static void termios_init(int speed) debug("term_io 2\n"); } +static void termios_final(void) +{ + /* General terminal-independent stuff */ + G.termios.c_iflag |= IXON | IXOFF; /* 2-way flow control */ + G.termios.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; + /* no longer in lflag: | ECHOCTL | ECHOPRT */ + G.termios.c_oflag |= OPOST; + /* G.termios.c_cflag = 0; */ + G.termios.c_cc[VINTR] = DEF_INTR; + G.termios.c_cc[VQUIT] = DEF_QUIT; + G.termios.c_cc[VEOF] = DEF_EOF; + G.termios.c_cc[VEOL] = DEF_EOL; +#ifdef VSWTC + G.termios.c_cc[VSWTC] = DEF_SWITCH; +#endif + + /* Account for special characters seen in input */ + if (G.eol == CR) { + G.termios.c_iflag |= ICRNL; /* map CR in input to NL */ + /* already done by termios_init */ + /* G.termios.c_oflag |= ONLCR; map NL in output to CR-NL */ + } + G.termios.c_cc[VKILL] = DEF_KILL; + +#ifdef CRTSCTS + /* Optionally enable hardware flow control */ + if (option_mask32 & F_RTSCTS) + G.termios.c_cflag |= CRTSCTS; +#endif + + /* Finally, make the new settings effective */ + if (tcsetattr_stdin_TCSANOW(&G.termios) < 0) + bb_perror_msg_and_die("tcsetattr"); +} + /* extract baud rate from modem status message */ static void auto_baud(void) { - int speed; - int vmin; - unsigned iflag; - char *bp; int nread; /* @@ -296,12 +334,9 @@ static void auto_baud(void) */ /* - * Use 7-bit characters, don't block if input queue is empty. Errors will - * be dealt with later on. + * Don't block if input queue is empty. + * Errors will be dealt with later on. */ - iflag = G.termios.c_iflag; - G.termios.c_iflag |= ISTRIP; /* enable 8th-bit stripping */ - vmin = G.termios.c_cc[VMIN]; G.termios.c_cc[VMIN] = 0; /* don't block if queue empty */ tcsetattr_stdin_TCSANOW(&G.termios); @@ -312,6 +347,8 @@ static void auto_baud(void) sleep(1); nread = safe_read(STDIN_FILENO, G.line_buf, sizeof(G.line_buf) - 1); if (nread > 0) { + int speed; + char *bp; G.line_buf[nread] = '\0'; for (bp = G.line_buf; bp < G.line_buf + nread; bp++) { if (isdigit(*bp)) { @@ -323,38 +360,38 @@ static void auto_baud(void) } } - /* Restore terminal settings. Errors will be dealt with later on. */ - G.termios.c_iflag = iflag; - G.termios.c_cc[VMIN] = vmin; + /* 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); } /* get user name, establish parity, speed, erase, kill, eol; - * return NULL on BREAK, logname on success */ + * return NULL on BREAK, logname on success + */ static char *get_logname(void) { char *bp; char c; - /* Flush pending input (esp. after parsing or switching the baud rate). */ + /* Flush pending input (esp. after parsing or switching the baud rate) */ usleep(100*1000); /* 0.1 sec */ tcflush(STDIN_FILENO, TCIOFLUSH); - /* Prompt for and read a login name. */ + /* Prompt for and read a login name */ G.line_buf[0] = '\0'; while (!G.line_buf[0]) { - /* Write issue file and prompt. */ + /* Write issue file and prompt */ #ifdef ISSUE if (!(option_mask32 & F_NOISSUE)) print_login_issue(G.issue, G.tty); #endif print_login_prompt(); - /* Read name, watch for break, parity, erase, kill, end-of-line. */ + /* Read name, watch for break, parity, erase, kill, end-of-line */ bp = G.line_buf; G.eol = '\0'; while (1) { - /* Do not report trivial EINTR/EIO errors. */ + /* Do not report trivial EINTR/EIO errors */ errno = EINTR; /* make read of 0 bytes be silent too */ if (read(STDIN_FILENO, &c, 1) < 1) { if (errno == EINTR || errno == EIO) @@ -367,7 +404,7 @@ static char *get_logname(void) if (c == '\0' && G.numspeed > 1) return NULL; - /* Do erase, kill and end-of-line processing. */ + /* Do erase, kill and end-of-line processing */ switch (c) { case CR: case NL: @@ -376,7 +413,7 @@ static char *get_logname(void) goto got_logname; case BS: case DEL: - G.erase = c; + G.termios.c_cc[VERASE] = c; if (bp > G.line_buf) { full_write(STDOUT_FILENO, "\010 \010", 3); bp--; @@ -407,61 +444,22 @@ static char *get_logname(void) return G.line_buf; } -/* set the final tty mode bits */ -static void termios_final(void) -{ - /* General terminal-independent stuff. */ - G.termios.c_iflag |= IXON | IXOFF; /* 2-way flow control */ - G.termios.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; - /* no longer in lflag: | ECHOCTL | ECHOPRT */ - G.termios.c_oflag |= OPOST; - /* G.termios.c_cflag = 0; */ - G.termios.c_cc[VINTR] = DEF_INTR; /* default interrupt */ - G.termios.c_cc[VQUIT] = DEF_QUIT; /* default quit */ - G.termios.c_cc[VEOF] = DEF_EOF; /* default EOF character */ - G.termios.c_cc[VEOL] = DEF_EOL; -#ifdef VSWTC - G.termios.c_cc[VSWTC] = DEF_SWITCH; /* default switch character */ -#endif - - /* Account for special characters seen in input. */ - if (G.eol == CR) { - G.termios.c_iflag |= ICRNL; /* map CR in input to NL */ - G.termios.c_oflag |= ONLCR; /* map NL in output to CR-NL */ - } - G.termios.c_cc[VERASE] = G.erase; /* set erase character */ - G.termios.c_cc[VKILL] = DEF_KILL; /* set kill character */ - -#ifdef CRTSCTS - /* Optionally enable hardware flow control */ - if (option_mask32 & F_RTSCTS) - G.termios.c_cflag |= CRTSCTS; -#endif - - /* Finally, make the new settings effective */ - if (tcsetattr_stdin_TCSANOW(&G.termios) < 0) - bb_perror_msg_and_die("tcsetattr"); -} - int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int getty_main(int argc UNUSED_PARAM, char **argv) { int n; pid_t pid; - char *fakehost = NULL; /* Fake hostname for ut_host */ char *logname; INIT_G(); G.login = _PATH_LOGIN; /* default login program */ - G.initstring = ""; /* modem init string */ #ifdef ISSUE G.issue = ISSUE; /* default issue file */ #endif - G.erase = DEF_ERASE; G.eol = CR; - /* Parse command-line arguments. */ - parse_args(argv, &fakehost); + /* Parse command-line arguments */ + parse_args(argv); logmode = LOGMODE_NONE; @@ -524,9 +522,9 @@ int getty_main(int argc UNUSED_PARAM, char **argv) #endif /* Update the utmp file. This tty is ours now! */ - update_utmp(pid, LOGIN_PROCESS, G.tty, "LOGIN", fakehost); + update_utmp(pid, LOGIN_PROCESS, G.tty, "LOGIN", G.fakehost); - /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */ + /* Initialize the termios settings (raw mode, eight-bit, blocking i/o) */ debug("calling termios_init\n"); termios_init(G.speeds[0]); @@ -563,30 +561,30 @@ int getty_main(int argc UNUSED_PARAM, char **argv) int baud_index = 0; while (1) { - /* Read the login name. */ + /* Read the login name */ debug("reading login name\n"); logname = get_logname(); if (logname) break; - /* We are here only if G.numspeed > 1. */ + /* 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); } } - /* Disable timer. */ + /* Disable timer */ alarm(0); - /* Finalize the termios settings. */ + /* Finalize the termios settings */ termios_final(); - /* Now the newline character should be properly written. */ + /* Now the newline character should be properly written */ full_write(STDOUT_FILENO, "\n", 1); - /* Let the login program take care of password validation. */ + /* Let the login program take care of password validation */ /* We use PATH because we trust that root doesn't set "bad" PATH, - * and getty is not suid-root applet. */ + * and getty is not suid-root applet */ /* With -n, logname == NULL, and login will ask for username instead */ BB_EXECLP(G.login, G.login, "--", logname, NULL); bb_error_msg_and_die("can't execute '%s'", G.login); -- cgit v1.1