Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/loginutils/getty.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 532 by niro, Sat Sep 1 22:45:15 2007 UTC revision 816 by niro, Fri Apr 24 18:33:46 2009 UTC
# Line 6  Line 6 
6   *   *
7   * option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95   * option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95
8   *   *
9   * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>   * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
10   * - added Native Language Support   * - added Native Language Support
11     *
12   * 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>   * 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>
13   * - enable hardware flow control before displaying /etc/issue   * - enable hardware flow control before displaying /etc/issue
14   *   *
15   * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.   * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  *  
16   */   */
17    
18  #include "busybox.h"  #include "libbb.h"
19  #include <syslog.h>  #include <syslog.h>
20    
21  #if ENABLE_FEATURE_UTMP  #if ENABLE_FEATURE_UTMP
22  #include <utmp.h>  #include <utmp.h> /* updwtmp() */
23  #endif  #endif
24    
25  /*  /*
# Line 28  Line 27 
27   * System V, assume it is SunOS 4.   * System V, assume it is SunOS 4.
28   */   */
29  #ifdef LOGIN_PROCESS                    /* defined in System V utmp.h */  #ifdef LOGIN_PROCESS                    /* defined in System V utmp.h */
 #define SYSV_STYLE                      /* select System V style getty */  
30  #include <sys/utsname.h>  #include <sys/utsname.h>
31  #include <time.h>  #include <time.h>
32  #if ENABLE_FEATURE_WTMP  #else /* if !sysV style, wtmp/utmp code is off */
33  extern void updwtmp(const char *filename, const struct utmp *ut);  #undef ENABLE_FEATURE_UTMP
34  static void update_utmp(char *line);  #undef ENABLE_FEATURE_WTMP
35  #endif  #define ENABLE_FEATURE_UTMP 0
36    #define ENABLE_FEATURE_WTMP 0
37  #endif  /* LOGIN_PROCESS */  #endif  /* LOGIN_PROCESS */
38    
39  /*  /*
# Line 47  static void update_utmp(char *line); Line 46  static void update_utmp(char *line);
46    
47  /* I doubt there are systems which still need this */  /* I doubt there are systems which still need this */
48  #undef HANDLE_ALLCAPS  #undef HANDLE_ALLCAPS
49    #undef ANCIENT_BS_KILL_CHARS
50    
51  #define _PATH_LOGIN "/bin/login"  #define _PATH_LOGIN "/bin/login"
52    
# Line 57  static void update_utmp(char *line); Line 57  static void update_utmp(char *line);
57  #define ISSUE "/etc/issue"              /* displayed before the login prompt */  #define ISSUE "/etc/issue"              /* displayed before the login prompt */
58    
59  /* Some shorthands for control characters. */  /* Some shorthands for control characters. */
60  #define CTL(x)          (x ^ 0100)      /* Assumes ASCII dialect */  #define CTL(x)          ((x) ^ 0100)    /* Assumes ASCII dialect */
61  #define CR              CTL('M')        /* carriage return */  #define CR              CTL('M')        /* carriage return */
62  #define NL              CTL('J')        /* line feed */  #define NL              CTL('J')        /* line feed */
63  #define BS              CTL('H')        /* back space */  #define BS              CTL('H')        /* back space */
# Line 76  static void update_utmp(char *line); Line 76  static void update_utmp(char *line);
76   * When multiple baud rates are specified on the command line, the first one   * When multiple baud rates are specified on the command line, the first one
77   * we will try is the first one specified.   * we will try is the first one specified.
78   */   */
 #define FIRST_SPEED     0  
   
 /* Storage for command-line options. */  
   
79  #define MAX_SPEED       10              /* max. nr. of baud rates */  #define MAX_SPEED       10              /* max. nr. of baud rates */
80    
81    /* Storage for command-line options. */
82  struct options {  struct options {
83   int flags;                      /* toggle switches, see below */   int flags;                      /* toggle switches, see below */
84   unsigned timeout;               /* time-out period */   unsigned timeout;               /* time-out period */
85   char *login;                    /* login program */   const char *login;              /* login program */
86   char *tty;                      /* name of tty */   const char *tty;                /* name of tty */
87   char *initstring;               /* modem init string */   const char *initstring;         /* modem init string */
88   char *issue;                    /* alternative issue file */   const char *issue;              /* alternative issue file */
89   int numspeed;                   /* number of baud rates to try */   int numspeed;                   /* number of baud rates to try */
90   int speeds[MAX_SPEED];          /* baud rates to be tried */   int speeds[MAX_SPEED];          /* baud rates to be tried */
91  };  };
92    
 static const char opt_string[] = "I:LH:f:hil:mt:wn";  
 #define F_INITSTRING    (1<<0)          /* initstring is set */  
 #define F_LOCAL         (1<<1)          /* force local */  
 #define F_FAKEHOST      (1<<2)          /* force fakehost */  
 #define F_CUSTISSUE     (1<<3)          /* give alternative issue file */  
 #define F_RTSCTS        (1<<4)          /* enable RTS/CTS flow control */  
 #define F_ISSUE         (1<<5)          /* display /etc/issue */  
 #define F_LOGIN         (1<<6)          /* non-default login program */  
 #define F_PARSE         (1<<7)          /* process modem status messages */  
 #define F_TIMEOUT       (1<<8)          /* time out */  
 #define F_WAITCRLF      (1<<9)          /* wait for CR or LF */  
 #define F_NOPROMPT      (1<<10)         /* don't ask for login name! */  
   
93  /* Storage for things detected while the login name was read. */  /* Storage for things detected while the login name was read. */
94  struct chardata {  struct chardata {
95   unsigned char erase;    /* erase character */   unsigned char erase;    /* erase character */
96   unsigned char kill;     /* kill character */   unsigned char kill;     /* kill character */
97   unsigned char eol;      /* end-of-line character */   unsigned char eol;      /* end-of-line character */
98   unsigned char parity;   /* what parity did we see */   unsigned char parity;   /* what parity did we see */
99     /* (parity & 1): saw odd parity char with 7th bit set */
100     /* (parity & 2): saw even parity char with 7th bit set */
101     /* parity == 0: probably 7-bit, space parity? */
102     /* parity == 1: probably 7-bit, odd parity? */
103     /* parity == 2: probably 7-bit, even parity? */
104     /* parity == 3: definitely 8 bit, no parity! */
105     /* Hmm... with any value of "parity" 8 bit, no parity is possible */
106  #ifdef HANDLE_ALLCAPS  #ifdef HANDLE_ALLCAPS
107   unsigned char capslock; /* upper case without lower case */   unsigned char capslock; /* upper case without lower case */
108  #endif  #endif
109  };  };
110    
111    
112  /* Initial values for the above. */  /* Initial values for the above. */
113  static const struct chardata init_chardata = {  static const struct chardata init_chardata = {
114   DEF_ERASE,                              /* default erase character */   DEF_ERASE,                              /* default erase character */
# Line 128  static const struct chardata init_charda Line 120  static const struct chardata init_charda
120  #endif  #endif
121  };  };
122    
123  /* The following is used for understandable diagnostics. */  static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn";
124    #define F_INITSTRING    (1 << 0)        /* -I initstring is set */
125    #define F_LOCAL         (1 << 1)        /* -L force local */
126    #define F_FAKEHOST      (1 << 2)        /* -H fake hostname */
127    #define F_CUSTISSUE     (1 << 3)        /* -f give alternative issue file */
128    #define F_RTSCTS        (1 << 4)        /* -h enable RTS/CTS flow control */
129    #define F_ISSUE         (1 << 5)        /* -i display /etc/issue */
130    #define F_LOGIN         (1 << 6)        /* -l non-default login program */
131    #define F_PARSE         (1 << 7)        /* -m process modem status messages */
132    #define F_TIMEOUT       (1 << 8)        /* -t time out */
133    #define F_WAITCRLF      (1 << 9)        /* -w wait for CR or LF */
134    #define F_NOPROMPT      (1 << 10)       /* -n don't ask for login name */
135    
 /* Fake hostname for ut_host specified on command line. */  
 static char *fakehost = NULL;  
136    
137  /* ... */  #define line_buf bb_common_bufsiz1
138    
139    /* The following is used for understandable diagnostics. */
140  #ifdef DEBUGGING  #ifdef DEBUGGING
 #define debug(s) fprintf(dbf,s); fflush(dbf)  
 #define DEBUGTERM "/dev/ttyp0"  
141  static FILE *dbf;  static FILE *dbf;
142    #define DEBUGTERM "/dev/ttyp0"
143    #define debug(...) do { fprintf(dbf, __VA_ARGS__); fflush(dbf); } while (0)
144  #else  #else
145  #define debug(s) /* nothing */  #define debug(...) ((void)0)
146  #endif  #endif
147    
148    
149  /* bcode - convert speed string to speed code; return 0 on failure */  /* bcode - convert speed string to speed code; return <= 0 on failure */
150  static int bcode(const char *s)  static int bcode(const char *s)
151  {  {
152   int r;   int value = bb_strtou(s, NULL, 10); /* yes, int is intended! */
153   unsigned value = bb_strtou(s, NULL, 10);   if (value < 0) /* bad terminating char, overflow, etc */
154   if (errno) {   return value;
155   return -1;   return tty_value_to_baud(value);
  }  
  r = tty_value_to_baud(value);  
  if (r > 0) {  
  return r;  
  }  
  return 0;  
156  }  }
157    
   
158  /* parse_speeds - parse alternate baud rates */  /* parse_speeds - parse alternate baud rates */
159  static void parse_speeds(struct options *op, char *arg)  static void parse_speeds(struct options *op, char *arg)
160  {  {
161   char *cp;   char *cp;
162    
163     /* NB: at least one iteration is always done */
164   debug("entered parse_speeds\n");   debug("entered parse_speeds\n");
165   for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {   while ((cp = strsep(&arg, ",")) != NULL) {
166   if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)   op->speeds[op->numspeed] = bcode(cp);
167     if (op->speeds[op->numspeed] <= 0)
168   bb_error_msg_and_die("bad speed: %s", cp);   bb_error_msg_and_die("bad speed: %s", cp);
169     op->numspeed++;
170   if (op->numspeed > MAX_SPEED)   if (op->numspeed > MAX_SPEED)
171   bb_error_msg_and_die("too many alternate speeds");   bb_error_msg_and_die("too many alternate speeds");
172   }   }
173   debug("exiting parsespeeds\n");   debug("exiting parse_speeds\n");
174  }  }
175    
   
176  /* parse_args - parse command-line arguments */  /* parse_args - parse command-line arguments */
177  static void parse_args(int argc, char **argv, struct options *op)  static void parse_args(char **argv, struct options *op, char **fakehost_p)
178  {  {
179   char *ts;   char *ts;
180    
181   op->flags = getopt32(argc, argv, opt_string,   opt_complementary = "-2:t+"; /* at least 2 args; -t N */
182   &(op->initstring), &fakehost, &(op->issue),   op->flags = getopt32(argv, opt_string,
183   &(op->login), &ts);   &(op->initstring), fakehost_p, &(op->issue),
184     &(op->login), &op->timeout);
185     argv += optind;
186   if (op->flags & F_INITSTRING) {   if (op->flags & F_INITSTRING) {
187   const char *p = op->initstring;   const char *p = op->initstring;
188   char *q;   char *q;
189    
190   q = op->initstring = xstrdup(op->initstring);   op->initstring = q = xstrdup(p);
191   /* copy optarg into op->initstring decoding \ddd   /* copy optarg into op->initstring decoding \ddd
192     octal codes into chars */     octal codes into chars */
193   while (*p) {   while (*p) {
# Line 200  static void parse_args(int argc, char ** Line 200  static void parse_args(int argc, char **
200   }   }
201   *q = '\0';   *q = '\0';
202   }   }
203   op->flags ^= F_ISSUE;           /* revert flag show /etc/issue */   op->flags ^= F_ISSUE;           /* invert flag "show /etc/issue" */
204   if (op->flags & F_TIMEOUT) {   debug("after getopt\n");
  op->timeout = xatoul_range(ts, 1, INT_MAX);  
  }  
  argv += optind;  
  argc -= optind;  
  debug("after getopt loop\n");  
  if (argc < 2)          /* check parameter count */  
  bb_show_usage();  
205    
206   /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */   /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
207     op->tty = argv[0];      /* tty name */
208     ts = argv[1];           /* baud rate(s) */
209   if (isdigit(argv[0][0])) {   if (isdigit(argv[0][0])) {
210   /* a number first, assume it's a speed (BSD style) */   /* a number first, assume it's a speed (BSD style) */
211   parse_speeds(op, argv[0]);       /* baud rate(s) */   op->tty = ts;   /* tty name is in argv[1] */
212   op->tty = argv[1]; /* tty name */   ts = argv[0];   /* baud rate(s) */
  } else {  
  op->tty = argv[0];       /* tty name */  
  parse_speeds(op, argv[1]); /* baud rate(s) */  
213   }   }
214     parse_speeds(op, ts);
215    
216   if (argv[2])  // TODO: if applet_name is set to "getty: TTY", bb_error_msg's get simpler!
217   setenv("TERM", argv[2], 1);  // grep for "%s:"
218    
219   debug("exiting parseargs\n");   if (argv[2])
220  }   xsetenv("TERM", argv[2]);
221    
222  static void xdup2(int srcfd, int dstfd, const char *tty)   debug("exiting parse_args\n");
 {  
  if (dup2(srcfd, dstfd) == -1)  
  bb_perror_msg_and_die("%s: dup", tty);  
223  }  }
224    
225  /* open_tty - set up tty as standard { input, output, error } */  /* open_tty - set up tty as standard { input, output, error } */
226  static void open_tty(char *tty, struct termios *tp, int local)  static void open_tty(const char *tty)
227  {  {
  int chdir_to_root = 0;  
   
228   /* Set up new standard input, unless we are given an already opened port. */   /* Set up new standard input, unless we are given an already opened port. */
   
229   if (NOT_LONE_DASH(tty)) {   if (NOT_LONE_DASH(tty)) {
230   struct stat st;  // struct stat st;
231   int fd;  // int cur_dir_fd;
232    // int fd;
233    
234   /* Sanity checks... */   /* Sanity checks... */
235    // cur_dir_fd = xopen(".", O_DIRECTORY | O_NONBLOCK);
236    // xchdir("/dev");
237    // xstat(tty, &st);
238    // if ((st.st_mode & S_IFMT) != S_IFCHR)
239    // bb_error_msg_and_die("%s: not a character device", tty);
240    
241   xchdir("/dev");   if (tty[0] != '/')
242   chdir_to_root = 1;   tty = xasprintf("/dev/%s", tty); /* will leak it */
  xstat(tty, &st);  
  if ((st.st_mode & S_IFMT) != S_IFCHR)  
  bb_error_msg_and_die("%s: not a character device", tty);  
243    
244   /* Open the tty as standard input. */   /* Open the tty as standard input. */
   
245   debug("open(2)\n");   debug("open(2)\n");
246   fd = xopen(tty, O_RDWR | O_NONBLOCK);   close(0);
247   xdup2(fd, 0, tty);   /*fd =*/ xopen(tty, O_RDWR | O_NONBLOCK); /* uses fd 0 */
248   while (fd > 2) close(fd--);  
249    // /* Restore current directory */
250    // fchdir(cur_dir_fd);
251    
252     /* Open the tty as standard input, continued */
253    // xmove_fd(fd, 0);
254    // /* fd is >= cur_dir_fd, and cur_dir_fd gets closed too here: */
255    // while (fd > 2)
256    // close(fd--);
257    
258     /* Set proper protections and ownership. */
259     fchown(0, 0, 0);        /* 0:0 */
260     fchmod(0, 0620);        /* crw--w---- */
261   } else {   } else {
262   /*   /*
263   * Standard input should already be connected to an open port. Make   * Standard input should already be connected to an open port. Make
264   * sure it is open for read/write.   * sure it is open for read/write.
265   */   */
266     if ((fcntl(0, F_GETFL) & O_RDWR) != O_RDWR)
267   if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR)   bb_error_msg_and_die("stdin is not open for read/write");
  bb_error_msg_and_die("%s: not open for read/write", tty);  
268   }   }
   
  /* Replace current standard output/error fd's with new ones */  
  debug("duping\n");  
  xdup2(0, 1, tty);  
  xdup2(0, 2, tty);  
   
  /*  
  * The following ioctl will fail if stdin is not a tty, but also when  
  * there is noise on the modem control lines. In the latter case, the  
  * common course of action is (1) fix your cables (2) give the modem more  
  * time to properly reset after hanging up. SunOS users can achieve (2)  
  * by patching the SunOS kernel variable "zsadtrlow" to a larger value;  
  * 5 seconds seems to be a good value.  
  */  
   
  if (ioctl(0, TCGETS, tp) < 0)  
  bb_perror_msg_and_die("%s: ioctl(TCGETS)", tty);  
   
  /*  
  * It seems to be a terminal. Set proper protections and ownership. Mode  
  * 0622 is suitable for SYSV <4 because /bin/login does not change  
  * protections. SunOS 4 login will change the protections to 0620 (write  
  * access for group tty) after the login has succeeded.  
  */  
   
 #ifdef DEBIAN  
 #warning Debian /dev/vcs[a]NN hack is deprecated and will be removed  
  {  
  /* tty to root.dialout 660 */  
  struct group *gr;  
  int id;  
   
  gr = getgrnam("dialout");  
  id = gr ? gr->gr_gid : 0;  
  chown(tty, 0, id);  
  chmod(tty, 0660);  
   
  /* vcs,vcsa to root.sys 600 */  
  if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) {  
  char *vcs, *vcsa;  
   
  vcs = xstrdup(tty);  
  vcsa = xmalloc(strlen(tty) + 2);  
  strcpy(vcs, "vcs");  
  strcpy(vcs + 3, tty + 3);  
  strcpy(vcsa, "vcsa");  
  strcpy(vcsa + 4, tty + 3);  
   
  id = (gr = getgrnam("sys")) ? gr->gr_gid : 0;  
  chown(vcs, 0, id);  
  chmod(vcs, 0600);  
  chown(vcsa, 0, id);  
  chmod(vcs, 0600);  
   
  free(vcs);  
  free(vcsa);  
  }  
  }  
 #else  
  if (NOT_LONE_DASH(tty)) {  
  chown(tty, 0, 0);        /* 0:0 */  
  chmod(tty, 0622);        /* crw--w--w- */  
  }  
 #endif  
  if (chdir_to_root)  
  xchdir("/");  
269  }  }
270    
271  /* termios_init - initialize termios settings */  /* termios_init - initialize termios settings */
# Line 345  static void termios_init(struct termios Line 279  static void termios_init(struct termios
279   */   */
280  #ifdef __linux__  #ifdef __linux__
281   /* flush input and output queues, important for modems! */   /* flush input and output queues, important for modems! */
282   ioctl(0, TCFLSH, TCIOFLUSH);   ioctl(0, TCFLSH, TCIOFLUSH); /* tcflush(0, TCIOFLUSH)? - same */
283  #endif  #endif
284    
285   tp->c_cflag = CS8 | HUPCL | CREAD | speed;   tp->c_cflag = CS8 | HUPCL | CREAD | speed;
286   if (op->flags & F_LOCAL) {   if (op->flags & F_LOCAL)
287   tp->c_cflag |= CLOCAL;   tp->c_cflag |= CLOCAL;
  }  
288    
289   tp->c_iflag = tp->c_lflag = tp->c_line = 0;   tp->c_iflag = tp->c_lflag = tp->c_line = 0;
290   tp->c_oflag = OPOST | ONLCR;   tp->c_oflag = OPOST | ONLCR;
# Line 359  static void termios_init(struct termios Line 292  static void termios_init(struct termios
292   tp->c_cc[VTIME] = 0;   tp->c_cc[VTIME] = 0;
293    
294   /* Optionally enable hardware flow control */   /* Optionally enable hardware flow control */
295    #ifdef CRTSCTS
 #ifdef  CRTSCTS  
296   if (op->flags & F_RTSCTS)   if (op->flags & F_RTSCTS)
297   tp->c_cflag |= CRTSCTS;   tp->c_cflag |= CRTSCTS;
298  #endif  #endif
299    
300   ioctl(0, TCSETS, tp);   tcsetattr_stdin_TCSANOW(tp);
   
  /* go to blocking input even in local mode */  
  fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK);  
301    
302   debug("term_io 2\n");   debug("term_io 2\n");
303  }  }
# Line 401  static void auto_baud(char *buf, unsigne Line 330  static void auto_baud(char *buf, unsigne
330   * Use 7-bit characters, don't block if input queue is empty. Errors will   * Use 7-bit characters, don't block if input queue is empty. Errors will
331   * be dealt with later on.   * be dealt with later on.
332   */   */
   
333   iflag = tp->c_iflag;   iflag = tp->c_iflag;
334   tp->c_iflag |= ISTRIP;          /* enable 8th-bit stripping */   tp->c_iflag |= ISTRIP;          /* enable 8th-bit stripping */
335   vmin = tp->c_cc[VMIN];   vmin = tp->c_cc[VMIN];
336   tp->c_cc[VMIN] = 0;                     /* don't block if queue empty */   tp->c_cc[VMIN] = 0;             /* don't block if queue empty */
337   ioctl(0, TCSETS, tp);   tcsetattr_stdin_TCSANOW(tp);
338    
339   /*   /*
340   * Wait for a while, then read everything the modem has said so far and   * Wait for a while, then read everything the modem has said so far and
341   * try to extract the speed of the dial-in call.   * try to extract the speed of the dial-in call.
342   */   */
   
343   sleep(1);   sleep(1);
344   nread = read(0, buf, size_buf - 1);   nread = safe_read(STDIN_FILENO, buf, size_buf - 1);
345   if (nread > 0) {   if (nread > 0) {
346   buf[nread] = '\0';   buf[nread] = '\0';
347   for (bp = buf; bp < buf + nread; bp++) {   for (bp = buf; bp < buf + nread; bp++) {
348   if (isascii(*bp) && isdigit(*bp)) {   if (isdigit(*bp)) {
349   speed = bcode(bp);   speed = bcode(bp);
350   if (speed) {   if (speed > 0) {
351   tp->c_cflag &= ~CBAUD;   tp->c_cflag &= ~CBAUD;
352   tp->c_cflag |= speed;   tp->c_cflag |= speed;
353   }   }
# Line 428  static void auto_baud(char *buf, unsigne Line 355  static void auto_baud(char *buf, unsigne
355   }   }
356   }   }
357   }   }
  /* Restore terminal settings. Errors will be dealt with later on. */  
358    
359     /* Restore terminal settings. Errors will be dealt with later on. */
360   tp->c_iflag = iflag;   tp->c_iflag = iflag;
361   tp->c_cc[VMIN] = vmin;   tp->c_cc[VMIN] = vmin;
362   ioctl(0, TCSETS, tp);   tcsetattr_stdin_TCSANOW(tp);
 }  
   
 /* next_speed - select next baud rate */  
 static void next_speed(struct termios *tp, struct options *op)  
 {  
  static int baud_index = FIRST_SPEED;    /* current speed index */  
   
  baud_index = (baud_index + 1) % op->numspeed;  
  tp->c_cflag &= ~CBAUD;  
  tp->c_cflag |= op->speeds[baud_index];  
  ioctl(0, TCSETS, tp);  
363  }  }
364    
   
365  /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */  /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
366  static void do_prompt(struct options *op, struct termios *tp)  static void do_prompt(struct options *op)
367  {  {
368  #ifdef ISSUE  #ifdef ISSUE
369   print_login_issue(op->issue, op->tty);   print_login_issue(op->issue, op->tty);
# Line 457  static void do_prompt(struct options *op Line 372  static void do_prompt(struct options *op
372  }  }
373    
374  #ifdef HANDLE_ALLCAPS  #ifdef HANDLE_ALLCAPS
375  /* caps_lock - string contains upper case without lower case */  /* all_is_upcase - string contains upper case without lower case */
376  /* returns 1 if true, 0 if false */  /* returns 1 if true, 0 if false */
377  static int caps_lock(const char *s)  static int all_is_upcase(const char *s)
378  {  {
379   while (*s)   while (*s)
380   if (islower(*s++))   if (islower(*s++))
# Line 468  static int caps_lock(const char *s) Line 383  static int caps_lock(const char *s)
383  }  }
384  #endif  #endif
385    
386  /* get_logname - get user name, establish parity, speed, erase, kill, eol */  /* get_logname - get user name, establish parity, speed, erase, kill, eol;
387  /* return NULL on failure, logname on success */   * return NULL on BREAK, logname on success */
388  static char *get_logname(char *logname, unsigned size_logname,  static char *get_logname(char *logname, unsigned size_logname,
389   struct options *op, struct chardata *cp, struct termios *tp)   struct options *op, struct chardata *cp)
390  {  {
391   char *bp;   char *bp;
392   char c; /* input character, full eight bits */   char c; /* input character, full eight bits */
393   char ascval;                    /* low 7 bits of input character */   char ascval;                    /* low 7 bits of input character */
394   int bits;                       /* # of "1" bits per character */   int bits;                       /* # of "1" bits per character */
395   int mask;                       /* mask with 1 bit up */   int mask;                       /* mask with 1 bit up */
396   static const char erase[][3] = {    /* backspace-space-backspace */   static const char erase[][3] = {/* backspace-space-backspace */
397   "\010\040\010",                 /* space parity */   "\010\040\010",                 /* space parity */
398   "\010\040\010",                 /* odd parity */   "\010\040\010",                 /* odd parity */
399   "\210\240\210",                 /* even parity */   "\210\240\210",                 /* even parity */
400   "\210\240\210",                 /* no parity */   "\010\040\010",                 /* 8 bit no parity */
401   };   };
402    
403   /* Initialize kill, erase, parity etc. (also after switching speeds). */   /* NB: *cp is pre-initialized with init_chardata */
   
  *cp = init_chardata;  
404    
405   /* Flush pending input (esp. after parsing or switching the baud rate). */   /* Flush pending input (esp. after parsing or switching the baud rate). */
   
406   sleep(1);   sleep(1);
407   ioctl(0, TCFLSH, TCIFLUSH);   ioctl(0, TCFLSH, TCIFLUSH); /* tcflush(0, TCIOFLUSH)? - same */
408    
409   /* Prompt for and read a login name. */   /* Prompt for and read a login name. */
   
410   logname[0] = '\0';   logname[0] = '\0';
411   while (!logname[0]) {   while (!logname[0]) {
   
412   /* Write issue file and prompt, with "parity" bit == 0. */   /* Write issue file and prompt, with "parity" bit == 0. */
413     do_prompt(op);
  do_prompt(op, tp);  
414    
415   /* Read name, watch for break, parity, erase, kill, end-of-line. */   /* Read name, watch for break, parity, erase, kill, end-of-line. */
   
416   bp = logname;   bp = logname;
417   cp->eol = '\0';   cp->eol = '\0';
418   while (cp->eol == '\0') {   while (cp->eol == '\0') {
419    
420   /* Do not report trivial EINTR/EIO errors. */   /* Do not report trivial EINTR/EIO errors. */
421   if (read(0, &c, 1) < 1) {   if (read(STDIN_FILENO, &c, 1) < 1) {
422   if (errno == EINTR || errno == EIO)   if (errno == EINTR || errno == EIO)
423   exit(0);   exit(EXIT_SUCCESS);
424   bb_perror_msg_and_die("%s: read", op->tty);   bb_perror_msg_and_die("%s: read", op->tty);
425   }   }
426    
427   /* Do BREAK handling elsewhere. */   /* BREAK. If we have speeds to try,
428     * return NULL (will switch speeds and return here) */
429   if (c == '\0' && op->numspeed > 1)   if (c == '\0' && op->numspeed > 1)
430   return NULL;   return NULL;
431    
432   /* Do parity bit handling. */   /* Do parity bit handling. */
433   ascval = c & 0177;   if (!(op->flags & F_LOCAL) && (c & 0x80)) {       /* "parity" bit on? */
  if (c != ascval) {       /* "parity" bit on ? */  
434   bits = 1;   bits = 1;
435   mask = 1;   mask = 1;
436   while (mask & 0177) {   while (mask & 0x7f) {
437   if (mask & ascval)   if (mask & c)
438   bits++; /* count "1" bits */   bits++; /* count "1" bits */
439   mask <<= 1;   mask <<= 1;
440   }   }
# Line 535  static char *get_logname(char *logname, Line 443  static char *get_logname(char *logname,
443   }   }
444    
445   /* Do erase, kill and end-of-line processing. */   /* Do erase, kill and end-of-line processing. */
446     ascval = c & 0x7f;
447   switch (ascval) {   switch (ascval) {
448   case CR:   case CR:
449   case NL:   case NL:
# Line 543  static char *get_logname(char *logname, Line 452  static char *get_logname(char *logname,
452   break;   break;
453   case BS:   case BS:
454   case DEL:   case DEL:
455    #ifdef ANCIENT_BS_KILL_CHARS
456   case '#':   case '#':
457    #endif
458   cp->erase = ascval;     /* set erase character */   cp->erase = ascval;     /* set erase character */
459   if (bp > logname) {   if (bp > logname) {
460   write(1, erase[cp->parity], 3);   full_write(STDOUT_FILENO, erase[cp->parity], 3);
461   bp--;   bp--;
462   }   }
463   break;   break;
464   case CTL('U'):   case CTL('U'):
465    #ifdef ANCIENT_BS_KILL_CHARS
466   case '@':   case '@':
467    #endif
468   cp->kill = ascval;      /* set kill character */   cp->kill = ascval;      /* set kill character */
469   while (bp > logname) {   while (bp > logname) {
470   write(1, erase[cp->parity], 3);   full_write(STDOUT_FILENO, erase[cp->parity], 3);
471   bp--;   bp--;
472   }   }
473   break;   break;
474   case CTL('D'):   case CTL('D'):
475   exit(0);   exit(EXIT_SUCCESS);
476   default:   default:
477   if (!isascii(ascval) || !isprint(ascval)) {   if (!isascii(ascval) || !isprint(ascval)) {
478   /* ignore garbage characters */   /* ignore garbage characters */
479   } else if (bp - logname >= size_logname - 1) {   } else if ((int)(bp - logname) >= size_logname - 1) {
480   bb_error_msg_and_die("%s: input overrun", op->tty);   bb_error_msg_and_die("%s: input overrun", op->tty);
481   } else {   } else {
482   write(1, &c, 1); /* echo the character */   full_write(STDOUT_FILENO, &c, 1); /* echo the character */
483   *bp++ = ascval; /* and store it */   *bp++ = ascval; /* and store it */
484   }   }
485   break;   break;
# Line 576  static char *get_logname(char *logname, Line 489  static char *get_logname(char *logname,
489   /* Handle names with upper case and no lower case. */   /* Handle names with upper case and no lower case. */
490    
491  #ifdef HANDLE_ALLCAPS  #ifdef HANDLE_ALLCAPS
492   cp->capslock = caps_lock(logname);   cp->capslock = all_is_upcase(logname);
493   if (cp->capslock) {   if (cp->capslock) {
494   for (bp = logname; *bp; bp++)   for (bp = logname; *bp; bp++)
495   if (isupper(*bp))   if (isupper(*bp))
# Line 590  static char *get_logname(char *logname, Line 503  static char *get_logname(char *logname,
503  static void termios_final(struct options *op, struct termios *tp, struct chardata *cp)  static void termios_final(struct options *op, struct termios *tp, struct chardata *cp)
504  {  {
505   /* General terminal-independent stuff. */   /* General terminal-independent stuff. */
   
506   tp->c_iflag |= IXON | IXOFF;    /* 2-way flow control */   tp->c_iflag |= IXON | IXOFF;    /* 2-way flow control */
507   tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;   tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
508   /* no longer| ECHOCTL | ECHOPRT */   /* no longer| ECHOCTL | ECHOPRT */
# Line 603  static void termios_final(struct options Line 515  static void termios_final(struct options
515   tp->c_cc[VSWTC] = DEF_SWITCH;   /* default switch character */   tp->c_cc[VSWTC] = DEF_SWITCH;   /* default switch character */
516    
517   /* Account for special characters seen in input. */   /* Account for special characters seen in input. */
   
518   if (cp->eol == CR) {   if (cp->eol == CR) {
519   tp->c_iflag |= ICRNL;   /* map CR in input to NL */   tp->c_iflag |= ICRNL;   /* map CR in input to NL */
520   tp->c_oflag |= ONLCR;   /* map NL in output to CR-NL */   tp->c_oflag |= ONLCR;   /* map NL in output to CR-NL */
# Line 612  static void termios_final(struct options Line 523  static void termios_final(struct options
523   tp->c_cc[VKILL] = cp->kill;     /* set kill character */   tp->c_cc[VKILL] = cp->kill;     /* set kill character */
524    
525   /* Account for the presence or absence of parity bits in input. */   /* Account for the presence or absence of parity bits in input. */
   
526   switch (cp->parity) {   switch (cp->parity) {
527   case 0:                                 /* space (always 0) parity */   case 0:                                 /* space (always 0) parity */
528    // I bet most people go here - they use only 7-bit chars in usernames....
529   break;   break;
530   case 1:                                 /* odd parity */   case 1:                                 /* odd parity */
531   tp->c_cflag |= PARODD;   tp->c_cflag |= PARODD;
# Line 626  static void termios_final(struct options Line 537  static void termios_final(struct options
537   case (1 | 2):                           /* no parity bit */   case (1 | 2):                           /* no parity bit */
538   tp->c_cflag &= ~CSIZE;   tp->c_cflag &= ~CSIZE;
539   tp->c_cflag |= CS7;   tp->c_cflag |= CS7;
540    // FIXME: wtf? case 3: we saw both even and odd 8-bit bytes -
541    // it's probably some umlauts etc, but definitely NOT 7-bit!!!
542    // Entire parity detection madness here just begs for deletion...
543   break;   break;
544   }   }
  /* Account for upper case without lower case. */  
545    
546     /* Account for upper case without lower case. */
547  #ifdef HANDLE_ALLCAPS  #ifdef HANDLE_ALLCAPS
548   if (cp->capslock) {   if (cp->capslock) {
549   tp->c_iflag |= IUCLC;   tp->c_iflag |= IUCLC;
# Line 638  static void termios_final(struct options Line 552  static void termios_final(struct options
552   }   }
553  #endif  #endif
554   /* Optionally enable hardware flow control */   /* Optionally enable hardware flow control */
555    #ifdef CRTSCTS
 #ifdef  CRTSCTS  
556   if (op->flags & F_RTSCTS)   if (op->flags & F_RTSCTS)
557   tp->c_cflag |= CRTSCTS;   tp->c_cflag |= CRTSCTS;
558  #endif  #endif
559    
560   /* Finally, make the new settings effective */   /* Finally, make the new settings effective */
561     /* It's tcsetattr_stdin_TCSANOW() + error check */
562   if (ioctl(0, TCSETS, tp) < 0)   ioctl_or_perror_and_die(0, TCSETS, tp, "%s: TCSETS", op->tty);
  bb_perror_msg_and_die("%s: ioctl(TCSETS)", op->tty);  
563  }  }
564    
   
 #ifdef SYSV_STYLE  
565  #if ENABLE_FEATURE_UTMP  #if ENABLE_FEATURE_UTMP
566    static void touch(const char *filename)
567    {
568     if (access(filename, R_OK | W_OK) == -1)
569     close(open(filename, O_WRONLY | O_CREAT, 0664));
570    }
571    
572  /* update_utmp - update our utmp entry */  /* update_utmp - update our utmp entry */
573  static void update_utmp(char *line)  static void update_utmp(const char *line, char *fakehost)
574  {  {
575   struct utmp ut;   struct utmp ut;
576   struct utmp *utp;   struct utmp *utp;
  time_t t;  
577   int mypid = getpid();   int mypid = getpid();
578    
579     /* In case we won't find an entry below... */
580     memset(&ut, 0, sizeof(ut));
581     safe_strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
582    
583   /*   /*
584   * The utmp file holds miscellaneous information about things started by   * The utmp file holds miscellaneous information about things started by
585   * /sbin/init and other system-related events. Our purpose is to update   * /sbin/init and other system-related events. Our purpose is to update
# Line 669  static void update_utmp(char *line) Line 588  static void update_utmp(char *line)
588   * utmp file can be opened for update, and if we are able to find our   * utmp file can be opened for update, and if we are able to find our
589   * entry in the utmp file.   * entry in the utmp file.
590   */   */
591   if (access(_PATH_UTMP, R_OK|W_OK) == -1) {   touch(_PATH_UTMP);
592   close(creat(_PATH_UTMP, 0664));  
  }  
593   utmpname(_PATH_UTMP);   utmpname(_PATH_UTMP);
594   setutent();   setutent();
595   while ((utp = getutent())   while ((utp = getutent()) != NULL) {
596     && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid))   if (utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid) {
597   /* nothing */;   memcpy(&ut, utp, sizeof(ut));
598     break;
599   if (utp) {   }
  memcpy(&ut, utp, sizeof(ut));  
  } else {  
  /* some inits don't initialize utmp... */  
  memset(&ut, 0, sizeof(ut));  
  safe_strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));  
600   }   }
  /* endutent(); */  
601    
602   strcpy(ut.ut_user, "LOGIN");   strcpy(ut.ut_user, "LOGIN");
603   safe_strncpy(ut.ut_line, line, sizeof(ut.ut_line));   safe_strncpy(ut.ut_line, line, sizeof(ut.ut_line));
604   if (fakehost)   if (fakehost)
605   safe_strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));   safe_strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
606   time(&t);   ut.ut_tv.tv_sec = time(NULL);
  ut.ut_time = t;  
607   ut.ut_type = LOGIN_PROCESS;   ut.ut_type = LOGIN_PROCESS;
608   ut.ut_pid = mypid;   ut.ut_pid = mypid;
609    
# Line 700  static void update_utmp(char *line) Line 611  static void update_utmp(char *line)
611   endutent();   endutent();
612    
613  #if ENABLE_FEATURE_WTMP  #if ENABLE_FEATURE_WTMP
614   if (access(bb_path_wtmp_file, R_OK|W_OK) == -1)   touch(bb_path_wtmp_file);
  close(creat(bb_path_wtmp_file, 0664));  
615   updwtmp(bb_path_wtmp_file, &ut);   updwtmp(bb_path_wtmp_file, &ut);
616  #endif  #endif
617  }  }
   
618  #endif /* CONFIG_FEATURE_UTMP */  #endif /* CONFIG_FEATURE_UTMP */
 #endif /* SYSV_STYLE */  
619    
620    int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
621  int getty_main(int argc, char **argv)  int getty_main(int argc UNUSED_PARAM, char **argv)
622  {  {
623   int nullfd;   int n;
624   char *logname = NULL;           /* login name, given to /bin/login */   char *fakehost = NULL;          /* Fake hostname for ut_host */
625     char *logname;                  /* login name, given to /bin/login */
626   /* Merging these into "struct local" may _seem_ to reduce   /* Merging these into "struct local" may _seem_ to reduce
627   * parameter passing, but today's gcc will inline   * parameter passing, but today's gcc will inline
628   * statics which are called once anyway, so don't do that */   * statics which are called once anyway, so don't do that */
629   struct chardata chardata;       /* set by get_logname() */   struct chardata chardata;       /* set by get_logname() */
630   struct termios termios;           /* terminal mode bits */   struct termios termios;         /* terminal mode bits */
631   struct options options = {   struct options options;
632   0,                      /* show /etc/issue (SYSV_STYLE) */  
633   0,                      /* no timeout */   chardata = init_chardata;
634   _PATH_LOGIN,            /* default login program */  
635   "tty1",                 /* default tty line */   memset(&options, 0, sizeof(options));
636   "",                     /* modem init string */   options.login = _PATH_LOGIN;    /* default login program */
637     options.tty = "tty1";           /* default tty line */
638     options.initstring = "";        /* modem init string */
639  #ifdef ISSUE  #ifdef ISSUE
640   ISSUE,                  /* default issue file */   options.issue = ISSUE;          /* default issue file */
 #else  
  NULL,  
641  #endif  #endif
  0,                      /* no baud rates known yet */  
  };  
642    
643   /* Already too late because of theoretical   /* Parse command-line arguments. */
644   * possibility of getty --help somehow triggered   parse_args(argv, &options, &fakehost);
645   * inadvertently before we reach this. Oh well. */  
646   logmode = LOGMODE_NONE;   logmode = LOGMODE_NONE;
647    
648     /* Create new session, lose controlling tty, if any */
649     /* docs/ctty.htm says:
650     * "This is allowed only when the current process
651     *  is not a process group leader" - is this a problem? */
652   setsid();   setsid();
653   nullfd = xopen(bb_dev_null, O_RDWR);   /* close stdio, and stray descriptors, just in case */
654   /* dup2(nullfd, 0); - no, because of possible "getty - 9600" */   n = xopen(bb_dev_null, O_RDWR);
655   /* open_tty() will take care of fd# 0 anyway */   /* dup2(n, 0); - no, we need to handle "getty - 9600" too */
656   dup2(nullfd, 1);   xdup2(n, 1);
657   dup2(nullfd, 2);   xdup2(n, 2);
658   while (nullfd > 2) close(nullfd--);   while (n > 2)
659   /* We want special flavor of error_msg_and_die */   close(n--);
660    
661     /* Logging. We want special flavor of error_msg_and_die */
662   die_sleep = 10;   die_sleep = 10;
663   msg_eol = "\r\n";   msg_eol = "\r\n";
664     /* most likely will internally use fd #3 in CLOEXEC mode: */
665   openlog(applet_name, LOG_PID, LOG_AUTH);   openlog(applet_name, LOG_PID, LOG_AUTH);
666   logmode = LOGMODE_BOTH;   logmode = LOGMODE_BOTH;
667    
668  #ifdef DEBUGGING  #ifdef DEBUGGING
669   dbf = xfopen(DEBUGTERM, "w");   dbf = xfopen_for_write(DEBUGTERM);
670     for (n = 1; argv[n]; n++) {
671   {   debug(argv[n]);
672   int i;   debug("\n");
   
  for (i = 1; i < argc; i++) {  
  debug(argv[i]);  
  debug("\n");  
  }  
673   }   }
674  #endif  #endif
675    
676   /* Parse command-line arguments. */   /* Open the tty as standard input, if it is not "-" */
677   parse_args(argc, argv, &options);   /* If it's not "-" and not taken yet, it will become our ctty */
   
 #ifdef SYSV_STYLE  
 #if ENABLE_FEATURE_UTMP  
  /* Update the utmp file. */  
  update_utmp(options.tty);  
 #endif  
 #endif  
   
678   debug("calling open_tty\n");   debug("calling open_tty\n");
679   /* Open the tty as standard { input, output, error }. */   open_tty(options.tty);
680   open_tty(options.tty, &termios, options.flags & F_LOCAL);   ndelay_off(0);
681     debug("duping\n");
682     xdup2(0, 1);
683     xdup2(0, 2);
684    
685     /*
686     * The following ioctl will fail if stdin is not a tty, but also when
687     * there is noise on the modem control lines. In the latter case, the
688     * common course of action is (1) fix your cables (2) give the modem more
689     * time to properly reset after hanging up. SunOS users can achieve (2)
690     * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
691     * 5 seconds seems to be a good value.
692     */
693     /* tcgetattr() + error check */
694     ioctl_or_perror_and_die(0, TCGETS, &termios, "%s: TCGETS", options.tty);
695    
696  #ifdef __linux__  #ifdef __linux__
697   {  // FIXME: do we need this? Otherwise "-" case seems to be broken...
698   int iv;   // /* Forcibly make fd 0 our controlling tty, even if another session
699     //  * has it as a ctty. (Another session loses ctty). */
700     // ioctl(0, TIOCSCTTY, (void*)1);
701     /* Make ourself a foreground process group within our session */
702     tcsetpgrp(0, getpid());
703    #endif
704    
705   iv = getpid();  #if ENABLE_FEATURE_UTMP
706   ioctl(0, TIOCSPGRP, &iv);   /* Update the utmp file. This tty is ours now! */
707   }   update_utmp(options.tty, fakehost);
708  #endif  #endif
709    
710   /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */   /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
711   debug("calling termios_init\n");   debug("calling termios_init\n");
712   termios_init(&termios, options.speeds[FIRST_SPEED], &options);   termios_init(&termios, options.speeds[0], &options);
713    
714   /* write the modem init string and DON'T flush the buffers */   /* Write the modem init string and DON'T flush the buffers */
715   if (options.flags & F_INITSTRING) {   if (options.flags & F_INITSTRING) {
716   debug("writing init string\n");   debug("writing init string\n");
717   write(1, options.initstring, strlen(options.initstring));   full_write(STDOUT_FILENO, options.initstring, strlen(options.initstring));
  }  
   
  if (!(options.flags & F_LOCAL)) {  
  /* go to blocking write mode unless -L is specified */  
  fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK);  
718   }   }
719    
720   /* Optionally detect the baud rate from the modem status message. */   /* Optionally detect the baud rate from the modem status message */
721   debug("before autobaud\n");   debug("before autobaud\n");
722   if (options.flags & F_PARSE)   if (options.flags & F_PARSE)
723   auto_baud(bb_common_bufsiz1, sizeof(bb_common_bufsiz1), &termios);   auto_baud(line_buf, sizeof(line_buf), &termios);
724    
725   /* Set the optional timer. */   /* Set the optional timer */
726   if (options.timeout)   alarm(options.timeout); /* if 0, alarm is not set */
  alarm(options.timeout);  
727    
728   /* optionally wait for CR or LF before writing /etc/issue */   /* Optionally wait for CR or LF before writing /etc/issue */
729   if (options.flags & F_WAITCRLF) {   if (options.flags & F_WAITCRLF) {
730   char ch;   char ch;
731    
732   debug("waiting for cr-lf\n");   debug("waiting for cr-lf\n");
733   while (read(0, &ch, 1) == 1) {   while (safe_read(STDIN_FILENO, &ch, 1) == 1) {
734     debug("read %x\n", (unsigned char)ch);
735   ch &= 0x7f;                     /* strip "parity bit" */   ch &= 0x7f;                     /* strip "parity bit" */
 #ifdef DEBUGGING  
  fprintf(dbf, "read %c\n", ch);  
 #endif  
736   if (ch == '\n' || ch == '\r')   if (ch == '\n' || ch == '\r')
737   break;   break;
738   }   }
739   }   }
740    
741   chardata = init_chardata;   logname = NULL;
742   if (!(options.flags & F_NOPROMPT)) {   if (!(options.flags & F_NOPROMPT)) {
743   /* Read the login name. */   /* NB:termios_init already set line speed
744   debug("reading login name\n");   * to options.speeds[0] */
745   logname = get_logname(bb_common_bufsiz1, sizeof(bb_common_bufsiz1),   int baud_index = 0;
746   &options, &chardata, &termios);  
747   while (logname == NULL)   while (1) {
748   next_speed(&termios, &options);   /* Read the login name. */
749     debug("reading login name\n");
750     logname = get_logname(line_buf, sizeof(line_buf),
751     &options, &chardata);
752     if (logname)
753     break;
754     /* we are here only if options.numspeed > 1 */
755     baud_index = (baud_index + 1) % options.numspeed;
756     termios.c_cflag &= ~CBAUD;
757     termios.c_cflag |= options.speeds[baud_index];
758     tcsetattr_stdin_TCSANOW(&termios);
759     }
760   }   }
761    
762   /* Disable timer. */   /* Disable timer. */
763     alarm(0);
  if (options.timeout)  
  alarm(0);  
764    
765   /* Finalize the termios settings. */   /* Finalize the termios settings. */
   
766   termios_final(&options, &termios, &chardata);   termios_final(&options, &termios, &chardata);
767    
768   /* Now the newline character should be properly written. */   /* Now the newline character should be properly written. */
769     full_write(STDOUT_FILENO, "\n", 1);
  write(1, "\n", 1);  
770    
771   /* Let the login program take care of password validation. */   /* Let the login program take care of password validation. */
772     /* We use PATH because we trust that root doesn't set "bad" PATH,
773   execl(options.login, options.login, "--", logname, (char *) 0);   * and getty is not suid-root applet. */
774     /* With -n, logname == NULL, and login will ask for username instead */
775     BB_EXECLP(options.login, options.login, "--", logname, NULL);
776   bb_error_msg_and_die("%s: can't exec %s", options.tty, options.login);   bb_error_msg_and_die("%s: can't exec %s", options.tty, options.login);
777  }  }

Legend:
Removed from v.532  
changed lines
  Added in v.816