X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;ds=inline;f=loginutils%2Fgetty.c;h=dfa15b3da2beee50113f20cd748d28cf470cee57;hb=0bfb9c2cf0e8b711634014e06d39b5f47c5a95c2;hp=ab55ea4b003847df29f7474ccebbdff322d1211a;hpb=53600591311a129717abd2e3bcaa302622a6ce67;p=oweals%2Fbusybox.git diff --git a/loginutils/getty.c b/loginutils/getty.c index ab55ea4b0..dfa15b3da 100644 --- a/loginutils/getty.c +++ b/loginutils/getty.c @@ -1,43 +1,50 @@ /* vi: set sw=4 ts=4: */ -/* agetty.c - 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. The entire man-page used to - * be here. Now read the real man-page agetty.8 instead. + * 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. */ #include "libbb.h" #include +#ifndef IUCLC +# define IUCLC 0 +#endif -#if ENABLE_FEATURE_UTMP -# include /* LOGIN_PROCESS */ +#ifndef LOGIN_PROCESS +# undef ENABLE_FEATURE_UTMP +# undef ENABLE_FEATURE_WTMP +# define ENABLE_FEATURE_UTMP 0 +# define ENABLE_FEATURE_WTMP 0 #endif -#ifndef IUCLC -# define IUCLC 0 + +/* The following is used for understandable diagnostics */ +#ifdef DEBUGGING +static FILE *dbf; +# define DEBUGTERM "/dev/ttyp0" +# define debug(...) do { fprintf(dbf, __VA_ARGS__); fflush(dbf); } while (0) +#else +# define debug(...) ((void)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 */ -#undef ENABLE_FEATURE_UTMP -#undef ENABLE_FEATURE_WTMP -#define ENABLE_FEATURE_UTMP 0 -#define ENABLE_FEATURE_WTMP 0 -#endif /* LOGIN_PROCESS */ /* * Things you may want to modify. @@ -46,110 +53,88 @@ * below. Note, however, that DEL cannot be used for interrupt generation * and for line editing at the same time. */ - -/* I doubt there are systems which still need this */ -#undef HANDLE_ALLCAPS -#undef ANCIENT_BS_KILL_CHARS - +#undef _PATH_LOGIN #define _PATH_LOGIN "/bin/login" -/* If ISSUE is not defined, getty will never display the contents of the +/* Displayed before the login prompt. + * If ISSUE is not defined, getty will never display the contents of the * /etc/issue file. You will not want to spit out large "issue" files at the * wrong baud rate. */ -#define ISSUE "/etc/issue" /* displayed before the login prompt */ +#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. */ -#define DEF_ERASE DEL /* default erase character */ +/* Defaults for line-editing etc. characters; you may want to change this */ #define DEF_INTR CTL('C') /* default interrupt character */ #define DEF_QUIT CTL('\\') /* default quit char */ #define DEF_KILL CTL('U') /* default kill char */ #define DEF_EOF CTL('D') /* default EOF char */ #define DEF_EOL '\n' -#define DEF_SWITCH 0 /* default switch char */ +#define DEF_SWITCH 0 /* default switch char (none) */ /* - * 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 */ -/* Storage for command-line options. */ -struct options { - int flags; /* toggle switches, see below */ +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 */ + 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 */ + char line_buf[128]; }; -/* Storage for things detected while the login name was read. */ -struct chardata { - unsigned char erase; /* erase character */ - unsigned char kill; /* kill character */ - unsigned char eol; /* end-of-line character */ - unsigned char parity; /* what parity did we see */ - /* (parity & 1): saw odd parity char with 7th bit set */ - /* (parity & 2): saw even parity char with 7th bit set */ - /* parity == 0: probably 7-bit, space parity? */ - /* parity == 1: probably 7-bit, odd parity? */ - /* parity == 2: probably 7-bit, even parity? */ - /* parity == 3: definitely 8 bit, no parity! */ - /* Hmm... with any value of "parity" 8 bit, no parity is possible */ -#ifdef HANDLE_ALLCAPS - unsigned char capslock; /* upper case without lower case */ -#endif -}; - - -/* Initial values for the above. */ -static const struct chardata init_chardata = { - DEF_ERASE, /* default erase character */ - DEF_KILL, /* default kill character */ - 13, /* default eol char */ - 0, /* space parity */ -#ifdef HANDLE_ALLCAPS - 0, /* no capslock */ -#endif -}; +#define G (*ptr_to_globals) +#define INIT_G() do { \ + SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ +} while (0) + +//usage:#define getty_trivial_usage +//usage: "[OPTIONS] BAUD_RATE[,BAUD_RATE]... TTY [TERMTYPE]" +//usage:#define getty_full_usage "\n\n" +//usage: "Open a tty, prompt for a login name, then invoke /bin/login\n" +//usage: "\nOptions:" +//usage: "\n -h Enable hardware RTS/CTS flow control" +//usage: "\n -L Set CLOCAL (ignore Carrier Detect state)" +//usage: "\n -m Get baud rate from modem's CONNECT status message" +//usage: "\n -n Don't prompt for login name" +//usage: "\n -w Wait for CR or LF before sending /etc/issue" +//usage: "\n -i Don't display /etc/issue" +//usage: "\n -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue" +//usage: "\n -l LOGIN Invoke LOGIN instead of /bin/login" +//usage: "\n -t SEC Terminate after SEC if no login name is read" +//usage: "\n -I INITSTR Send INITSTR before anything else" +//usage: "\n -H HOST Log HOST into the utmp file as the hostname" +//usage: "\n" +//usage: "\nBAUD_RATE of 0 leaves it unchanged" static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn"; -#define F_INITSTRING (1 << 0) /* -I initstring is set */ -#define F_LOCAL (1 << 1) /* -L force local */ -#define F_FAKEHOST (1 << 2) /* -H fake hostname */ -#define F_CUSTISSUE (1 << 3) /* -f give alternative issue file */ -#define F_RTSCTS (1 << 4) /* -h enable RTS/CTS flow control */ -#define F_ISSUE (1 << 5) /* -i display /etc/issue */ -#define F_LOGIN (1 << 6) /* -l non-default login program */ -#define F_PARSE (1 << 7) /* -m process modem status messages */ -#define F_TIMEOUT (1 << 8) /* -t time out */ -#define F_WAITCRLF (1 << 9) /* -w wait for CR or LF */ -#define F_NOPROMPT (1 << 10) /* -n don't ask for login name */ - - -#define line_buf bb_common_bufsiz1 - -/* The following is used for understandable diagnostics. */ -#ifdef DEBUGGING -static FILE *dbf; -#define DEBUGTERM "/dev/ttyp0" -#define debug(...) do { fprintf(dbf, __VA_ARGS__); fflush(dbf); } while (0) -#else -#define debug(...) ((void)0) -#endif - - -/* bcode - convert speed string to speed code; return <= 0 on failure */ +#define F_INITSTRING (1 << 0) /* -I */ +#define F_LOCAL (1 << 1) /* -L */ +#define F_FAKEHOST (1 << 2) /* -H */ +#define F_CUSTISSUE (1 << 3) /* -f */ +#define F_RTSCTS (1 << 4) /* -h */ +#define F_NOISSUE (1 << 5) /* -i */ +#define F_LOGIN (1 << 6) /* -l */ +#define F_PARSE (1 << 7) /* -m */ +#define F_TIMEOUT (1 << 8) /* -t */ +#define F_WAITCRLF (1 << 9) /* -w */ +#define F_NOPROMPT (1 << 10) /* -n */ + + +/* convert speed string to speed code; return <= 0 on failure */ static int bcode(const char *s) { int value = bb_strtou(s, NULL, 10); /* yes, int is intended! */ @@ -158,53 +143,54 @@ static int bcode(const char *s) return tty_value_to_baud(value); } -/* parse_speeds - parse alternate baud rates */ -static void parse_speeds(struct options *op, char *arg) +/* parse alternate baud rates */ +static void parse_speeds(char *arg) { char *cp; /* NB: at least one iteration is always done */ debug("entered parse_speeds\n"); while ((cp = strsep(&arg, ",")) != NULL) { - op->speeds[op->numspeed] = bcode(cp); - if (op->speeds[op->numspeed] < 0) + G.speeds[G.numspeed] = bcode(cp); + if (G.speeds[G.numspeed] < 0) bb_error_msg_and_die("bad speed: %s", cp); /* note: arg "0" turns into speed B0 */ - op->numspeed++; - if (op->numspeed > MAX_SPEED) + G.numspeed++; + if (G.numspeed > MAX_SPEED) bb_error_msg_and_die("too many alternate speeds"); } debug("exiting parse_speeds\n"); } -/* parse_args - parse command-line arguments */ -static void parse_args(char **argv, struct options *op, char **fakehost_p) +/* parse command-line arguments */ +static void parse_args(char **argv) { char *ts; + int flags; opt_complementary = "-2:t+"; /* at least 2 args; -t N */ - op->flags = getopt32(argv, opt_string, - &(op->initstring), fakehost_p, &(op->issue), - &(op->login), &op->timeout); - argv += optind; - if (op->flags & F_INITSTRING) { - op->initstring = xstrdup(op->initstring); + flags = getopt32(argv, opt_string, + &G.initstring, &G.fakehost, &G.issue, + &G.login, &G.timeout + ); + if (flags & F_INITSTRING) { + G.initstring = xstrdup(G.initstring); /* decode \ddd octal codes into chars */ - strcpy_and_process_escape_sequences((char*)op->initstring, op->initstring); + strcpy_and_process_escape_sequences(G.initstring, G.initstring); } - op->flags ^= F_ISSUE; /* invert flag "show /etc/issue" */ + argv += optind; debug("after getopt\n"); - /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ - op->tty = argv[0]; /* tty name */ + /* We loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ + G.tty = argv[0]; /* tty name */ ts = argv[1]; /* baud rate(s) */ if (isdigit(argv[0][0])) { - /* a number first, assume it's a speed (BSD style) */ - op->tty = ts; /* tty name is in argv[1] */ + /* A number first, assume it's a speed (BSD style) */ + G.tty = ts; /* tty name is in argv[1] */ ts = argv[0]; /* baud rate(s) */ } - parse_speeds(op, ts); - applet_name = xasprintf("getty: %s", op->tty); + parse_speeds(ts); + applet_name = xasprintf("getty: %s", G.tty); if (argv[2]) xsetenv("TERM", argv[2]); @@ -212,40 +198,20 @@ static void parse_args(char **argv, struct options *op, char **fakehost_p) debug("exiting parse_args\n"); } -/* open_tty - set up tty as standard { input, output, error } */ -static void open_tty(const char *tty) +/* 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. */ - if (NOT_LONE_DASH(tty)) { -// struct stat st; -// int cur_dir_fd; -// int fd; - - /* Sanity checks... */ -// cur_dir_fd = xopen(".", O_DIRECTORY | O_NONBLOCK); -// xchdir("/dev"); -// xstat(tty, &st); -// if (!S_ISCHR(st.st_mode)) -// bb_error_msg_and_die("not a character device"); - - if (tty[0] != '/') - tty = xasprintf("/dev/%s", tty); /* will leak it */ - - /* Open the tty as standard input. */ + /* 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 */ debug("open(2)\n"); close(0); - /*fd =*/ xopen(tty, O_RDWR | O_NONBLOCK); /* uses fd 0 */ - -// /* Restore current directory */ -// fchdir(cur_dir_fd); + xopen(G.tty, O_RDWR | O_NONBLOCK); /* uses fd 0 */ - /* Open the tty as standard input, continued */ -// xmove_fd(fd, 0); -// /* fd is >= cur_dir_fd, and cur_dir_fd gets closed too here: */ -// while (fd > 2) -// close(fd--); - - /* Set proper protections and ownership. */ + /* Set proper protections and ownership */ fchown(0, 0, 0); /* 0:0 */ fchmod(0, 0620); /* crw--w---- */ } else { @@ -253,62 +219,174 @@ static void open_tty(const char *tty) * Standard input should already be connected to an open port. Make * sure it is open for read/write. */ - if ((fcntl(0, F_GETFL) & O_RDWR) != O_RDWR) + if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR) bb_error_msg_and_die("stdin is not open for read/write"); } } -/* termios_init - initialize termios settings */ -static void termios_init(struct termios *tp, int speed, struct options *op) +static void set_termios(void) { - speed_t ispeed, ospeed; - /* - * Initial termios settings: 8-bit characters, raw-mode, blocking i/o. + 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 + * - 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) +{ + /* 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. + */ + 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. * 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. + * reads will be done in raw mode anyway. */ - /* flush input and output queues, important for modems! */ - tcflush(0, TCIOFLUSH); - ispeed = ospeed = speed; - if (speed == B0) { - /* Speed was specified as "0" on command line. - * Just leave it unchanged */ - ispeed = cfgetispeed(tp); - ospeed = cfgetospeed(tp); + /* Clear all bits except: */ + G.termios.c_cflag &= (0 + /* 2 stop bits (1 otherwise) + * Enable parity bit (both on input and output) + * Odd parity (else even) + */ + | CSTOPB | PARENB | PARODD +#ifdef CMSPAR + | CMSPAR /* mark or space parity */ +#endif + | CBAUD /* (output) baud rate */ +#ifdef CBAUDEX + | CBAUDEX /* (output) baud rate */ +#endif +#ifdef CIBAUD + | CIBAUD /* input baud rate */ +#endif + ); + /* Set: 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: + * opens don't block when CD is low, + * losing CD doesn't hang up processes whose ctty is this tty + */ + G.termios.c_cflag |= CLOCAL; } - tp->c_cflag = CS8 | HUPCL | CREAD; - if (op->flags & F_LOCAL) - tp->c_cflag |= CLOCAL; - cfsetispeed(tp, ispeed); - cfsetospeed(tp, ospeed); - - tp->c_iflag = tp->c_lflag = 0; - tp->c_oflag = OPOST | ONLCR; - tp->c_cc[VMIN] = 1; - tp->c_cc[VTIME] = 0; -#ifdef __linux__ - tp->c_line = 0; +#ifdef CRTSCTS + if (option_mask32 & F_RTSCTS) + G.termios.c_cflag |= CRTSCTS; /* flow control using RTS/CTS pins */ #endif + G.termios.c_iflag = 0; + G.termios.c_lflag = 0; + /* non-raw output; add CR to each NL */ + G.termios.c_oflag = OPOST | ONLCR; - /* Optionally enable hardware flow control */ -#ifdef CRTSCTS - if (op->flags & F_RTSCTS) - tp->c_cflag |= CRTSCTS; + G.termios.c_cc[VMIN] = 1; /* block reads if < 1 char is available */ + G.termios.c_cc[VTIME] = 0; /* no timeout (reads block forever) */ +#ifdef __linux__ + G.termios.c_line = 0; #endif - tcsetattr_stdin_TCSANOW(tp); + set_termios(); debug("term_io 2\n"); } -/* auto_baud - extract baud rate from modem status message */ -static void auto_baud(char *buf, unsigned size_buf, struct termios *tp) +static void termios_final(void) +{ + /* software flow control on output (stop sending if XOFF is recvd); + * and on input (send XOFF when buffer is full) + */ + G.termios.c_iflag |= IXON | IXOFF; + if (G.eol == '\r') { + G.termios.c_iflag |= ICRNL; /* map CR on input to NL */ + } + /* Other bits in c_iflag: + * IXANY Any recvd char enables output (any char is also a XON) + * INPCK Enable parity check + * IGNPAR Ignore parity errors (drop bad bytes) + * PARMRK Mark parity errors with 0xff, 0x00 prefix + * (else bad byte is received as 0x00) + * ISTRIP Strip parity bit + * IGNBRK Ignore break condition + * BRKINT Send SIGINT on break - maybe set this? + * INLCR Map NL to CR + * IGNCR Ignore CR + * 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 + */ + + /* line buffered input (NL or EOL or EOF chars end a line); + * recognize INT/QUIT/SUSP chars; + * echo input chars; + * echo BS-SP-BS on erase character; + * echo kill char specially, not as ^c (ECHOKE controls how exactly); + * erase all input via BS-SP-BS on kill char (else go to next line) + */ + G.termios.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; + /* 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 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] + * 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 processed) + * TOSTOP Send SIGTTOU for background output + * (why "stty sane" unsets this bit?) + */ + + 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 +#ifdef VSWTCH + G.termios.c_cc[VSWTCH] = DEF_SWITCH; +#endif + G.termios.c_cc[VKILL] = DEF_KILL; + /* Other control chars: + * VEOL2 + * VERASE, VWERASE - (word) erase. we may set VERASE in get_logname + * VREPRINT - reprint current input buffer + * VLNEXT, VDISCARD, VSTATUS + * VSUSP, VDSUSP - send (delayed) SIGTSTP + * VSTART, VSTOP - chars used for IXON/IXOFF + */ + + set_termios(); +} + +/* extract baud rate from modem status message */ +static void auto_baud(void) { - int speed; - int vmin; - unsigned iflag; - char *bp; int nread; /* @@ -326,96 +404,61 @@ static void auto_baud(char *buf, unsigned size_buf, struct termios *tp) * modem status messages is enabled. */ - /* - * Use 7-bit characters, don't block if input queue is empty. Errors will - * be dealt with later on. - */ - iflag = tp->c_iflag; - tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */ - vmin = tp->c_cc[VMIN]; - tp->c_cc[VMIN] = 0; /* don't block if queue empty */ - tcsetattr_stdin_TCSANOW(tp); + G.termios.c_cc[VMIN] = 0; /* don't block reads (min read is 0 chars) */ + set_termios(); /* * Wait for a while, then read everything the modem has said so far and * try to extract the speed of the dial-in call. */ sleep(1); - nread = safe_read(STDIN_FILENO, buf, size_buf - 1); + nread = safe_read(STDIN_FILENO, G.line_buf, sizeof(G.line_buf) - 1); if (nread > 0) { - buf[nread] = '\0'; - for (bp = buf; bp < buf + nread; bp++) { + int speed; + char *bp; + G.line_buf[nread] = '\0'; + for (bp = G.line_buf; bp < G.line_buf + nread; bp++) { if (isdigit(*bp)) { speed = bcode(bp); if (speed > 0) - cfsetspeed(tp, speed); + cfsetspeed(&G.termios, speed); break; } } } - /* Restore terminal settings. Errors will be dealt with later on. */ - tp->c_iflag = iflag; - tp->c_cc[VMIN] = vmin; - tcsetattr_stdin_TCSANOW(tp); -} - -/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */ -static void do_prompt(struct options *op) -{ -#ifdef ISSUE - print_login_issue(op->issue, op->tty); -#endif - print_login_prompt(); -} - -#ifdef HANDLE_ALLCAPS -/* all_is_upcase - string contains upper case without lower case */ -/* returns 1 if true, 0 if false */ -static int all_is_upcase(const char *s) -{ - while (*s) - if (islower(*s++)) - return 0; - return 1; + /* Restore terminal settings */ + G.termios.c_cc[VMIN] = 1; /* restore to value set by termios_init */ + set_termios(); } -#endif -/* get_logname - get user name, establish parity, speed, erase, kill, eol; - * return NULL on BREAK, logname on success */ -static char *get_logname(char *logname, unsigned size_logname, - struct options *op, struct chardata *cp) +/* get user name, establish parity, speed, erase, kill, eol; + * return NULL on BREAK, logname on success + */ +static char *get_logname(void) { char *bp; - char c; /* input character, full eight bits */ - char ascval; /* low 7 bits of input character */ - int bits; /* # of "1" bits per character */ - int mask; /* mask with 1 bit up */ - static const char erase[][3] = {/* backspace-space-backspace */ - "\010\040\010", /* space parity */ - "\010\040\010", /* odd parity */ - "\210\240\210", /* even parity */ - "\010\040\010", /* 8 bit no parity */ - }; - - /* NB: *cp is pre-initialized with init_chardata */ - - /* Flush pending input (esp. after parsing or switching the baud rate). */ - sleep(1); - tcflush(0, TCIOFLUSH); + char c; - /* Prompt for and read a login name. */ - logname[0] = '\0'; - while (!logname[0]) { - /* Write issue file and prompt, with "parity" bit == 0. */ - do_prompt(op); + /* Flush pending input (esp. after parsing or switching the baud rate) */ + usleep(100*1000); /* 0.1 sec */ + tcflush(STDIN_FILENO, TCIFLUSH); - /* Read name, watch for break, parity, erase, kill, end-of-line. */ - bp = logname; - cp->eol = '\0'; - while (cp->eol == '\0') { + /* Prompt for and read a login name */ + G.line_buf[0] = '\0'; + while (!G.line_buf[0]) { + /* Write issue file and prompt */ +#ifdef ISSUE + if (!(option_mask32 & F_NOISSUE)) + print_login_issue(G.issue, G.tty); +#endif + print_login_prompt(); - /* Do not report trivial EINTR/EIO errors. */ + /* 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 */ errno = EINTR; /* make read of 0 bytes be silent too */ if (read(STDIN_FILENO, &c, 1) < 1) { if (errno == EINTR || errno == EIO) @@ -425,142 +468,47 @@ static char *get_logname(char *logname, unsigned size_logname, /* BREAK. If we have speeds to try, * return NULL (will switch speeds and return here) */ - if (c == '\0' && op->numspeed > 1) + if (c == '\0' && G.numspeed > 1) return NULL; - /* Do parity bit handling. */ - if (!(op->flags & F_LOCAL) && (c & 0x80)) { /* "parity" bit on? */ - bits = 1; - mask = 1; - while (mask & 0x7f) { - if (mask & c) - bits++; /* count "1" bits */ - mask <<= 1; - } - /* ... |= 2 - even, 1 - odd */ - cp->parity |= 2 - (bits & 1); - } - - /* Do erase, kill and end-of-line processing. */ - ascval = c & 0x7f; - switch (ascval) { - case CR: - case NL: - *bp = '\0'; /* terminate logname */ - cp->eol = ascval; /* set end-of-line char */ - break; + /* Do erase, kill and end-of-line processing */ + switch (c) { + case '\r': + case '\n': + *bp = '\0'; + G.eol = c; + goto got_logname; case BS: case DEL: -#ifdef ANCIENT_BS_KILL_CHARS - case '#': -#endif - cp->erase = ascval; /* set erase character */ - if (bp > logname) { - full_write(STDOUT_FILENO, erase[cp->parity], 3); + G.termios.c_cc[VERASE] = c; + if (bp > G.line_buf) { + full_write(STDOUT_FILENO, "\010 \010", 3); bp--; } break; case CTL('U'): -#ifdef ANCIENT_BS_KILL_CHARS - case '@': -#endif - cp->kill = ascval; /* set kill character */ - while (bp > logname) { - full_write(STDOUT_FILENO, erase[cp->parity], 3); + while (bp > G.line_buf) { + full_write(STDOUT_FILENO, "\010 \010", 3); bp--; } break; case CTL('D'): exit(EXIT_SUCCESS); default: - if (ascval < ' ') { + if ((unsigned char)c < ' ') { /* ignore garbage characters */ - } else if ((int)(bp - logname) >= size_logname - 1) { - bb_error_msg_and_die("input overrun"); - } else { - full_write(STDOUT_FILENO, &c, 1); /* echo the character */ - *bp++ = ascval; /* and store it */ + } else if ((int)(bp - G.line_buf) < sizeof(G.line_buf) - 1) { + /* echo and store the character */ + full_write(STDOUT_FILENO, &c, 1); + *bp++ = c; } break; } - } - } - /* Handle names with upper case and no lower case. */ - -#ifdef HANDLE_ALLCAPS - cp->capslock = all_is_upcase(logname); - if (cp->capslock) { - for (bp = logname; *bp; bp++) - if (isupper(*bp)) - *bp = tolower(*bp); /* map name to lower case */ - } -#endif - return logname; -} - -/* termios_final - set the final tty mode bits */ -static void termios_final(struct options *op, struct termios *tp, struct chardata *cp) -{ - /* General terminal-independent stuff. */ - tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */ - tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; - /* no longer| ECHOCTL | ECHOPRT */ - tp->c_oflag |= OPOST; - /* tp->c_cflag = 0; */ - tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */ - tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */ - tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */ - tp->c_cc[VEOL] = DEF_EOL; -#ifdef VSWTC - tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */ -#endif - - /* Account for special characters seen in input. */ - if (cp->eol == CR) { - tp->c_iflag |= ICRNL; /* map CR in input to NL */ - tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */ - } - tp->c_cc[VERASE] = cp->erase; /* set erase character */ - tp->c_cc[VKILL] = cp->kill; /* set kill character */ - - /* Account for the presence or absence of parity bits in input. */ - switch (cp->parity) { - case 0: /* space (always 0) parity */ -// I bet most people go here - they use only 7-bit chars in usernames.... - break; - case 1: /* odd parity */ - tp->c_cflag |= PARODD; - /* FALLTHROUGH */ - case 2: /* even parity */ - tp->c_cflag |= PARENB; - tp->c_iflag |= INPCK | ISTRIP; - /* FALLTHROUGH */ - case (1 | 2): /* no parity bit */ - tp->c_cflag &= ~CSIZE; - tp->c_cflag |= CS7; -// FIXME: wtf? case 3: we saw both even and odd 8-bit bytes - -// it's probably some umlauts etc, but definitely NOT 7-bit!!! -// Entire parity detection madness here just begs for deletion... - break; - } - - /* Account for upper case without lower case. */ -#ifdef HANDLE_ALLCAPS - if (cp->capslock) { - tp->c_iflag |= IUCLC; - tp->c_lflag |= XCASE; - tp->c_oflag |= OLCUC; - } -#endif - /* Optionally enable hardware flow control */ -#ifdef CRTSCTS - if (op->flags & F_RTSCTS) - tp->c_cflag |= CRTSCTS; -#endif + } /* end of get char loop */ + got_logname: ; + } /* while logname is empty */ - /* Finally, make the new settings effective */ - if (tcsetattr_stdin_TCSANOW(tp) < 0) - bb_perror_msg_and_die("tcsetattr"); + return G.line_buf; } int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; @@ -568,27 +516,17 @@ int getty_main(int argc UNUSED_PARAM, char **argv) { int n; pid_t pid; - char *fakehost = NULL; /* Fake hostname for ut_host */ - char *logname; /* login name, given to /bin/login */ - /* Merging these into "struct local" may _seem_ to reduce - * parameter passing, but today's gcc will inline - * statics which are called once anyway, so don't do that */ - struct chardata chardata; /* set by get_logname() */ - struct termios termios; /* terminal mode bits */ - struct options options; - - chardata = init_chardata; + char *logname; - memset(&options, 0, sizeof(options)); - options.login = _PATH_LOGIN; /* default login program */ - options.tty = "tty1"; /* default tty line */ - options.initstring = ""; /* modem init string */ + INIT_G(); + G.login = _PATH_LOGIN; /* default login program */ #ifdef ISSUE - options.issue = ISSUE; /* default issue file */ + G.issue = ISSUE; /* default issue file */ #endif + G.eol = '\r'; - /* Parse command-line arguments. */ - parse_args(argv, &options, &fakehost); + /* Parse command-line arguments */ + parse_args(argv); logmode = LOGMODE_NONE; @@ -623,7 +561,7 @@ int getty_main(int argc UNUSED_PARAM, char **argv) /* Open the tty as standard input, if it is not "-" */ /* If it's not "-" and not taken yet, it will become our ctty */ debug("calling open_tty\n"); - open_tty(options.tty); + open_tty(); ndelay_off(0); debug("duping\n"); xdup2(0, 1); @@ -637,7 +575,7 @@ int getty_main(int argc UNUSED_PARAM, char **argv) * by patching the SunOS kernel variable "zsadtrlow" to a larger value; * 5 seconds seems to be a good value. */ - if (tcgetattr(0, &termios) < 0) + if (tcgetattr(STDIN_FILENO, &G.termios) < 0) bb_perror_msg_and_die("tcgetattr"); pid = getpid(); @@ -645,79 +583,76 @@ int getty_main(int argc UNUSED_PARAM, char **argv) // FIXME: do we need this? Otherwise "-" case seems to be broken... // /* Forcibly make fd 0 our controlling tty, even if another session // * has it as a ctty. (Another session loses ctty). */ - // ioctl(0, TIOCSCTTY, (void*)1); + // ioctl(STDIN_FILENO, TIOCSCTTY, (void*)1); /* Make ourself a foreground process group within our session */ - tcsetpgrp(0, pid); + tcsetpgrp(STDIN_FILENO, pid); #endif /* Update the utmp file. This tty is ours now! */ - update_utmp(pid, LOGIN_PROCESS, options.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(&termios, options.speeds[0], &options); + termios_init(G.speeds[0]); /* Write the modem init string and DON'T flush the buffers */ - if (options.flags & F_INITSTRING) { + if (option_mask32 & F_INITSTRING) { debug("writing init string\n"); - full_write1_str(options.initstring); + full_write1_str(G.initstring); } /* Optionally detect the baud rate from the modem status message */ debug("before autobaud\n"); - if (options.flags & F_PARSE) - auto_baud(line_buf, sizeof(line_buf), &termios); + if (option_mask32 & F_PARSE) + auto_baud(); /* Set the optional timer */ - alarm(options.timeout); /* if 0, alarm is not set */ + alarm(G.timeout); /* if 0, alarm is not set */ +//BUG: death by signal won't restore termios /* Optionally wait for CR or LF before writing /etc/issue */ - if (options.flags & F_WAITCRLF) { + if (option_mask32 & F_WAITCRLF) { char ch; - debug("waiting for cr-lf\n"); while (safe_read(STDIN_FILENO, &ch, 1) == 1) { debug("read %x\n", (unsigned char)ch); - ch &= 0x7f; /* strip "parity bit" */ if (ch == '\n' || ch == '\r') break; } } logname = NULL; - if (!(options.flags & F_NOPROMPT)) { - /* NB:termios_init already set line speed - * to options.speeds[0] */ + if (!(option_mask32 & F_NOPROMPT)) { + /* NB: termios_init already set line speed + * to G.speeds[0] */ int baud_index = 0; while (1) { - /* Read the login name. */ + /* Read the login name */ debug("reading login name\n"); - logname = get_logname(line_buf, sizeof(line_buf), - &options, &chardata); + logname = get_logname(); if (logname) break; - /* we are here only if options.numspeed > 1 */ - baud_index = (baud_index + 1) % options.numspeed; - cfsetispeed(&termios, options.speeds[baud_index]); - cfsetospeed(&termios, options.speeds[baud_index]); - tcsetattr_stdin_TCSANOW(&termios); + /* We are here only if G.numspeed > 1 */ + baud_index = (baud_index + 1) % G.numspeed; + cfsetspeed(&G.termios, G.speeds[baud_index]); + set_termios(); } } - /* Disable timer. */ + /* Disable timer */ alarm(0); - /* Finalize the termios settings. */ - termios_final(&options, &termios, &chardata); + /* 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(options.login, options.login, "--", logname, NULL); - bb_error_msg_and_die("can't execute '%s'", options.login); + BB_EXECLP(G.login, G.login, "--", logname, NULL); + bb_error_msg_and_die("can't execute '%s'", G.login); }