diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2011-10-22 06:27:41 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2011-10-22 06:27:41 +0200 |
commit | 7449e18190b8ed07a7cd1711b40885ae4b97efb4 (patch) | |
tree | c6913f5cab94f304f49731d7700d7afa30fd2a2a | |
parent | ef5a2d757a963fa50807c0abc8cb65be8657c8ee (diff) | |
download | busybox-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.htm | 1 | ||||
-rw-r--r-- | libbb/bb_askpass.c | 22 | ||||
-rw-r--r-- | loginutils/getty.c | 208 | ||||
-rw-r--r-- | loginutils/login.c | 45 |
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 | |||
279 | Again, if TOSTOP is set but the background process ignores or blocks | 279 | Again, if TOSTOP is set but the background process ignores or blocks |
280 | the SIGTTOU signal, or if its process group is orphaned (see below), | 280 | the SIGTTOU signal, or if its process group is orphaned (see below), |
281 | then the write() returns an EIO error, and no signal is sent. | 281 | then 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 | ||
85 | struct globals { | 85 | struct 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) | |||
201 | static void open_tty(void) | 200 | static 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 | ||
226 | static void set_termios(void) | 232 | static 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 | */ |
239 | static void termios_init(int speed) | 245 | static 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 | ||
311 | static void termios_final(void) | 317 | static 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 | ||
519 | static void alarm_handler(int sig UNUSED_PARAM) | ||
520 | { | ||
521 | finalize_tty_attrs(); | ||
522 | _exit(EXIT_SUCCESS); | ||
523 | } | ||
524 | |||
513 | int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 525 | int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
514 | int getty_main(int argc UNUSED_PARAM, char **argv) | 526 | int 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 | ||
44 | struct 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 |
45 | static void die_if_nologin(void) | 52 | static void die_if_nologin(void) |
46 | { | 53 | { |
@@ -206,15 +213,21 @@ static void motd(void) | |||
206 | 213 | ||
207 | static void alarm_handler(int sig UNUSED_PARAM) | 214 | static 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 */ |