Changeset 3232 in MondoRescue for branches/3.2/mindi-busybox/loginutils/getty.c
- Timestamp:
- Jan 1, 2014, 12:47:38 AM (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/3.2/mindi-busybox/loginutils/getty.c
r2725 r3232 1 1 /* vi: set sw=4 ts=4: */ 2 /* agetty.c - another getty program for Linux. By W. Z. Venema 1989 2 /* 3 * Based on agetty - another getty program for Linux. By W. Z. Venema 1989 3 4 * Ported to Linux by Peter Orbaek <poe@daimi.aau.dk> 4 * This program is freely distributable. The entire man-page used to 5 * be here. Now read the real man-page agetty.8 instead. 5 * This program is freely distributable. 6 6 * 7 7 * option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95 8 8 * 9 9 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org> 10 * - added Native Language Support10 * - Added Native Language Support 11 11 * 12 12 * 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net> 13 * - enable hardware flow control before displaying /etc/issue 13 * - Enabled hardware flow control before displaying /etc/issue 14 * 15 * 2011-01 Venys Vlasenko 16 * - Removed parity detection code. It can't work reliably: 17 * if all chars received have bit 7 cleared and odd (or even) parity, 18 * it is impossible to determine whether other side is 8-bit,no-parity 19 * or 7-bit,odd(even)-parity. It also interferes with non-ASCII usernames. 20 * - From now on, we assume that parity is correctly set. 14 21 * 15 22 * Licensed under GPLv2 or later, see file LICENSE in this source tree. … … 18 25 #include "libbb.h" 19 26 #include <syslog.h> 20 21 #if ENABLE_FEATURE_UTMP22 # include <utmp.h> /* LOGIN_PROCESS */23 #endif24 25 27 #ifndef IUCLC 26 28 # define IUCLC 0 27 29 #endif 28 30 29 /* 30 * Some heuristics to find out what environment we are in: if it is not 31 * System V, assume it is SunOS 4. 32 */ 33 #ifdef LOGIN_PROCESS /* defined in System V utmp.h */ 34 #include <sys/utsname.h> 35 #else /* if !sysV style, wtmp/utmp code is off */ 36 #undef ENABLE_FEATURE_UTMP 37 #undef ENABLE_FEATURE_WTMP 38 #define ENABLE_FEATURE_UTMP 0 39 #define ENABLE_FEATURE_WTMP 0 40 #endif /* LOGIN_PROCESS */ 31 #ifndef LOGIN_PROCESS 32 # undef ENABLE_FEATURE_UTMP 33 # undef ENABLE_FEATURE_WTMP 34 # define ENABLE_FEATURE_UTMP 0 35 # define ENABLE_FEATURE_WTMP 0 36 #endif 37 38 39 /* The following is used for understandable diagnostics */ 40 #ifdef DEBUGGING 41 static FILE *dbf; 42 # define DEBUGTERM "/dev/ttyp0" 43 # define debug(...) do { fprintf(dbf, __VA_ARGS__); fflush(dbf); } while (0) 44 #else 45 # define debug(...) ((void)0) 46 #endif 47 41 48 42 49 /* … … 47 54 * and for line editing at the same time. 48 55 */ 49 50 /* I doubt there are systems which still need this */ 51 #undef HANDLE_ALLCAPS 52 #undef ANCIENT_BS_KILL_CHARS 53 56 #undef _PATH_LOGIN 54 57 #define _PATH_LOGIN "/bin/login" 55 58 56 /* If ISSUE is not defined, getty will never display the contents of the 59 /* Displayed before the login prompt. 60 * If ISSUE is not defined, getty will never display the contents of the 57 61 * /etc/issue file. You will not want to spit out large "issue" files at the 58 62 * wrong baud rate. 59 63 */ 60 #define ISSUE "/etc/issue" /* displayed before the login prompt */ 61 62 /* Some shorthands for control characters. */ 63 #define CTL(x) ((x) ^ 0100) /* Assumes ASCII dialect */ 64 #define CR CTL('M') /* carriage return */ 65 #define NL CTL('J') /* line feed */ 66 #define BS CTL('H') /* back space */ 67 #define DEL CTL('?') /* delete */ 68 69 /* Defaults for line-editing etc. characters; you may want to change this. */ 70 #define DEF_ERASE DEL /* default erase character */ 71 #define DEF_INTR CTL('C') /* default interrupt character */ 72 #define DEF_QUIT CTL('\\') /* default quit char */ 73 #define DEF_KILL CTL('U') /* default kill char */ 74 #define DEF_EOF CTL('D') /* default EOF char */ 75 #define DEF_EOL '\n' 76 #define DEF_SWITCH 0 /* default switch char */ 64 #define ISSUE "/etc/issue" 65 66 /* Macro to build Ctrl-LETTER. Assumes ASCII dialect */ 67 #define CTL(x) ((x) ^ 0100) 77 68 78 69 /* 79 * When multiple baud rates are specified on the command line, the first one80 * we will try is the first one specified.70 * When multiple baud rates are specified on the command line, 71 * the first one we will try is the first one specified. 81 72 */ 82 73 #define MAX_SPEED 10 /* max. nr. of baud rates */ 83 74 84 /* Storage for command-line options. */ 85 struct options { 86 int flags; /* toggle switches, see below */ 87 unsigned timeout; /* time-out period */ 75 struct globals { 76 unsigned timeout; 88 77 const char *login; /* login program */ 89 const char *tty; /* name of tty */ 90 const char *initstring; /* modem init string */ 78 const char *fakehost; 79 const char *tty_name; 80 char *initstring; /* modem init string */ 91 81 const char *issue; /* alternative issue file */ 92 82 int numspeed; /* number of baud rates to try */ 93 83 int speeds[MAX_SPEED]; /* baud rates to be tried */ 84 unsigned char eol; /* end-of-line char seen (CR or NL) */ 85 struct termios tty_attrs; 86 char line_buf[128]; 94 87 }; 95 88 96 /* Storage for things detected while the login name was read. */ 97 struct chardata { 98 unsigned char erase; /* erase character */ 99 unsigned char kill; /* kill character */ 100 unsigned char eol; /* end-of-line character */ 101 unsigned char parity; /* what parity did we see */ 102 /* (parity & 1): saw odd parity char with 7th bit set */ 103 /* (parity & 2): saw even parity char with 7th bit set */ 104 /* parity == 0: probably 7-bit, space parity? */ 105 /* parity == 1: probably 7-bit, odd parity? */ 106 /* parity == 2: probably 7-bit, even parity? */ 107 /* parity == 3: definitely 8 bit, no parity! */ 108 /* Hmm... with any value of "parity" 8 bit, no parity is possible */ 109 #ifdef HANDLE_ALLCAPS 110 unsigned char capslock; /* upper case without lower case */ 111 #endif 112 }; 113 114 115 /* Initial values for the above. */ 116 static const struct chardata init_chardata = { 117 DEF_ERASE, /* default erase character */ 118 DEF_KILL, /* default kill character */ 119 13, /* default eol char */ 120 0, /* space parity */ 121 #ifdef HANDLE_ALLCAPS 122 0, /* no capslock */ 123 #endif 124 }; 89 #define G (*ptr_to_globals) 90 #define INIT_G() do { \ 91 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 92 } while (0) 93 94 //usage:#define getty_trivial_usage 95 //usage: "[OPTIONS] BAUD_RATE[,BAUD_RATE]... TTY [TERMTYPE]" 96 //usage:#define getty_full_usage "\n\n" 97 //usage: "Open TTY, prompt for login name, then invoke /bin/login\n" 98 //usage: "\n -h Enable hardware RTS/CTS flow control" 99 //usage: "\n -L Set CLOCAL (ignore Carrier Detect state)" 100 //usage: "\n -m Get baud rate from modem's CONNECT status message" 101 //usage: "\n -n Don't prompt for login name" 102 //usage: "\n -w Wait for CR or LF before sending /etc/issue" 103 //usage: "\n -i Don't display /etc/issue" 104 //usage: "\n -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue" 105 //usage: "\n -l LOGIN Invoke LOGIN instead of /bin/login" 106 //usage: "\n -t SEC Terminate after SEC if no login name is read" 107 //usage: "\n -I INITSTR Send INITSTR before anything else" 108 //usage: "\n -H HOST Log HOST into the utmp file as the hostname" 109 //usage: "\n" 110 //usage: "\nBAUD_RATE of 0 leaves it unchanged" 125 111 126 112 static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn"; 127 #define F_INITSTRING (1 << 0) /* -I initstring is set */ 128 #define F_LOCAL (1 << 1) /* -L force local */ 129 #define F_FAKEHOST (1 << 2) /* -H fake hostname */ 130 #define F_CUSTISSUE (1 << 3) /* -f give alternative issue file */ 131 #define F_RTSCTS (1 << 4) /* -h enable RTS/CTS flow control */ 132 #define F_ISSUE (1 << 5) /* -i display /etc/issue */ 133 #define F_LOGIN (1 << 6) /* -l non-default login program */ 134 #define F_PARSE (1 << 7) /* -m process modem status messages */ 135 #define F_TIMEOUT (1 << 8) /* -t time out */ 136 #define F_WAITCRLF (1 << 9) /* -w wait for CR or LF */ 137 #define F_NOPROMPT (1 << 10) /* -n don't ask for login name */ 138 139 140 #define line_buf bb_common_bufsiz1 141 142 /* The following is used for understandable diagnostics. */ 143 #ifdef DEBUGGING 144 static FILE *dbf; 145 #define DEBUGTERM "/dev/ttyp0" 146 #define debug(...) do { fprintf(dbf, __VA_ARGS__); fflush(dbf); } while (0) 147 #else 148 #define debug(...) ((void)0) 149 #endif 150 151 152 /* bcode - convert speed string to speed code; return <= 0 on failure */ 113 #define F_INITSTRING (1 << 0) /* -I */ 114 #define F_LOCAL (1 << 1) /* -L */ 115 #define F_FAKEHOST (1 << 2) /* -H */ 116 #define F_CUSTISSUE (1 << 3) /* -f */ 117 #define F_RTSCTS (1 << 4) /* -h */ 118 #define F_NOISSUE (1 << 5) /* -i */ 119 #define F_LOGIN (1 << 6) /* -l */ 120 #define F_PARSE (1 << 7) /* -m */ 121 #define F_TIMEOUT (1 << 8) /* -t */ 122 #define F_WAITCRLF (1 << 9) /* -w */ 123 #define F_NOPROMPT (1 << 10) /* -n */ 124 125 126 /* convert speed string to speed code; return <= 0 on failure */ 153 127 static int bcode(const char *s) 154 128 { … … 159 133 } 160 134 161 /* parse _speeds - parsealternate baud rates */162 static void parse_speeds( struct options *op,char *arg)135 /* parse alternate baud rates */ 136 static void parse_speeds(char *arg) 163 137 { 164 138 char *cp; … … 167 141 debug("entered parse_speeds\n"); 168 142 while ((cp = strsep(&arg, ",")) != NULL) { 169 op->speeds[op->numspeed] = bcode(cp);170 if ( op->speeds[op->numspeed] < 0)143 G.speeds[G.numspeed] = bcode(cp); 144 if (G.speeds[G.numspeed] < 0) 171 145 bb_error_msg_and_die("bad speed: %s", cp); 172 146 /* note: arg "0" turns into speed B0 */ 173 op->numspeed++;174 if ( op->numspeed > MAX_SPEED)147 G.numspeed++; 148 if (G.numspeed > MAX_SPEED) 175 149 bb_error_msg_and_die("too many alternate speeds"); 176 150 } … … 178 152 } 179 153 180 /* parse _args - parsecommand-line arguments */181 static void parse_args(char **argv , struct options *op, char **fakehost_p)154 /* parse command-line arguments */ 155 static void parse_args(char **argv) 182 156 { 183 157 char *ts; 158 int flags; 184 159 185 160 opt_complementary = "-2:t+"; /* at least 2 args; -t N */ 186 op->flags = getopt32(argv, opt_string, 187 &(op->initstring), fakehost_p, &(op->issue), 188 &(op->login), &op->timeout); 161 flags = getopt32(argv, opt_string, 162 &G.initstring, &G.fakehost, &G.issue, 163 &G.login, &G.timeout 164 ); 165 if (flags & F_INITSTRING) { 166 G.initstring = xstrdup(G.initstring); 167 /* decode \ddd octal codes into chars */ 168 strcpy_and_process_escape_sequences(G.initstring, G.initstring); 169 } 189 170 argv += optind; 190 if (op->flags & F_INITSTRING) {191 op->initstring = xstrdup(op->initstring);192 /* decode \ddd octal codes into chars */193 strcpy_and_process_escape_sequences((char*)op->initstring, op->initstring);194 }195 op->flags ^= F_ISSUE; /* invert flag "show /etc/issue" */196 171 debug("after getopt\n"); 197 172 198 /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */199 op->tty = argv[0]; /* tty name */200 ts = argv[1]; /* baud rate(s) */173 /* We loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ 174 G.tty_name = argv[0]; 175 ts = argv[1]; /* baud rate(s) */ 201 176 if (isdigit(argv[0][0])) { 202 /* a number first, assume it's a speed (BSD style) */ 203 op->tty = ts; /* tty name is in argv[1] */ 204 ts = argv[0]; /* baud rate(s) */ 205 } 206 parse_speeds(op, ts); 207 applet_name = xasprintf("getty: %s", op->tty); 177 /* A number first, assume it's a speed (BSD style) */ 178 G.tty_name = ts; /* tty name is in argv[1] */ 179 ts = argv[0]; /* baud rate(s) */ 180 } 181 parse_speeds(ts); 208 182 209 183 if (argv[2]) … … 213 187 } 214 188 215 /* open_tty - set up tty as standard { input, output, error } */ 216 static void open_tty(const char *tty) 217 { 218 /* Set up new standard input, unless we are given an already opened port. */ 219 if (NOT_LONE_DASH(tty)) { 220 // struct stat st; 221 // int cur_dir_fd; 222 // int fd; 223 224 /* Sanity checks... */ 225 // cur_dir_fd = xopen(".", O_DIRECTORY | O_NONBLOCK); 226 // xchdir("/dev"); 227 // xstat(tty, &st); 228 // if (!S_ISCHR(st.st_mode)) 229 // bb_error_msg_and_die("not a character device"); 230 231 if (tty[0] != '/') 232 tty = xasprintf("/dev/%s", tty); /* will leak it */ 233 234 /* Open the tty as standard input. */ 189 /* set up tty as standard input, output, error */ 190 static void open_tty(void) 191 { 192 /* Set up new standard input, unless we are given an already opened port */ 193 if (NOT_LONE_DASH(G.tty_name)) { 194 if (G.tty_name[0] != '/') 195 G.tty_name = xasprintf("/dev/%s", G.tty_name); /* will leak it */ 196 197 /* Open the tty as standard input */ 235 198 debug("open(2)\n"); 236 199 close(0); 237 /*fd =*/ xopen(tty, O_RDWR | O_NONBLOCK); /* uses fd 0 */ 238 239 // /* Restore current directory */ 240 // fchdir(cur_dir_fd); 241 242 /* Open the tty as standard input, continued */ 243 // xmove_fd(fd, 0); 244 // /* fd is >= cur_dir_fd, and cur_dir_fd gets closed too here: */ 245 // while (fd > 2) 246 // close(fd--); 247 248 /* Set proper protections and ownership. */ 200 xopen(G.tty_name, O_RDWR | O_NONBLOCK); /* uses fd 0 */ 201 202 /* Set proper protections and ownership */ 249 203 fchown(0, 0, 0); /* 0:0 */ 250 204 fchmod(0, 0620); /* crw--w---- */ 251 205 } else { 206 char *n; 252 207 /* 253 * Standard input should already be connected to an open port. Make254 * sure it is open for read/write.208 * Standard input should already be connected to an open port. 209 * Make sure it is open for read/write. 255 210 */ 256 if ((fcntl(0, F_GETFL) & O_RDWR) != O_RDWR)211 if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR) 257 212 bb_error_msg_and_die("stdin is not open for read/write"); 258 } 259 } 260 261 /* termios_init - initialize termios settings */ 262 static void termios_init(struct termios *tp, int speed, struct options *op) 263 { 264 speed_t ispeed, ospeed; 265 /* 266 * Initial termios settings: 8-bit characters, raw-mode, blocking i/o. 213 214 /* Try to get real tty name instead of "-" */ 215 n = xmalloc_ttyname(0); 216 if (n) 217 G.tty_name = n; 218 } 219 applet_name = xasprintf("getty: %s", skip_dev_pfx(G.tty_name)); 220 } 221 222 static void set_tty_attrs(void) 223 { 224 if (tcsetattr_stdin_TCSANOW(&G.tty_attrs) < 0) 225 bb_perror_msg_and_die("tcsetattr"); 226 } 227 228 /* We manipulate tty_attrs this way: 229 * - first, we read existing tty_attrs 230 * - init_tty_attrs modifies some parts and sets it 231 * - auto_baud and/or BREAK processing can set different speed and set tty attrs 232 * - finalize_tty_attrs again modifies some parts and sets tty attrs before 233 * execing login 234 */ 235 static void init_tty_attrs(int speed) 236 { 237 /* Try to drain output buffer, with 5 sec timeout. 238 * Added on request from users of ~600 baud serial interface 239 * with biggish buffer on a 90MHz CPU. 240 * They were losing hundreds of bytes of buffered output 241 * on tcflush. 242 */ 243 signal_no_SA_RESTART_empty_mask(SIGALRM, record_signo); 244 alarm(5); 245 tcdrain(STDIN_FILENO); 246 alarm(0); 247 248 /* Flush input and output queues, important for modems! */ 249 tcflush(STDIN_FILENO, TCIOFLUSH); 250 251 /* Set speed if it wasn't specified as "0" on command line */ 252 if (speed != B0) 253 cfsetspeed(&G.tty_attrs, speed); 254 255 /* Initial settings: 8-bit characters, raw mode, blocking i/o. 267 256 * Special characters are set after we have read the login name; all 268 * reads will be done in raw mode anyway. Errors will be dealt with 269 * later on. 270 */ 271 /* flush input and output queues, important for modems! */ 272 tcflush(0, TCIOFLUSH); 273 ispeed = ospeed = speed; 274 if (speed == B0) { 275 /* Speed was specified as "0" on command line. 276 * Just leave it unchanged */ 277 ispeed = cfgetispeed(tp); 278 ospeed = cfgetospeed(tp); 279 } 280 tp->c_cflag = CS8 | HUPCL | CREAD; 281 if (op->flags & F_LOCAL) 282 tp->c_cflag |= CLOCAL; 283 cfsetispeed(tp, ispeed); 284 cfsetospeed(tp, ospeed); 285 286 tp->c_iflag = tp->c_lflag = 0; 287 tp->c_oflag = OPOST | ONLCR; 288 tp->c_cc[VMIN] = 1; 289 tp->c_cc[VTIME] = 0; 257 * reads will be done in raw mode anyway. 258 */ 259 /* Clear all bits except: */ 260 G.tty_attrs.c_cflag &= (0 261 /* 2 stop bits (1 otherwise) 262 * Enable parity bit (both on input and output) 263 * Odd parity (else even) 264 */ 265 | CSTOPB | PARENB | PARODD 266 #ifdef CMSPAR 267 | CMSPAR /* mark or space parity */ 268 #endif 269 #ifdef CBAUD 270 | CBAUD /* (output) baud rate */ 271 #endif 272 #ifdef CBAUDEX 273 | CBAUDEX /* (output) baud rate */ 274 #endif 275 #ifdef CIBAUD 276 | CIBAUD /* input baud rate */ 277 #endif 278 ); 279 /* Set: 8 bits; hang up (drop DTR) on last close; enable receive */ 280 G.tty_attrs.c_cflag |= CS8 | HUPCL | CREAD; 281 if (option_mask32 & F_LOCAL) { 282 /* ignore Carrier Detect pin: 283 * opens don't block when CD is low, 284 * losing CD doesn't hang up processes whose ctty is this tty 285 */ 286 G.tty_attrs.c_cflag |= CLOCAL; 287 } 288 #ifdef CRTSCTS 289 if (option_mask32 & F_RTSCTS) 290 G.tty_attrs.c_cflag |= CRTSCTS; /* flow control using RTS/CTS pins */ 291 #endif 292 G.tty_attrs.c_iflag = 0; 293 G.tty_attrs.c_lflag = 0; 294 /* non-raw output; add CR to each NL */ 295 G.tty_attrs.c_oflag = OPOST | ONLCR; 296 297 /* reads would block only if < 1 char is available */ 298 G.tty_attrs.c_cc[VMIN] = 1; 299 /* no timeout (reads block forever) */ 300 G.tty_attrs.c_cc[VTIME] = 0; 290 301 #ifdef __linux__ 291 tp->c_line = 0; 292 #endif 293 294 /* Optionally enable hardware flow control */ 295 #ifdef CRTSCTS 296 if (op->flags & F_RTSCTS) 297 tp->c_cflag |= CRTSCTS; 298 #endif 299 300 tcsetattr_stdin_TCSANOW(tp); 302 G.tty_attrs.c_line = 0; 303 #endif 304 305 set_tty_attrs(); 301 306 302 307 debug("term_io 2\n"); 303 308 } 304 309 305 /* auto_baud - extract baud rate from modem status message */ 306 static void auto_baud(char *buf, unsigned size_buf, struct termios *tp) 307 { 308 int speed; 309 int vmin; 310 unsigned iflag; 311 char *bp; 310 static void finalize_tty_attrs(void) 311 { 312 /* software flow control on output (stop sending if XOFF is recvd); 313 * and on input (send XOFF when buffer is full) 314 */ 315 G.tty_attrs.c_iflag |= IXON | IXOFF; 316 if (G.eol == '\r') { 317 G.tty_attrs.c_iflag |= ICRNL; /* map CR on input to NL */ 318 } 319 /* Other bits in c_iflag: 320 * IXANY Any recvd char enables output (any char is also a XON) 321 * INPCK Enable parity check 322 * IGNPAR Ignore parity errors (drop bad bytes) 323 * PARMRK Mark parity errors with 0xff, 0x00 prefix 324 * (else bad byte is received as 0x00) 325 * ISTRIP Strip parity bit 326 * IGNBRK Ignore break condition 327 * BRKINT Send SIGINT on break - maybe set this? 328 * INLCR Map NL to CR 329 * IGNCR Ignore CR 330 * ICRNL Map CR to NL 331 * IUCLC Map uppercase to lowercase 332 * IMAXBEL Echo BEL on input line too long 333 * IUTF8 Appears to affect tty's idea of char widths, 334 * observed to improve backspacing through Unicode chars 335 */ 336 337 /* line buffered input (NL or EOL or EOF chars end a line); 338 * recognize INT/QUIT/SUSP chars; 339 * echo input chars; 340 * echo BS-SP-BS on erase character; 341 * echo kill char specially, not as ^c (ECHOKE controls how exactly); 342 * erase all input via BS-SP-BS on kill char (else go to next line) 343 */ 344 G.tty_attrs.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; 345 /* Other bits in c_lflag: 346 * XCASE Map uppercase to \lowercase [tried, doesn't work] 347 * ECHONL Echo NL even if ECHO is not set 348 * ECHOCTL Echo ctrl chars as ^c (else don't echo) - maybe set this? 349 * ECHOPRT On erase, echo erased chars 350 * [qwe<BS><BS><BS> input looks like "qwe\ewq/" on screen] 351 * NOFLSH Don't flush input buffer after interrupt or quit chars 352 * IEXTEN Enable extended functions (??) 353 * [glibc says it enables c_cc[LNEXT] "enter literal char" 354 * and c_cc[VDISCARD] "toggle discard buffered output" chars] 355 * FLUSHO Output being flushed (c_cc[VDISCARD] is in effect) 356 * PENDIN Retype pending input at next read or input char 357 * (c_cc[VREPRINT] is being processed) 358 * TOSTOP Send SIGTTOU for background output 359 * (why "stty sane" unsets this bit?) 360 */ 361 362 G.tty_attrs.c_cc[VINTR] = CTL('C'); 363 G.tty_attrs.c_cc[VQUIT] = CTL('\\'); 364 G.tty_attrs.c_cc[VEOF] = CTL('D'); 365 G.tty_attrs.c_cc[VEOL] = '\n'; 366 #ifdef VSWTC 367 G.tty_attrs.c_cc[VSWTC] = 0; 368 #endif 369 #ifdef VSWTCH 370 G.tty_attrs.c_cc[VSWTCH] = 0; 371 #endif 372 G.tty_attrs.c_cc[VKILL] = CTL('U'); 373 /* Other control chars: 374 * VEOL2 375 * VERASE, VWERASE - (word) erase. we may set VERASE in get_logname 376 * VREPRINT - reprint current input buffer 377 * VLNEXT, VDISCARD, VSTATUS 378 * VSUSP, VDSUSP - send (delayed) SIGTSTP 379 * VSTART, VSTOP - chars used for IXON/IXOFF 380 */ 381 382 set_tty_attrs(); 383 384 /* Now the newline character should be properly written */ 385 full_write(STDOUT_FILENO, "\n", 1); 386 } 387 388 /* extract baud rate from modem status message */ 389 static void auto_baud(void) 390 { 312 391 int nread; 313 392 … … 327 406 */ 328 407 329 /* 330 * Use 7-bit characters, don't block if input queue is empty. Errors will 331 * be dealt with later on. 332 */ 333 iflag = tp->c_iflag; 334 tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */ 335 vmin = tp->c_cc[VMIN]; 336 tp->c_cc[VMIN] = 0; /* don't block if queue empty */ 337 tcsetattr_stdin_TCSANOW(tp); 408 G.tty_attrs.c_cc[VMIN] = 0; /* don't block reads (min read is 0 chars) */ 409 set_tty_attrs(); 338 410 339 411 /* … … 342 414 */ 343 415 sleep(1); 344 nread = safe_read(STDIN_FILENO, buf, size_buf- 1);416 nread = safe_read(STDIN_FILENO, G.line_buf, sizeof(G.line_buf) - 1); 345 417 if (nread > 0) { 346 buf[nread] = '\0'; 347 for (bp = buf; bp < buf + nread; bp++) { 418 int speed; 419 char *bp; 420 G.line_buf[nread] = '\0'; 421 for (bp = G.line_buf; bp < G.line_buf + nread; bp++) { 348 422 if (isdigit(*bp)) { 349 423 speed = bcode(bp); 350 424 if (speed > 0) 351 cfsetspeed( tp, speed);425 cfsetspeed(&G.tty_attrs, speed); 352 426 break; 353 427 } … … 355 429 } 356 430 357 /* Restore terminal settings. Errors will be dealt with later on. */ 358 tp->c_iflag = iflag; 359 tp->c_cc[VMIN] = vmin; 360 tcsetattr_stdin_TCSANOW(tp); 361 } 362 363 /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */ 364 static void do_prompt(struct options *op) 365 { 431 /* Restore terminal settings */ 432 G.tty_attrs.c_cc[VMIN] = 1; /* restore to value set by init_tty_attrs */ 433 set_tty_attrs(); 434 } 435 436 /* get user name, establish parity, speed, erase, kill, eol; 437 * return NULL on BREAK, logname on success 438 */ 439 static char *get_logname(void) 440 { 441 char *bp; 442 char c; 443 444 /* Flush pending input (esp. after parsing or switching the baud rate) */ 445 usleep(100*1000); /* 0.1 sec */ 446 tcflush(STDIN_FILENO, TCIFLUSH); 447 448 /* Prompt for and read a login name */ 449 do { 450 /* Write issue file and prompt */ 366 451 #ifdef ISSUE 367 print_login_issue(op->issue, op->tty); 368 #endif 369 print_login_prompt(); 370 } 371 372 #ifdef HANDLE_ALLCAPS 373 /* all_is_upcase - string contains upper case without lower case */ 374 /* returns 1 if true, 0 if false */ 375 static int all_is_upcase(const char *s) 376 { 377 while (*s) 378 if (islower(*s++)) 379 return 0; 380 return 1; 381 } 382 #endif 383 384 /* get_logname - get user name, establish parity, speed, erase, kill, eol; 385 * return NULL on BREAK, logname on success */ 386 static char *get_logname(char *logname, unsigned size_logname, 387 struct options *op, struct chardata *cp) 388 { 389 char *bp; 390 char c; /* input character, full eight bits */ 391 char ascval; /* low 7 bits of input character */ 392 int bits; /* # of "1" bits per character */ 393 int mask; /* mask with 1 bit up */ 394 static const char erase[][3] = {/* backspace-space-backspace */ 395 "\010\040\010", /* space parity */ 396 "\010\040\010", /* odd parity */ 397 "\210\240\210", /* even parity */ 398 "\010\040\010", /* 8 bit no parity */ 399 }; 400 401 /* NB: *cp is pre-initialized with init_chardata */ 402 403 /* Flush pending input (esp. after parsing or switching the baud rate). */ 404 sleep(1); 405 tcflush(0, TCIOFLUSH); 406 407 /* Prompt for and read a login name. */ 408 logname[0] = '\0'; 409 while (!logname[0]) { 410 /* Write issue file and prompt, with "parity" bit == 0. */ 411 do_prompt(op); 412 413 /* Read name, watch for break, parity, erase, kill, end-of-line. */ 414 bp = logname; 415 cp->eol = '\0'; 416 while (cp->eol == '\0') { 417 418 /* Do not report trivial EINTR/EIO errors. */ 452 if (!(option_mask32 & F_NOISSUE)) 453 print_login_issue(G.issue, G.tty_name); 454 #endif 455 print_login_prompt(); 456 457 /* Read name, watch for break, erase, kill, end-of-line */ 458 bp = G.line_buf; 459 while (1) { 460 /* Do not report trivial EINTR/EIO errors */ 419 461 errno = EINTR; /* make read of 0 bytes be silent too */ 420 462 if (read(STDIN_FILENO, &c, 1) < 1) { 463 finalize_tty_attrs(); 421 464 if (errno == EINTR || errno == EIO) 422 465 exit(EXIT_SUCCESS); … … 424 467 } 425 468 426 /* BREAK. If we have speeds to try, 427 * return NULL (will switch speeds and return here) */ 428 if (c == '\0' && op->numspeed > 1) 429 return NULL; 430 431 /* Do parity bit handling. */ 432 if (!(op->flags & F_LOCAL) && (c & 0x80)) { /* "parity" bit on? */ 433 bits = 1; 434 mask = 1; 435 while (mask & 0x7f) { 436 if (mask & c) 437 bits++; /* count "1" bits */ 438 mask <<= 1; 439 } 440 /* ... |= 2 - even, 1 - odd */ 441 cp->parity |= 2 - (bits & 1); 442 } 443 444 /* Do erase, kill and end-of-line processing. */ 445 ascval = c & 0x7f; 446 switch (ascval) { 447 case CR: 448 case NL: 449 *bp = '\0'; /* terminate logname */ 450 cp->eol = ascval; /* set end-of-line char */ 451 break; 452 case BS: 453 case DEL: 454 #ifdef ANCIENT_BS_KILL_CHARS 455 case '#': 456 #endif 457 cp->erase = ascval; /* set erase character */ 458 if (bp > logname) { 459 full_write(STDOUT_FILENO, erase[cp->parity], 3); 469 switch (c) { 470 case '\r': 471 case '\n': 472 *bp = '\0'; 473 G.eol = c; 474 goto got_logname; 475 case CTL('H'): 476 case 0x7f: 477 G.tty_attrs.c_cc[VERASE] = c; 478 if (bp > G.line_buf) { 479 full_write(STDOUT_FILENO, "\010 \010", 3); 460 480 bp--; 461 481 } 462 482 break; 463 483 case CTL('U'): 464 #ifdef ANCIENT_BS_KILL_CHARS 465 case '@': 466 #endif 467 cp->kill = ascval; /* set kill character */ 468 while (bp > logname) { 469 full_write(STDOUT_FILENO, erase[cp->parity], 3); 484 while (bp > G.line_buf) { 485 full_write(STDOUT_FILENO, "\010 \010", 3); 470 486 bp--; 471 487 } 472 488 break; 489 case CTL('C'): 473 490 case CTL('D'): 491 finalize_tty_attrs(); 474 492 exit(EXIT_SUCCESS); 493 case '\0': 494 /* BREAK. If we have speeds to try, 495 * return NULL (will switch speeds and return here) */ 496 if (G.numspeed > 1) 497 return NULL; 498 /* fall through and ignore it */ 475 499 default: 476 if ( ascval< ' ') {500 if ((unsigned char)c < ' ') { 477 501 /* ignore garbage characters */ 478 } else if ((int)(bp - logname) >= size_logname - 1) { 479 bb_error_msg_and_die("input overrun"); 480 } else { 481 full_write(STDOUT_FILENO, &c, 1); /* echo the character */ 482 *bp++ = ascval; /* and store it */ 502 } else if ((int)(bp - G.line_buf) < sizeof(G.line_buf) - 1) { 503 /* echo and store the character */ 504 full_write(STDOUT_FILENO, &c, 1); 505 *bp++ = c; 483 506 } 484 507 break; 485 508 } 486 } 487 } 488 /* Handle names with upper case and no lower case. */ 489 490 #ifdef HANDLE_ALLCAPS 491 cp->capslock = all_is_upcase(logname); 492 if (cp->capslock) { 493 for (bp = logname; *bp; bp++) 494 if (isupper(*bp)) 495 *bp = tolower(*bp); /* map name to lower case */ 496 } 497 #endif 498 return logname; 499 } 500 501 /* termios_final - set the final tty mode bits */ 502 static void termios_final(struct options *op, struct termios *tp, struct chardata *cp) 503 { 504 /* General terminal-independent stuff. */ 505 tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */ 506 tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; 507 /* no longer| ECHOCTL | ECHOPRT */ 508 tp->c_oflag |= OPOST; 509 /* tp->c_cflag = 0; */ 510 tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */ 511 tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */ 512 tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */ 513 tp->c_cc[VEOL] = DEF_EOL; 514 #ifdef VSWTC 515 tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */ 516 #endif 517 518 /* Account for special characters seen in input. */ 519 if (cp->eol == CR) { 520 tp->c_iflag |= ICRNL; /* map CR in input to NL */ 521 tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */ 522 } 523 tp->c_cc[VERASE] = cp->erase; /* set erase character */ 524 tp->c_cc[VKILL] = cp->kill; /* set kill character */ 525 526 /* Account for the presence or absence of parity bits in input. */ 527 switch (cp->parity) { 528 case 0: /* space (always 0) parity */ 529 // I bet most people go here - they use only 7-bit chars in usernames.... 530 break; 531 case 1: /* odd parity */ 532 tp->c_cflag |= PARODD; 533 /* FALLTHROUGH */ 534 case 2: /* even parity */ 535 tp->c_cflag |= PARENB; 536 tp->c_iflag |= INPCK | ISTRIP; 537 /* FALLTHROUGH */ 538 case (1 | 2): /* no parity bit */ 539 tp->c_cflag &= ~CSIZE; 540 tp->c_cflag |= CS7; 541 // FIXME: wtf? case 3: we saw both even and odd 8-bit bytes - 542 // it's probably some umlauts etc, but definitely NOT 7-bit!!! 543 // Entire parity detection madness here just begs for deletion... 544 break; 545 } 546 547 /* Account for upper case without lower case. */ 548 #ifdef HANDLE_ALLCAPS 549 if (cp->capslock) { 550 tp->c_iflag |= IUCLC; 551 tp->c_lflag |= XCASE; 552 tp->c_oflag |= OLCUC; 553 } 554 #endif 555 /* Optionally enable hardware flow control */ 556 #ifdef CRTSCTS 557 if (op->flags & F_RTSCTS) 558 tp->c_cflag |= CRTSCTS; 559 #endif 560 561 /* Finally, make the new settings effective */ 562 if (tcsetattr_stdin_TCSANOW(tp) < 0) 563 bb_perror_msg_and_die("tcsetattr"); 509 } /* end of get char loop */ 510 got_logname: ; 511 } while (G.line_buf[0] == '\0'); /* while logname is empty */ 512 513 return G.line_buf; 514 } 515 516 static void alarm_handler(int sig UNUSED_PARAM) 517 { 518 finalize_tty_attrs(); 519 _exit(EXIT_SUCCESS); 564 520 } 565 521 … … 568 524 { 569 525 int n; 570 pid_t pid; 571 char *fakehost = NULL; /* Fake hostname for ut_host */ 572 char *logname; /* login name, given to /bin/login */ 573 /* Merging these into "struct local" may _seem_ to reduce 574 * parameter passing, but today's gcc will inline 575 * statics which are called once anyway, so don't do that */ 576 struct chardata chardata; /* set by get_logname() */ 577 struct termios termios; /* terminal mode bits */ 578 struct options options; 579 580 chardata = init_chardata; 581 582 memset(&options, 0, sizeof(options)); 583 options.login = _PATH_LOGIN; /* default login program */ 584 options.tty = "tty1"; /* default tty line */ 585 options.initstring = ""; /* modem init string */ 526 pid_t pid, tsid; 527 char *logname; 528 529 INIT_G(); 530 G.login = _PATH_LOGIN; /* default login program */ 586 531 #ifdef ISSUE 587 options.issue = ISSUE; /* default issue file */ 588 #endif 589 590 /* Parse command-line arguments. */ 591 parse_args(argv, &options, &fakehost); 592 593 logmode = LOGMODE_NONE; 594 595 /* Create new session, lose controlling tty, if any */ 596 /* docs/ctty.htm says: 597 * "This is allowed only when the current process 598 * is not a process group leader" - is this a problem? */ 599 setsid(); 600 /* close stdio, and stray descriptors, just in case */ 532 G.issue = ISSUE; /* default issue file */ 533 #endif 534 G.eol = '\r'; 535 536 /* Parse command-line arguments */ 537 parse_args(argv); 538 539 /* Create new session and pgrp, lose controlling tty */ 540 pid = setsid(); /* this also gives us our pid :) */ 541 if (pid < 0) { 542 int fd; 543 /* :( 544 * docs/ctty.htm says: 545 * "This is allowed only when the current process 546 * is not a process group leader". 547 * Thus, setsid() will fail if we _already_ are 548 * a session leader - which is quite possible for getty! 549 */ 550 pid = getpid(); 551 if (getsid(0) != pid) { 552 //for debugging: 553 //bb_perror_msg_and_die("setsid failed:" 554 // " pid %d ppid %d" 555 // " sid %d pgid %d", 556 // pid, getppid(), 557 // getsid(0), getpgid(0)); 558 bb_perror_msg_and_die("setsid"); 559 } 560 /* Looks like we are already a session leader. 561 * In this case (setsid failed) we may still have ctty, 562 * and it may be different from tty we need to control! 563 * If we still have ctty, on Linux ioctl(TIOCSCTTY) 564 * (which we are going to use a bit later) always fails - 565 * even if we try to take ctty which is already ours! 566 * Try to drop old ctty now to prevent that. 567 * Use O_NONBLOCK: old ctty may be a serial line. 568 */ 569 fd = open("/dev/tty", O_RDWR | O_NONBLOCK); 570 if (fd >= 0) { 571 /* TIOCNOTTY sends SIGHUP to the foreground 572 * process group - which may include us! 573 * Make sure to not die on it: 574 */ 575 sighandler_t old = signal(SIGHUP, SIG_IGN); 576 ioctl(fd, TIOCNOTTY); 577 close(fd); 578 signal(SIGHUP, old); 579 } 580 } 581 582 /* Close stdio, and stray descriptors, just in case */ 601 583 n = xopen(bb_dev_null, O_RDWR); 602 584 /* dup2(n, 0); - no, we need to handle "getty - 9600" too */ … … 622 604 623 605 /* Open the tty as standard input, if it is not "-" */ 624 /* If it's not "-" and not taken yet, it will become our ctty */625 606 debug("calling open_tty\n"); 626 open_tty( options.tty);627 ndelay_off( 0);607 open_tty(); 608 ndelay_off(STDIN_FILENO); 628 609 debug("duping\n"); 629 xdup2(0, 1); 630 xdup2(0, 2); 610 xdup2(STDIN_FILENO, 1); 611 xdup2(STDIN_FILENO, 2); 612 613 /* Steal ctty if we don't have it yet */ 614 tsid = tcgetsid(STDIN_FILENO); 615 if (tsid < 0 || pid != tsid) { 616 if (ioctl(STDIN_FILENO, TIOCSCTTY, /*force:*/ (long)1) < 0) 617 bb_perror_msg_and_die("TIOCSCTTY"); 618 } 619 620 #ifdef __linux__ 621 /* Make ourself a foreground process group within our session */ 622 if (tcsetpgrp(STDIN_FILENO, pid) < 0) 623 bb_perror_msg_and_die("tcsetpgrp"); 624 #endif 631 625 632 626 /* … … 638 632 * 5 seconds seems to be a good value. 639 633 */ 640 if (tcgetattr( 0, &termios) < 0)634 if (tcgetattr(STDIN_FILENO, &G.tty_attrs) < 0) 641 635 bb_perror_msg_and_die("tcgetattr"); 642 636 643 pid = getpid();644 #ifdef __linux__645 // FIXME: do we need this? Otherwise "-" case seems to be broken...646 // /* Forcibly make fd 0 our controlling tty, even if another session647 // * has it as a ctty. (Another session loses ctty). */648 // ioctl(0, TIOCSCTTY, (void*)1);649 /* Make ourself a foreground process group within our session */650 tcsetpgrp(0, pid);651 #endif652 653 637 /* Update the utmp file. This tty is ours now! */ 654 update_utmp(pid, LOGIN_PROCESS, options.tty, "LOGIN",fakehost);655 656 /* Initialize t he termios settings (raw mode, eight-bit, blocking i/o).*/657 debug("calling termios_init\n");658 termios_init(&termios, options.speeds[0], &options);638 update_utmp(pid, LOGIN_PROCESS, G.tty_name, "LOGIN", G.fakehost); 639 640 /* Initialize tty attrs (raw mode, eight-bit, blocking i/o) */ 641 debug("calling init_tty_attrs\n"); 642 init_tty_attrs(G.speeds[0]); 659 643 660 644 /* Write the modem init string and DON'T flush the buffers */ 661 if (option s.flags& F_INITSTRING) {645 if (option_mask32 & F_INITSTRING) { 662 646 debug("writing init string\n"); 663 full_write1_str( options.initstring);647 full_write1_str(G.initstring); 664 648 } 665 649 666 650 /* Optionally detect the baud rate from the modem status message */ 667 651 debug("before autobaud\n"); 668 if (option s.flags& F_PARSE)669 auto_baud( line_buf, sizeof(line_buf), &termios);652 if (option_mask32 & F_PARSE) 653 auto_baud(); 670 654 671 655 /* Set the optional timer */ 672 alarm(options.timeout); /* if 0, alarm is not set */ 656 signal(SIGALRM, alarm_handler); 657 alarm(G.timeout); /* if 0, alarm is not set */ 673 658 674 659 /* Optionally wait for CR or LF before writing /etc/issue */ 675 if (option s.flags& F_WAITCRLF) {660 if (option_mask32 & F_WAITCRLF) { 676 661 char ch; 677 678 662 debug("waiting for cr-lf\n"); 679 663 while (safe_read(STDIN_FILENO, &ch, 1) == 1) { 680 664 debug("read %x\n", (unsigned char)ch); 681 ch &= 0x7f; /* strip "parity bit" */682 665 if (ch == '\n' || ch == '\r') 683 666 break; … … 686 669 687 670 logname = NULL; 688 if (!(option s.flags& F_NOPROMPT)) {689 /* NB: termios_initalready set line speed690 * to options.speeds[0] */671 if (!(option_mask32 & F_NOPROMPT)) { 672 /* NB: init_tty_attrs already set line speed 673 * to G.speeds[0] */ 691 674 int baud_index = 0; 692 675 693 676 while (1) { 694 /* Read the login name .*/677 /* Read the login name */ 695 678 debug("reading login name\n"); 696 logname = get_logname(line_buf, sizeof(line_buf), 697 &options, &chardata); 679 logname = get_logname(); 698 680 if (logname) 699 681 break; 700 /* we are here only if options.numspeed > 1 */ 701 baud_index = (baud_index + 1) % options.numspeed; 702 cfsetispeed(&termios, options.speeds[baud_index]); 703 cfsetospeed(&termios, options.speeds[baud_index]); 704 tcsetattr_stdin_TCSANOW(&termios); 682 /* We are here only if G.numspeed > 1 */ 683 baud_index = (baud_index + 1) % G.numspeed; 684 cfsetspeed(&G.tty_attrs, G.speeds[baud_index]); 685 set_tty_attrs(); 705 686 } 706 687 } 707 688 708 /* Disable timer .*/689 /* Disable timer */ 709 690 alarm(0); 710 691 711 /* Finalize the termios settings. */ 712 termios_final(&options, &termios, &chardata); 713 714 /* Now the newline character should be properly written. */ 715 full_write(STDOUT_FILENO, "\n", 1); 716 717 /* Let the login program take care of password validation. */ 692 finalize_tty_attrs(); 693 694 /* Let the login program take care of password validation */ 718 695 /* We use PATH because we trust that root doesn't set "bad" PATH, 719 * and getty is not suid-root applet .*/696 * and getty is not suid-root applet */ 720 697 /* With -n, logname == NULL, and login will ask for username instead */ 721 BB_EXECLP( options.login, options.login, "--", logname, NULL);722 bb_error_msg_and_die("can't execute '%s'", options.login);723 } 698 BB_EXECLP(G.login, G.login, "--", logname, NULL); 699 bb_error_msg_and_die("can't execute '%s'", G.login); 700 }
Note:
See TracChangeset
for help on using the changeset viewer.