aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2011-10-22 06:27:41 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2011-10-22 06:27:41 +0200
commit7449e18190b8ed07a7cd1711b40885ae4b97efb4 (patch)
treec6913f5cab94f304f49731d7700d7afa30fd2a2a
parentef5a2d757a963fa50807c0abc8cb65be8657c8ee (diff)
downloadbusybox-w32-7449e18190b8ed07a7cd1711b40885ae4b97efb4.tar.gz
busybox-w32-7449e18190b8ed07a7cd1711b40885ae4b97efb4.tar.bz2
busybox-w32-7449e18190b8ed07a7cd1711b40885ae4b97efb4.zip
getty,login: tighten up handling of ctty, pgrp, and tty attr restoring on timeout
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--docs/ctty.htm1
-rw-r--r--libbb/bb_askpass.c22
-rw-r--r--loginutils/getty.c208
-rw-r--r--loginutils/login.c45
4 files changed, 171 insertions, 105 deletions
diff --git a/docs/ctty.htm b/docs/ctty.htm
index e7d2877f8..3cb2dd2bd 100644
--- a/docs/ctty.htm
+++ b/docs/ctty.htm
@@ -279,6 +279,7 @@ and inspect it by
279Again, if TOSTOP is set but the background process ignores or blocks 279Again, if TOSTOP is set but the background process ignores or blocks
280the SIGTTOU signal, or if its process group is orphaned (see below), 280the SIGTTOU signal, or if its process group is orphaned (see below),
281then the write() returns an EIO error, and no signal is sent. 281then the write() returns an EIO error, and no signal is sent.
282[vda: correction. SUS says that if SIGTTOU is blocked/ignored, write succeeds. ]
282<p> 283<p>
283</p><h3>Orphaned process groups</h3> 284</p><h3>Orphaned process groups</h3>
284 285
diff --git a/libbb/bb_askpass.c b/libbb/bb_askpass.c
index 9a4188f52..fe2b50677 100644
--- a/libbb/bb_askpass.c
+++ b/libbb/bb_askpass.c
@@ -30,14 +30,23 @@ char* FAST_FUNC bb_ask(const int fd, int timeout, const char *prompt)
30 struct sigaction sa, oldsa; 30 struct sigaction sa, oldsa;
31 struct termios tio, oldtio; 31 struct termios tio, oldtio;
32 32
33 tcgetattr(fd, &oldtio); 33 fputs(prompt, stdout);
34 fflush_all();
34 tcflush(fd, TCIFLUSH); 35 tcflush(fd, TCIFLUSH);
36
37 tcgetattr(fd, &oldtio);
35 tio = oldtio; 38 tio = oldtio;
36#ifndef IUCLC 39#if 0
37# define IUCLC 0 40 /* Switch off UPPERCASE->lowercase conversion (never used since 198x)
38#endif 41 * and XON/XOFF (why we want to mess with this??)
42 */
43# ifndef IUCLC
44# define IUCLC 0
45# endif
39 tio.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY); 46 tio.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
40 tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP); 47#endif
48 /* Switch off echo */
49 tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL);
41 tcsetattr(fd, TCSANOW, &tio); 50 tcsetattr(fd, TCSANOW, &tio);
42 51
43 memset(&sa, 0, sizeof(sa)); 52 memset(&sa, 0, sizeof(sa));
@@ -50,9 +59,6 @@ char* FAST_FUNC bb_ask(const int fd, int timeout, const char *prompt)
50 alarm(timeout); 59 alarm(timeout);
51 } 60 }
52 61
53 fputs(prompt, stdout);
54 fflush_all();
55
56 if (!passwd) 62 if (!passwd)
57 passwd = xmalloc(sizeof_passwd); 63 passwd = xmalloc(sizeof_passwd);
58 ret = passwd; 64 ret = passwd;
diff --git a/loginutils/getty.c b/loginutils/getty.c
index baad04e9b..168ae4de1 100644
--- a/loginutils/getty.c
+++ b/loginutils/getty.c
@@ -83,16 +83,16 @@ static FILE *dbf;
83#define MAX_SPEED 10 /* max. nr. of baud rates */ 83#define MAX_SPEED 10 /* max. nr. of baud rates */
84 84
85struct globals { 85struct globals {
86 unsigned timeout; /* time-out period */ 86 unsigned timeout;
87 const char *login; /* login program */ 87 const char *login; /* login program */
88 const char *fakehost; 88 const char *fakehost;
89 const char *tty; /* name of tty */ 89 const char *tty_name;
90 char *initstring; /* modem init string */ 90 char *initstring; /* modem init string */
91 const char *issue; /* alternative issue file */ 91 const char *issue; /* alternative issue file */
92 int numspeed; /* number of baud rates to try */ 92 int numspeed; /* number of baud rates to try */
93 int speeds[MAX_SPEED]; /* baud rates to be tried */ 93 int speeds[MAX_SPEED]; /* baud rates to be tried */
94 unsigned char eol; /* end-of-line char seen (CR or NL) */ 94 unsigned char eol; /* end-of-line char seen (CR or NL) */
95 struct termios termios; /* terminal mode bits */ 95 struct termios tty_attrs;
96 char line_buf[128]; 96 char line_buf[128];
97}; 97};
98 98
@@ -181,15 +181,14 @@ static void parse_args(char **argv)
181 debug("after getopt\n"); 181 debug("after getopt\n");
182 182
183 /* We loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ 183 /* We loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
184 G.tty = argv[0]; /* tty name */ 184 G.tty_name = argv[0];
185 ts = argv[1]; /* baud rate(s) */ 185 ts = argv[1]; /* baud rate(s) */
186 if (isdigit(argv[0][0])) { 186 if (isdigit(argv[0][0])) {
187 /* A number first, assume it's a speed (BSD style) */ 187 /* A number first, assume it's a speed (BSD style) */
188 G.tty = ts; /* tty name is in argv[1] */ 188 G.tty_name = ts; /* tty name is in argv[1] */
189 ts = argv[0]; /* baud rate(s) */ 189 ts = argv[0]; /* baud rate(s) */
190 } 190 }
191 parse_speeds(ts); 191 parse_speeds(ts);
192 applet_name = xasprintf("getty: %s", G.tty);
193 192
194 if (argv[2]) 193 if (argv[2])
195 xsetenv("TERM", argv[2]); 194 xsetenv("TERM", argv[2]);
@@ -201,42 +200,49 @@ static void parse_args(char **argv)
201static void open_tty(void) 200static void open_tty(void)
202{ 201{
203 /* Set up new standard input, unless we are given an already opened port */ 202 /* Set up new standard input, unless we are given an already opened port */
204 if (NOT_LONE_DASH(G.tty)) { 203 if (NOT_LONE_DASH(G.tty_name)) {
205 if (G.tty[0] != '/') 204 if (G.tty_name[0] != '/')
206 G.tty = xasprintf("/dev/%s", G.tty); /* will leak it */ 205 G.tty_name = xasprintf("/dev/%s", G.tty_name); /* will leak it */
207 206
208 /* Open the tty as standard input */ 207 /* Open the tty as standard input */
209 debug("open(2)\n"); 208 debug("open(2)\n");
210 close(0); 209 close(0);
211 xopen(G.tty, O_RDWR | O_NONBLOCK); /* uses fd 0 */ 210 xopen(G.tty_name, O_RDWR | O_NONBLOCK); /* uses fd 0 */
212 211
213 /* Set proper protections and ownership */ 212 /* Set proper protections and ownership */
214 fchown(0, 0, 0); /* 0:0 */ 213 fchown(0, 0, 0); /* 0:0 */
215 fchmod(0, 0620); /* crw--w---- */ 214 fchmod(0, 0620); /* crw--w---- */
216 } else { 215 } else {
216 char *n;
217 /* 217 /*
218 * Standard input should already be connected to an open port. Make 218 * Standard input should already be connected to an open port.
219 * sure it is open for read/write. 219 * Make sure it is open for read/write.
220 */ 220 */
221 if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR) 221 if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR)
222 bb_error_msg_and_die("stdin is not open for read/write"); 222 bb_error_msg_and_die("stdin is not open for read/write");
223
224 /* Try to get real tty name instead of "-" */
225 n = xmalloc_ttyname(0);
226 if (n)
227 G.tty_name = n;
223 } 228 }
229 applet_name = xasprintf("getty: %s", skip_dev_pfx(G.tty_name));
224} 230}
225 231
226static void set_termios(void) 232static void set_tty_attrs(void)
227{ 233{
228 if (tcsetattr_stdin_TCSANOW(&G.termios) < 0) 234 if (tcsetattr_stdin_TCSANOW(&G.tty_attrs) < 0)
229 bb_perror_msg_and_die("tcsetattr"); 235 bb_perror_msg_and_die("tcsetattr");
230} 236}
231 237
232/* We manipulate termios this way: 238/* We manipulate tty_attrs this way:
233 * - first, we read existing termios settings 239 * - first, we read existing tty_attrs
234 * - termios_init modifies some parts and sets it 240 * - init_tty_attrs modifies some parts and sets it
235 * - auto_baud and/or BREAK processing can set different speed and set termios 241 * - auto_baud and/or BREAK processing can set different speed and set tty attrs
236 * - termios_final again modifies some parts and sets termios before 242 * - finalize_tty_attrs again modifies some parts and sets tty attrs before
237 * execing login 243 * execing login
238 */ 244 */
239static void termios_init(int speed) 245static void init_tty_attrs(int speed)
240{ 246{
241 /* Try to drain output buffer, with 5 sec timeout. 247 /* Try to drain output buffer, with 5 sec timeout.
242 * Added on request from users of ~600 baud serial interface 248 * Added on request from users of ~600 baud serial interface
@@ -255,14 +261,14 @@ static void termios_init(int speed)
255 261
256 /* Set speed if it wasn't specified as "0" on command line */ 262 /* Set speed if it wasn't specified as "0" on command line */
257 if (speed != B0) 263 if (speed != B0)
258 cfsetspeed(&G.termios, speed); 264 cfsetspeed(&G.tty_attrs, speed);
259 265
260 /* Initial termios settings: 8-bit characters, raw mode, blocking i/o. 266 /* Initial settings: 8-bit characters, raw mode, blocking i/o.
261 * Special characters are set after we have read the login name; all 267 * Special characters are set after we have read the login name; all
262 * reads will be done in raw mode anyway. 268 * reads will be done in raw mode anyway.
263 */ 269 */
264 /* Clear all bits except: */ 270 /* Clear all bits except: */
265 G.termios.c_cflag &= (0 271 G.tty_attrs.c_cflag &= (0
266 /* 2 stop bits (1 otherwise) 272 /* 2 stop bits (1 otherwise)
267 * Enable parity bit (both on input and output) 273 * Enable parity bit (both on input and output)
268 * Odd parity (else even) 274 * Odd parity (else even)
@@ -280,42 +286,42 @@ static void termios_init(int speed)
280#endif 286#endif
281 ); 287 );
282 /* Set: 8 bits; hang up (drop DTR) on last close; enable receive */ 288 /* Set: 8 bits; hang up (drop DTR) on last close; enable receive */
283 G.termios.c_cflag |= CS8 | HUPCL | CREAD; 289 G.tty_attrs.c_cflag |= CS8 | HUPCL | CREAD;
284 if (option_mask32 & F_LOCAL) { 290 if (option_mask32 & F_LOCAL) {
285 /* ignore Carrier Detect pin: 291 /* ignore Carrier Detect pin:
286 * opens don't block when CD is low, 292 * opens don't block when CD is low,
287 * losing CD doesn't hang up processes whose ctty is this tty 293 * losing CD doesn't hang up processes whose ctty is this tty
288 */ 294 */
289 G.termios.c_cflag |= CLOCAL; 295 G.tty_attrs.c_cflag |= CLOCAL;
290 } 296 }
291#ifdef CRTSCTS 297#ifdef CRTSCTS
292 if (option_mask32 & F_RTSCTS) 298 if (option_mask32 & F_RTSCTS)
293 G.termios.c_cflag |= CRTSCTS; /* flow control using RTS/CTS pins */ 299 G.tty_attrs.c_cflag |= CRTSCTS; /* flow control using RTS/CTS pins */
294#endif 300#endif
295 G.termios.c_iflag = 0; 301 G.tty_attrs.c_iflag = 0;
296 G.termios.c_lflag = 0; 302 G.tty_attrs.c_lflag = 0;
297 /* non-raw output; add CR to each NL */ 303 /* non-raw output; add CR to each NL */
298 G.termios.c_oflag = OPOST | ONLCR; 304 G.tty_attrs.c_oflag = OPOST | ONLCR;
299 305
300 G.termios.c_cc[VMIN] = 1; /* block reads if < 1 char is available */ 306 G.tty_attrs.c_cc[VMIN] = 1; /* block reads if < 1 char is available */
301 G.termios.c_cc[VTIME] = 0; /* no timeout (reads block forever) */ 307 G.tty_attrs.c_cc[VTIME] = 0; /* no timeout (reads block forever) */
302#ifdef __linux__ 308#ifdef __linux__
303 G.termios.c_line = 0; 309 G.tty_attrs.c_line = 0;
304#endif 310#endif
305 311
306 set_termios(); 312 set_tty_attrs();
307 313
308 debug("term_io 2\n"); 314 debug("term_io 2\n");
309} 315}
310 316
311static void termios_final(void) 317static void finalize_tty_attrs(void)
312{ 318{
313 /* software flow control on output (stop sending if XOFF is recvd); 319 /* software flow control on output (stop sending if XOFF is recvd);
314 * and on input (send XOFF when buffer is full) 320 * and on input (send XOFF when buffer is full)
315 */ 321 */
316 G.termios.c_iflag |= IXON | IXOFF; 322 G.tty_attrs.c_iflag |= IXON | IXOFF;
317 if (G.eol == '\r') { 323 if (G.eol == '\r') {
318 G.termios.c_iflag |= ICRNL; /* map CR on input to NL */ 324 G.tty_attrs.c_iflag |= ICRNL; /* map CR on input to NL */
319 } 325 }
320 /* Other bits in c_iflag: 326 /* Other bits in c_iflag:
321 * IXANY Any recvd char enables output (any char is also a XON) 327 * IXANY Any recvd char enables output (any char is also a XON)
@@ -342,7 +348,7 @@ static void termios_final(void)
342 * echo kill char specially, not as ^c (ECHOKE controls how exactly); 348 * echo kill char specially, not as ^c (ECHOKE controls how exactly);
343 * erase all input via BS-SP-BS on kill char (else go to next line) 349 * erase all input via BS-SP-BS on kill char (else go to next line)
344 */ 350 */
345 G.termios.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; 351 G.tty_attrs.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
346 /* Other bits in c_lflag: 352 /* Other bits in c_lflag:
347 * XCASE Map uppercase to \lowercase [tried, doesn't work] 353 * XCASE Map uppercase to \lowercase [tried, doesn't work]
348 * ECHONL Echo NL even if ECHO is not set 354 * ECHONL Echo NL even if ECHO is not set
@@ -360,17 +366,17 @@ static void termios_final(void)
360 * (why "stty sane" unsets this bit?) 366 * (why "stty sane" unsets this bit?)
361 */ 367 */
362 368
363 G.termios.c_cc[VINTR] = DEF_INTR; 369 G.tty_attrs.c_cc[VINTR] = DEF_INTR;
364 G.termios.c_cc[VQUIT] = DEF_QUIT; 370 G.tty_attrs.c_cc[VQUIT] = DEF_QUIT;
365 G.termios.c_cc[VEOF] = DEF_EOF; 371 G.tty_attrs.c_cc[VEOF] = DEF_EOF;
366 G.termios.c_cc[VEOL] = DEF_EOL; 372 G.tty_attrs.c_cc[VEOL] = DEF_EOL;
367#ifdef VSWTC 373#ifdef VSWTC
368 G.termios.c_cc[VSWTC] = DEF_SWITCH; 374 G.tty_attrs.c_cc[VSWTC] = DEF_SWITCH;
369#endif 375#endif
370#ifdef VSWTCH 376#ifdef VSWTCH
371 G.termios.c_cc[VSWTCH] = DEF_SWITCH; 377 G.tty_attrs.c_cc[VSWTCH] = DEF_SWITCH;
372#endif 378#endif
373 G.termios.c_cc[VKILL] = DEF_KILL; 379 G.tty_attrs.c_cc[VKILL] = DEF_KILL;
374 /* Other control chars: 380 /* Other control chars:
375 * VEOL2 381 * VEOL2
376 * VERASE, VWERASE - (word) erase. we may set VERASE in get_logname 382 * VERASE, VWERASE - (word) erase. we may set VERASE in get_logname
@@ -380,7 +386,7 @@ static void termios_final(void)
380 * VSTART, VSTOP - chars used for IXON/IXOFF 386 * VSTART, VSTOP - chars used for IXON/IXOFF
381 */ 387 */
382 388
383 set_termios(); 389 set_tty_attrs();
384} 390}
385 391
386/* extract baud rate from modem status message */ 392/* extract baud rate from modem status message */
@@ -403,8 +409,8 @@ static void auto_baud(void)
403 * modem status messages is enabled. 409 * modem status messages is enabled.
404 */ 410 */
405 411
406 G.termios.c_cc[VMIN] = 0; /* don't block reads (min read is 0 chars) */ 412 G.tty_attrs.c_cc[VMIN] = 0; /* don't block reads (min read is 0 chars) */
407 set_termios(); 413 set_tty_attrs();
408 414
409 /* 415 /*
410 * Wait for a while, then read everything the modem has said so far and 416 * Wait for a while, then read everything the modem has said so far and
@@ -420,15 +426,15 @@ static void auto_baud(void)
420 if (isdigit(*bp)) { 426 if (isdigit(*bp)) {
421 speed = bcode(bp); 427 speed = bcode(bp);
422 if (speed > 0) 428 if (speed > 0)
423 cfsetspeed(&G.termios, speed); 429 cfsetspeed(&G.tty_attrs, speed);
424 break; 430 break;
425 } 431 }
426 } 432 }
427 } 433 }
428 434
429 /* Restore terminal settings */ 435 /* Restore terminal settings */
430 G.termios.c_cc[VMIN] = 1; /* restore to value set by termios_init */ 436 G.tty_attrs.c_cc[VMIN] = 1; /* restore to value set by init_tty_attrs */
431 set_termios(); 437 set_tty_attrs();
432} 438}
433 439
434/* get user name, establish parity, speed, erase, kill, eol; 440/* get user name, establish parity, speed, erase, kill, eol;
@@ -449,7 +455,7 @@ static char *get_logname(void)
449 /* Write issue file and prompt */ 455 /* Write issue file and prompt */
450#ifdef ISSUE 456#ifdef ISSUE
451 if (!(option_mask32 & F_NOISSUE)) 457 if (!(option_mask32 & F_NOISSUE))
452 print_login_issue(G.issue, G.tty); 458 print_login_issue(G.issue, G.tty_name);
453#endif 459#endif
454 print_login_prompt(); 460 print_login_prompt();
455 461
@@ -479,7 +485,7 @@ static char *get_logname(void)
479 goto got_logname; 485 goto got_logname;
480 case BS: 486 case BS:
481 case DEL: 487 case DEL:
482 G.termios.c_cc[VERASE] = c; 488 G.tty_attrs.c_cc[VERASE] = c;
483 if (bp > G.line_buf) { 489 if (bp > G.line_buf) {
484 full_write(STDOUT_FILENO, "\010 \010", 3); 490 full_write(STDOUT_FILENO, "\010 \010", 3);
485 bp--; 491 bp--;
@@ -510,11 +516,17 @@ static char *get_logname(void)
510 return G.line_buf; 516 return G.line_buf;
511} 517}
512 518
519static void alarm_handler(int sig UNUSED_PARAM)
520{
521 finalize_tty_attrs();
522 _exit(EXIT_SUCCESS);
523}
524
513int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 525int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
514int getty_main(int argc UNUSED_PARAM, char **argv) 526int getty_main(int argc UNUSED_PARAM, char **argv)
515{ 527{
516 int n; 528 int n;
517 pid_t pid; 529 pid_t pid, tsid;
518 char *logname; 530 char *logname;
519 531
520 INIT_G(); 532 INIT_G();
@@ -527,14 +539,35 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
527 /* Parse command-line arguments */ 539 /* Parse command-line arguments */
528 parse_args(argv); 540 parse_args(argv);
529 541
530 logmode = LOGMODE_NONE; 542 /* Create new session and pgrp, lose controlling tty */
543 pid = setsid(); /* this also gives us our pid :) */
544 if (pid < 0) {
545 int fd;
546 /* :(
547 * docs/ctty.htm says:
548 * "This is allowed only when the current process
549 * is not a process group leader".
550 * Thus, setsid() will fail if we _already_ are
551 * a session leader - which is quite possible for getty!
552 */
553 pid = getpid();
554 if (getsid(0) != pid)
555 bb_perror_msg_and_die("setsid");
556 /* Looks like we are already a session leader.
557 * In this case (setsid failed) we may still have ctty,
558 * and it may be different from tty we need to control!
559 * If we still have ctty, on Linux ioctl(TIOCSCTTY)
560 * (which we are going to call a bit later) always fails.
561 * Try to drop ctty now to prevent that.
562 */
563 fd = open("/dev/tty", O_RDWR);
564 if (fd >= 0) {
565 ioctl(fd, TIOCNOTTY);
566 close(fd);
567 }
568 }
531 569
532 /* Create new session, lose controlling tty, if any */ 570 /* Close stdio, and stray descriptors, just in case */
533 /* docs/ctty.htm says:
534 * "This is allowed only when the current process
535 * is not a process group leader" - is this a problem? */
536 setsid();
537 /* close stdio, and stray descriptors, just in case */
538 n = xopen(bb_dev_null, O_RDWR); 571 n = xopen(bb_dev_null, O_RDWR);
539 /* dup2(n, 0); - no, we need to handle "getty - 9600" too */ 572 /* dup2(n, 0); - no, we need to handle "getty - 9600" too */
540 xdup2(n, 1); 573 xdup2(n, 1);
@@ -558,13 +591,25 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
558#endif 591#endif
559 592
560 /* Open the tty as standard input, if it is not "-" */ 593 /* Open the tty as standard input, if it is not "-" */
561 /* If it's not "-" and not taken yet, it will become our ctty */
562 debug("calling open_tty\n"); 594 debug("calling open_tty\n");
563 open_tty(); 595 open_tty();
564 ndelay_off(0); 596 ndelay_off(STDIN_FILENO);
565 debug("duping\n"); 597 debug("duping\n");
566 xdup2(0, 1); 598 xdup2(STDIN_FILENO, 1);
567 xdup2(0, 2); 599 xdup2(STDIN_FILENO, 2);
600
601 /* Steal ctty if we don't have it yet */
602 tsid = tcgetsid(STDIN_FILENO);
603 if (tsid < 0 || pid != tsid) {
604 if (ioctl(STDIN_FILENO, TIOCSCTTY, /*force:*/ (long)1) < 0)
605 bb_perror_msg_and_die("TIOCSCTTY");
606 }
607
608#ifdef __linux__
609 /* Make ourself a foreground process group within our session */
610 if (tcsetpgrp(STDIN_FILENO, pid) < 0)
611 bb_perror_msg_and_die("tcsetpgrp");
612#endif
568 613
569 /* 614 /*
570 * The following ioctl will fail if stdin is not a tty, but also when 615 * The following ioctl will fail if stdin is not a tty, but also when
@@ -574,25 +619,15 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
574 * by patching the SunOS kernel variable "zsadtrlow" to a larger value; 619 * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
575 * 5 seconds seems to be a good value. 620 * 5 seconds seems to be a good value.
576 */ 621 */
577 if (tcgetattr(STDIN_FILENO, &G.termios) < 0) 622 if (tcgetattr(STDIN_FILENO, &G.tty_attrs) < 0)
578 bb_perror_msg_and_die("tcgetattr"); 623 bb_perror_msg_and_die("tcgetattr");
579 624
580 pid = getpid();
581#ifdef __linux__
582// FIXME: do we need this? Otherwise "-" case seems to be broken...
583 // /* Forcibly make fd 0 our controlling tty, even if another session
584 // * has it as a ctty. (Another session loses ctty). */
585 // ioctl(STDIN_FILENO, TIOCSCTTY, (void*)1);
586 /* Make ourself a foreground process group within our session */
587 tcsetpgrp(STDIN_FILENO, pid);
588#endif
589
590 /* Update the utmp file. This tty is ours now! */ 625 /* Update the utmp file. This tty is ours now! */
591 update_utmp(pid, LOGIN_PROCESS, G.tty, "LOGIN", G.fakehost); 626 update_utmp(pid, LOGIN_PROCESS, G.tty_name, "LOGIN", G.fakehost);
592 627
593 /* Initialize the termios settings (raw mode, eight-bit, blocking i/o) */ 628 /* Initialize tty attrs (raw mode, eight-bit, blocking i/o) */
594 debug("calling termios_init\n"); 629 debug("calling init_tty_attrs\n");
595 termios_init(G.speeds[0]); 630 init_tty_attrs(G.speeds[0]);
596 631
597 /* Write the modem init string and DON'T flush the buffers */ 632 /* Write the modem init string and DON'T flush the buffers */
598 if (option_mask32 & F_INITSTRING) { 633 if (option_mask32 & F_INITSTRING) {
@@ -606,8 +641,8 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
606 auto_baud(); 641 auto_baud();
607 642
608 /* Set the optional timer */ 643 /* Set the optional timer */
644 signal(SIGALRM, alarm_handler);
609 alarm(G.timeout); /* if 0, alarm is not set */ 645 alarm(G.timeout); /* if 0, alarm is not set */
610//BUG: death by signal won't restore termios
611 646
612 /* Optionally wait for CR or LF before writing /etc/issue */ 647 /* Optionally wait for CR or LF before writing /etc/issue */
613 if (option_mask32 & F_WAITCRLF) { 648 if (option_mask32 & F_WAITCRLF) {
@@ -622,7 +657,7 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
622 657
623 logname = NULL; 658 logname = NULL;
624 if (!(option_mask32 & F_NOPROMPT)) { 659 if (!(option_mask32 & F_NOPROMPT)) {
625 /* NB: termios_init already set line speed 660 /* NB: init_tty_attrs already set line speed
626 * to G.speeds[0] */ 661 * to G.speeds[0] */
627 int baud_index = 0; 662 int baud_index = 0;
628 663
@@ -634,16 +669,15 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
634 break; 669 break;
635 /* We are here only if G.numspeed > 1 */ 670 /* We are here only if G.numspeed > 1 */
636 baud_index = (baud_index + 1) % G.numspeed; 671 baud_index = (baud_index + 1) % G.numspeed;
637 cfsetspeed(&G.termios, G.speeds[baud_index]); 672 cfsetspeed(&G.tty_attrs, G.speeds[baud_index]);
638 set_termios(); 673 set_tty_attrs();
639 } 674 }
640 } 675 }
641 676
642 /* Disable timer */ 677 /* Disable timer */
643 alarm(0); 678 alarm(0);
644 679
645 /* Finalize the termios settings */ 680 finalize_tty_attrs();
646 termios_final();
647 681
648 /* Now the newline character should be properly written */ 682 /* Now the newline character should be properly written */
649 full_write(STDOUT_FILENO, "\n", 1); 683 full_write(STDOUT_FILENO, "\n", 1);
diff --git a/loginutils/login.c b/loginutils/login.c
index 534343129..b54beef6e 100644
--- a/loginutils/login.c
+++ b/loginutils/login.c
@@ -41,6 +41,13 @@ enum {
41 TTYNAME_SIZE = 32, 41 TTYNAME_SIZE = 32,
42}; 42};
43 43
44struct globals {
45 struct termios tty_attrs;
46} FIX_ALIASING;
47#define G (*(struct globals*)&bb_common_bufsiz1)
48#define INIT_G() do { } while (0)
49
50
44#if ENABLE_FEATURE_NOLOGIN 51#if ENABLE_FEATURE_NOLOGIN
45static void die_if_nologin(void) 52static void die_if_nologin(void)
46{ 53{
@@ -206,15 +213,21 @@ static void motd(void)
206 213
207static void alarm_handler(int sig UNUSED_PARAM) 214static void alarm_handler(int sig UNUSED_PARAM)
208{ 215{
209 /* This is the escape hatch! Poor serial line users and the like 216 /* This is the escape hatch! Poor serial line users and the like
210 * arrive here when their connection is broken. 217 * arrive here when their connection is broken.
211 * We don't want to block here */ 218 * We don't want to block here */
212 ndelay_on(1); 219 ndelay_on(STDOUT_FILENO);
213 printf("\r\nLogin timed out after %d seconds\r\n", TIMEOUT); 220 /* Test for correct attr restoring:
221 * run "getty 0 -" from a shell, enter bogus username, stop at
222 * password prompt, let it time out. Without the tcsetattr below,
223 * when you are back at shell prompt, echo will be still off.
224 */
225 tcsetattr_stdin_TCSANOW(&G.tty_attrs);
226 printf("\r\nLogin timed out after %u seconds\r\n", TIMEOUT);
214 fflush_all(); 227 fflush_all();
215 /* unix API is brain damaged regarding O_NONBLOCK, 228 /* unix API is brain damaged regarding O_NONBLOCK,
216 * we should undo it, or else we can affect other processes */ 229 * we should undo it, or else we can affect other processes */
217 ndelay_off(1); 230 ndelay_off(STDOUT_FILENO);
218 _exit(EXIT_SUCCESS); 231 _exit(EXIT_SUCCESS);
219} 232}
220 233
@@ -250,9 +263,7 @@ int login_main(int argc UNUSED_PARAM, char **argv)
250 pid_t child_pid; 263 pid_t child_pid;
251#endif 264#endif
252 265
253 username[0] = '\0'; 266 INIT_G();
254 signal(SIGALRM, alarm_handler);
255 alarm(TIMEOUT);
256 267
257 /* More of suid paranoia if called by non-root: */ 268 /* More of suid paranoia if called by non-root: */
258 /* Clear dangerous stuff, set PATH */ 269 /* Clear dangerous stuff, set PATH */
@@ -264,6 +275,7 @@ int login_main(int argc UNUSED_PARAM, char **argv)
264 * (The name of the function is misleading. Not daemonizing here.) */ 275 * (The name of the function is misleading. Not daemonizing here.) */
265 bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE | DAEMON_CLOSE_EXTRA_FDS, NULL); 276 bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE | DAEMON_CLOSE_EXTRA_FDS, NULL);
266 277
278 username[0] = '\0';
267 opt = getopt32(argv, "f:h:p", &opt_user, &opt_host); 279 opt = getopt32(argv, "f:h:p", &opt_user, &opt_host);
268 if (opt & LOGIN_OPT_f) { 280 if (opt & LOGIN_OPT_f) {
269 if (!run_by_root) 281 if (!run_by_root)
@@ -274,9 +286,19 @@ int login_main(int argc UNUSED_PARAM, char **argv)
274 if (argv[0]) /* user from command line (getty) */ 286 if (argv[0]) /* user from command line (getty) */
275 safe_strncpy(username, argv[0], sizeof(username)); 287 safe_strncpy(username, argv[0], sizeof(username));
276 288
277 /* Let's find out and memorize our tty */ 289 /* Save tty attributes - and by doing it, check that it's indeed a tty */
278 if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO)) 290 if (tcgetattr(STDIN_FILENO, &G.tty_attrs) < 0
291 || !isatty(STDOUT_FILENO)
292 /*|| !isatty(STDERR_FILENO) - no, guess some people might want to redirect this */
293 ) {
279 return EXIT_FAILURE; /* Must be a terminal */ 294 return EXIT_FAILURE; /* Must be a terminal */
295 }
296
297 /* We install timeout handler only _after_ we saved G.tty_attrs */
298 signal(SIGALRM, alarm_handler);
299 alarm(TIMEOUT);
300
301 /* Find out and memorize our tty name */
280 full_tty = xmalloc_ttyname(STDIN_FILENO); 302 full_tty = xmalloc_ttyname(STDIN_FILENO);
281 if (!full_tty) 303 if (!full_tty)
282 full_tty = xstrdup("UNKNOWN"); 304 full_tty = xstrdup("UNKNOWN");
@@ -391,7 +413,10 @@ int login_main(int argc UNUSED_PARAM, char **argv)
391 if (!pw->pw_passwd[0]) 413 if (!pw->pw_passwd[0])
392 break; 414 break;
393 fake_it: 415 fake_it:
394 /* authorization takes place here */ 416 /* Password reading and authorization takes place here.
417 * Note that reads (in no-echo mode) trash tty attributes.
418 * If we get interrupted by SIGALRM, we need to restore attrs.
419 */
395 if (correct_password(pw)) 420 if (correct_password(pw))
396 break; 421 break;
397#endif /* ENABLE_PAM */ 422#endif /* ENABLE_PAM */