As usual, I forgot "svn del"...
[oweals/busybox.git] / loginutils / getty.c
1 /* vi: set sw=4 ts=4: */
2 /* agetty.c - another getty program for Linux. By W. Z. Venema 1989
3  * Ported to Linux by Peter Orbaek <poe@daimi.aau.dk>
4  * This program is freely distributable. The entire man-page used to
5  * be here. Now read the real man-page agetty.8 instead.
6  *
7  * option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95
8  *
9  * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
10  * - added Native Language Support
11
12  * 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>
13  * - enable hardware flow control before displaying /etc/issue
14  *
15  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
16  *
17  */
18
19 #include "busybox.h"
20 #include <syslog.h>
21
22 #ifdef CONFIG_FEATURE_UTMP
23 #include <utmp.h>
24 #endif
25
26  /*
27   * Some heuristics to find out what environment we are in: if it is not
28   * System V, assume it is SunOS 4.
29   */
30
31 #ifdef LOGIN_PROCESS                    /* defined in System V utmp.h */
32 #define SYSV_STYLE                      /* select System V style getty */
33 #ifdef CONFIG_FEATURE_WTMP
34 extern void updwtmp(const char *filename, const struct utmp *ut);
35 #endif
36 #endif  /* LOGIN_PROCESS */
37
38 #ifdef SYSV_STYLE
39 #include <sys/utsname.h>
40 #include <time.h>
41 #endif
42
43  /*
44   * Things you may want to modify.
45   *
46   * You may disagree with the default line-editing etc. characters defined
47   * below. Note, however, that DEL cannot be used for interrupt generation
48   * and for line editing at the same time.
49   */
50
51 /* I doubt there are systems which still need this */
52 #undef HANDLE_ALLCAPS
53
54 #define _PATH_LOGIN     "/bin/login"
55
56  /* If ISSUE is not defined, agetty will never display the contents of the
57   * /etc/issue file. You will not want to spit out large "issue" files at the
58   * wrong baud rate.
59   */
60 #define ISSUE "/etc/issue"              /* displayed before the login prompt */
61
62 /* Some shorthands for control characters. */
63
64 #define CTL(x)          (x ^ 0100)      /* Assumes ASCII dialect */
65 #define CR              CTL('M')        /* carriage return */
66 #define NL              CTL('J')        /* line feed */
67 #define BS              CTL('H')        /* back space */
68 #define DEL             CTL('?')        /* delete */
69
70 /* Defaults for line-editing etc. characters; you may want to change this. */
71
72 #define DEF_ERASE       DEL             /* default erase character */
73 #define DEF_INTR        CTL('C')        /* default interrupt character */
74 #define DEF_QUIT        CTL('\\')       /* default quit char */
75 #define DEF_KILL        CTL('U')        /* default kill char */
76 #define DEF_EOF         CTL('D')        /* default EOF char */
77 #define DEF_EOL         '\n'
78 #define DEF_SWITCH      0               /* default switch char */
79
80  /*
81   * SunOS 4.1.1 termio is broken. We must use the termios stuff instead,
82   * because the termio -> termios translation does not clear the termios
83   * CIBAUD bits. Therefore, the tty driver would sometimes report that input
84   * baud rate != output baud rate. I did not notice that problem with SunOS
85   * 4.1. We will use termios where available, and termio otherwise.
86   */
87
88 /* linux 0.12 termio is broken too, if we use it c_cc[VERASE] isn't set
89    properly, but all is well if we use termios?! */
90
91 #ifdef  TCGETS
92 #undef  TCGETA
93 #undef  TCSETA
94 #undef  TCSETAW
95 #define termio  termios
96 #define TCGETA  TCGETS
97 #define TCSETA  TCSETS
98 #define TCSETAW TCSETSW
99 #endif
100
101  /*
102   * When multiple baud rates are specified on the command line, the first one
103   * we will try is the first one specified.
104   */
105
106 #define FIRST_SPEED     0
107
108 /* Storage for command-line options. */
109
110 #define MAX_SPEED       10              /* max. nr. of baud rates */
111
112 struct options {
113         int flags;                      /* toggle switches, see below */
114         unsigned timeout;               /* time-out period */
115         char *login;                    /* login program */
116         char *tty;                      /* name of tty */
117         char *initstring;               /* modem init string */
118         char *issue;                    /* alternative issue file */
119         int numspeed;                   /* number of baud rates to try */
120         int speeds[MAX_SPEED];          /* baud rates to be tried */
121 };
122
123 static const char opt_string[] = "I:LH:f:hil:mt:wn";
124 #define F_INITSTRING    (1<<0)          /* initstring is set */
125 #define F_LOCAL         (1<<1)          /* force local */
126 #define F_FAKEHOST      (1<<2)          /* force fakehost */
127 #define F_CUSTISSUE     (1<<3)          /* give alternative issue file */
128 #define F_RTSCTS        (1<<4)          /* enable RTS/CTS flow control */
129 #define F_ISSUE         (1<<5)          /* display /etc/issue */
130 #define F_LOGIN         (1<<6)          /* non-default login program */
131 #define F_PARSE         (1<<7)          /* process modem status messages */
132 #define F_TIMEOUT       (1<<8)          /* time out */
133 #define F_WAITCRLF      (1<<9)          /* wait for CR or LF */
134 #define F_NOPROMPT      (1<<10)         /* don't ask for login name! */
135
136 /* Storage for things detected while the login name was read. */
137
138 struct chardata {
139         unsigned char erase;    /* erase character */
140         unsigned char kill;     /* kill character */
141         unsigned char eol;      /* end-of-line character */
142         unsigned char parity;   /* what parity did we see */
143 #ifdef HANDLE_ALLCAPS
144         unsigned char capslock; /* upper case without lower case */
145 #endif
146 };
147
148 /* Initial values for the above. */
149
150 static const struct chardata init_chardata = {
151         DEF_ERASE,                              /* default erase character */
152         DEF_KILL,                               /* default kill character */
153         13,                                     /* default eol char */
154         0,                                      /* space parity */
155 #ifdef HANDLE_ALLCAPS
156         0,                                      /* no capslock */
157 #endif
158 };
159
160 #ifdef SYSV_STYLE
161 #ifdef CONFIG_FEATURE_UTMP
162 static void update_utmp(char *line);
163 #endif
164 #endif
165
166 /* The following is used for understandable diagnostics. */
167
168 /* Fake hostname for ut_host specified on command line. */
169 static char *fakehost = NULL;
170
171 /* ... */
172 #ifdef DEBUGGING
173 #define debug(s) fprintf(dbf,s); fflush(dbf)
174 #define DEBUGTERM "/dev/ttyp0"
175 static FILE *dbf;
176 #else
177 #define debug(s) /* nothing */
178 #endif
179
180
181 /* bcode - convert speed string to speed code; return 0 on failure */
182 static int bcode(const char *s)
183 {
184         int r;
185         unsigned value;
186         if (safe_strtou((char *)s, &value)) {
187                 return -1;
188         }
189         r = tty_value_to_baud(value);
190         if (r > 0) {
191                 return r;
192         }
193         return 0;
194 }
195
196
197 /* parse_speeds - parse alternate baud rates */
198 static void parse_speeds(struct options *op, char *arg)
199 {
200         char *cp;
201
202         debug("entered parse_speeds\n");
203         for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {
204                 if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
205                         bb_error_msg_and_die("bad speed: %s", cp);
206                 if (op->numspeed > MAX_SPEED)
207                         bb_error_msg_and_die("too many alternate speeds");
208         }
209         debug("exiting parsespeeds\n");
210 }
211
212
213 /* parse_args - parse command-line arguments */
214 static void parse_args(int argc, char **argv, struct options *op)
215 {
216         char *ts;
217
218         op->flags = getopt32(argc, argv, opt_string,
219                 &(op->initstring), &fakehost, &(op->issue),
220                 &(op->login), &ts);
221         if (op->flags & F_INITSTRING) {
222                 const char *p = op->initstring;
223                 char *q;
224
225                 q = op->initstring = xstrdup(op->initstring);
226                 /* copy optarg into op->initstring decoding \ddd
227                    octal codes into chars */
228                 while (*p) {
229                         if (*p == '\\') {
230                                 p++;
231                                 *q++ = bb_process_escape_sequence(&p);
232                         } else {
233                                 *q++ = *p++;
234                         }
235                 }
236                 *q = '\0';
237         }
238         op->flags ^= F_ISSUE;           /* revert flag show /etc/issue */
239         if (op->flags & F_TIMEOUT) {
240                 op->timeout = xatoul_range(ts, 1, INT_MAX);
241         }
242         argv += optind;
243         argc -= optind;
244         debug("after getopt loop\n");
245         if (argc < 2)          /* check parameter count */
246                 bb_show_usage();
247
248         /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
249         if ('0' <= argv[0][0] && argv[0][0] <= '9') {
250                 /* a number first, assume it's a speed (BSD style) */
251                 parse_speeds(op, argv[0]);       /* baud rate(s) */
252                 op->tty = argv[1]; /* tty name */
253         } else {
254                 op->tty = argv[0];       /* tty name */
255                 parse_speeds(op, argv[1]); /* baud rate(s) */
256         }
257
258         if (argv[2])
259                 setenv("TERM", argv[2], 1);
260
261         debug("exiting parseargs\n");
262 }
263
264 static void xdup2(int srcfd, int dstfd, const char *tty)
265 {
266         if (dup2(srcfd, dstfd) == -1)
267                 bb_perror_msg_and_die("%s: dup", tty);
268 }
269
270 /* open_tty - set up tty as standard { input, output, error } */
271 static void open_tty(char *tty, struct termio *tp, int local)
272 {
273         int chdir_to_root = 0;
274
275         /* Set up new standard input, unless we are given an already opened port. */
276
277         if (strcmp(tty, "-")) {
278                 struct stat st;
279                 int fd;
280
281                 /* Sanity checks... */
282
283                 xchdir("/dev");
284                 chdir_to_root = 1;
285                 xstat(tty, &st);
286                 if ((st.st_mode & S_IFMT) != S_IFCHR)
287                         bb_error_msg_and_die("%s: not a character device", tty);
288
289                 /* Open the tty as standard input. */
290
291                 debug("open(2)\n");
292                 fd = xopen(tty, O_RDWR | O_NONBLOCK);
293                 if (fd) {
294                         xdup2(fd, 0, tty);
295                         close(fd);
296                 }
297         } else {
298                 /*
299                  * Standard input should already be connected to an open port. Make
300                  * sure it is open for read/write.
301                  */
302
303                 if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR)
304                         bb_error_msg_and_die("%s: not open for read/write", tty);
305         }
306
307         /* Replace current standard output/error fd's with new ones */
308         debug("duping\n");
309         xdup2(0, 1, tty);
310         xdup2(0, 2, tty);
311
312         /*
313          * The following ioctl will fail if stdin is not a tty, but also when
314          * there is noise on the modem control lines. In the latter case, the
315          * common course of action is (1) fix your cables (2) give the modem more
316          * time to properly reset after hanging up. SunOS users can achieve (2)
317          * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
318          * 5 seconds seems to be a good value.
319          */
320
321         if (ioctl(0, TCGETA, tp) < 0)
322                 bb_perror_msg_and_die("%s: ioctl(TCGETA)", tty);
323
324         /*
325          * It seems to be a terminal. Set proper protections and ownership. Mode
326          * 0622 is suitable for SYSV <4 because /bin/login does not change
327          * protections. SunOS 4 login will change the protections to 0620 (write
328          * access for group tty) after the login has succeeded.
329          */
330
331 #ifdef DEBIAN
332 #warning Debian /dev/vcs[a]NN hack is deprecated and will be removed
333         {
334                 /* tty to root.dialout 660 */
335                 struct group *gr;
336                 int id;
337
338                 gr = getgrnam("dialout");
339                 id = gr ? gr->gr_gid : 0;
340                 chown(tty, 0, id);
341                 chmod(tty, 0660);
342
343                 /* vcs,vcsa to root.sys 600 */
344                 if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) {
345                         char *vcs, *vcsa;
346
347                         vcs = xstrdup(tty);
348                         vcsa = xmalloc(strlen(tty) + 2);
349                         strcpy(vcs, "vcs");
350                         strcpy(vcs + 3, tty + 3);
351                         strcpy(vcsa, "vcsa");
352                         strcpy(vcsa + 4, tty + 3);
353
354                         id = (gr = getgrnam("sys")) ? gr->gr_gid : 0;
355                         chown(vcs, 0, id);
356                         chmod(vcs, 0600);
357                         chown(vcsa, 0, id);
358                         chmod(vcs, 0600);
359
360                         free(vcs);
361                         free(vcsa);
362                 }
363         }
364 #else
365         chown(tty, 0, 0);        /* root, sys */
366         chmod(tty, 0622);        /* crw--w--w- */
367 #endif
368         if (chdir_to_root)
369                 xchdir("/");
370 }
371
372 /* termio_init - initialize termio settings */
373 static void termio_init(struct termio *tp, int speed, struct options *op)
374 {
375         /*
376          * Initial termio settings: 8-bit characters, raw-mode, blocking i/o.
377          * Special characters are set after we have read the login name; all
378          * reads will be done in raw mode anyway. Errors will be dealt with
379          * later on.
380          */
381 #ifdef __linux__
382         /* flush input and output queues, important for modems! */
383         ioctl(0, TCFLSH, TCIOFLUSH);
384 #endif
385
386         tp->c_cflag = CS8 | HUPCL | CREAD | speed;
387         if (op->flags & F_LOCAL) {
388                 tp->c_cflag |= CLOCAL;
389         }
390
391         tp->c_iflag = tp->c_lflag = tp->c_line = 0;
392         tp->c_oflag = OPOST | ONLCR;
393         tp->c_cc[VMIN] = 1;
394         tp->c_cc[VTIME] = 0;
395
396         /* Optionally enable hardware flow control */
397
398 #ifdef  CRTSCTS
399         if (op->flags & F_RTSCTS)
400                 tp->c_cflag |= CRTSCTS;
401 #endif
402
403         ioctl(0, TCSETA, tp);
404
405         /* go to blocking input even in local mode */
406         fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK);
407
408         debug("term_io 2\n");
409 }
410
411 /* auto_baud - extract baud rate from modem status message */
412 static void auto_baud(char *buf, unsigned size_buf, struct termio *tp)
413 {
414         int speed;
415         int vmin;
416         unsigned iflag;
417         char *bp;
418         int nread;
419
420         /*
421          * This works only if the modem produces its status code AFTER raising
422          * the DCD line, and if the computer is fast enough to set the proper
423          * baud rate before the message has gone by. We expect a message of the
424          * following format:
425          *
426          * <junk><number><junk>
427          *
428          * The number is interpreted as the baud rate of the incoming call. If the
429          * modem does not tell us the baud rate within one second, we will keep
430          * using the current baud rate. It is advisable to enable BREAK
431          * processing (comma-separated list of baud rates) if the processing of
432          * modem status messages is enabled.
433          */
434
435         /*
436          * Use 7-bit characters, don't block if input queue is empty. Errors will
437          * be dealt with later on.
438          */
439
440         iflag = tp->c_iflag;
441         tp->c_iflag |= ISTRIP;          /* enable 8th-bit stripping */
442         vmin = tp->c_cc[VMIN];
443         tp->c_cc[VMIN] = 0;                     /* don't block if queue empty */
444         ioctl(0, TCSETA, tp);
445
446         /*
447          * Wait for a while, then read everything the modem has said so far and
448          * try to extract the speed of the dial-in call.
449          */
450
451         sleep(1);
452         nread = read(0, buf, size_buf - 1);
453         if (nread > 0) {
454                 buf[nread] = '\0';
455                 for (bp = buf; bp < buf + nread; bp++) {
456                         if (isascii(*bp) && isdigit(*bp)) {
457                                 speed = bcode(bp);
458                                 if (speed) {
459                                         tp->c_cflag &= ~CBAUD;
460                                         tp->c_cflag |= speed;
461                                 }
462                                 break;
463                         }
464                 }
465         }
466         /* Restore terminal settings. Errors will be dealt with later on. */
467
468         tp->c_iflag = iflag;
469         tp->c_cc[VMIN] = vmin;
470         ioctl(0, TCSETA, tp);
471 }
472
473 /* next_speed - select next baud rate */
474 static void next_speed(struct termio *tp, struct options *op)
475 {
476         static int baud_index = FIRST_SPEED;    /* current speed index */
477
478         baud_index = (baud_index + 1) % op->numspeed;
479         tp->c_cflag &= ~CBAUD;
480         tp->c_cflag |= op->speeds[baud_index];
481         ioctl(0, TCSETA, tp);
482 }
483
484
485 /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
486 static void do_prompt(struct options *op, struct termio *tp)
487 {
488 #ifdef ISSUE
489         print_login_issue(op->issue, op->tty);
490 #endif
491         print_login_prompt();
492 }
493
494 #ifdef HANDLE_ALLCAPS
495 /* caps_lock - string contains upper case without lower case */
496 /* returns 1 if true, 0 if false */
497 static int caps_lock(const char *s)
498 {
499         while (*s)
500                 if (islower(*s++))
501                         return 0;
502         return 1;
503 }
504 #endif
505
506 /* get_logname - get user name, establish parity, speed, erase, kill, eol */
507 /* return NULL on failure, logname on success */
508 static char *get_logname(char *logname, unsigned size_logname,
509                 struct options *op, struct chardata *cp, struct termio *tp)
510 {
511         char *bp;
512         char c;                         /* input character, full eight bits */
513         char ascval;                    /* low 7 bits of input character */
514         int bits;                       /* # of "1" bits per character */
515         int mask;                       /* mask with 1 bit up */
516         static const char erase[][3] = {    /* backspace-space-backspace */
517                 "\010\040\010",                 /* space parity */
518                 "\010\040\010",                 /* odd parity */
519                 "\210\240\210",                 /* even parity */
520                 "\210\240\210",                 /* no parity */
521         };
522
523         /* Initialize kill, erase, parity etc. (also after switching speeds). */
524
525         *cp = init_chardata;
526
527         /* Flush pending input (esp. after parsing or switching the baud rate). */
528
529         sleep(1);
530         ioctl(0, TCFLSH, TCIFLUSH);
531
532         /* Prompt for and read a login name. */
533
534         logname[0] = '\0';
535         while (!logname[0]) {
536
537                 /* Write issue file and prompt, with "parity" bit == 0. */
538
539                 do_prompt(op, tp);
540
541                 /* Read name, watch for break, parity, erase, kill, end-of-line. */
542
543                 bp = logname;
544                 cp->eol = '\0';
545                 while (cp->eol == '\0') {
546
547                         /* Do not report trivial EINTR/EIO errors. */
548                         if (read(0, &c, 1) < 1) {
549                                 if (errno == EINTR || errno == EIO)
550                                         exit(0);
551                                 bb_perror_msg_and_die("%s: read", op->tty);
552                         }
553
554                         /* Do BREAK handling elsewhere. */
555                         if (c == '\0' && op->numspeed > 1)
556                                 return NULL;
557
558                         /* Do parity bit handling. */
559                         ascval = c & 0177;
560                         if (c != ascval) {       /* "parity" bit on ? */
561                                 bits = 1;
562                                 mask = 1;
563                                 while (mask & 0177) {
564                                         if (mask & ascval)
565                                                 bits++; /* count "1" bits */
566                                         mask <<= 1;
567                                 }
568                                 /* ... |= 2 - even, 1 - odd */
569                                 cp->parity |= 2 - (bits & 1);
570                         }
571
572                         /* Do erase, kill and end-of-line processing. */
573                         switch (ascval) {
574                         case CR:
575                         case NL:
576                                 *bp = '\0';             /* terminate logname */
577                                 cp->eol = ascval;       /* set end-of-line char */
578                                 break;
579                         case BS:
580                         case DEL:
581                         case '#':
582                                 cp->erase = ascval;     /* set erase character */
583                                 if (bp > logname) {
584                                         write(1, erase[cp->parity], 3);
585                                         bp--;
586                                 }
587                                 break;
588                         case CTL('U'):
589                         case '@':
590                                 cp->kill = ascval;      /* set kill character */
591                                 while (bp > logname) {
592                                         write(1, erase[cp->parity], 3);
593                                         bp--;
594                                 }
595                                 break;
596                         case CTL('D'):
597                                 exit(0);
598                         default:
599                                 if (!isascii(ascval) || !isprint(ascval)) {
600                                         /* ignore garbage characters */
601                                 } else if (bp - logname >= size_logname - 1) {
602                                         bb_error_msg_and_die("%s: input overrun", op->tty);
603                                 } else {
604                                         write(1, &c, 1); /* echo the character */
605                                         *bp++ = ascval; /* and store it */
606                                 }
607                                 break;
608                         }
609                 }
610         }
611         /* Handle names with upper case and no lower case. */
612
613 #ifdef HANDLE_ALLCAPS
614         cp->capslock = caps_lock(logname);
615         if (cp->capslock) {
616                 for (bp = logname; *bp; bp++)
617                         if (isupper(*bp))
618                                 *bp = tolower(*bp);     /* map name to lower case */
619         }
620 #endif
621         return logname;
622 }
623
624 /* termio_final - set the final tty mode bits */
625 static void termio_final(struct options *op, struct termio *tp, struct chardata *cp)
626 {
627         /* General terminal-independent stuff. */
628
629         tp->c_iflag |= IXON | IXOFF;    /* 2-way flow control */
630         tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
631         /* no longer| ECHOCTL | ECHOPRT */
632         tp->c_oflag |= OPOST;
633         /* tp->c_cflag = 0; */
634         tp->c_cc[VINTR] = DEF_INTR;     /* default interrupt */
635         tp->c_cc[VQUIT] = DEF_QUIT;     /* default quit */
636         tp->c_cc[VEOF] = DEF_EOF;       /* default EOF character */
637         tp->c_cc[VEOL] = DEF_EOL;
638         tp->c_cc[VSWTC] = DEF_SWITCH;   /* default switch character */
639
640         /* Account for special characters seen in input. */
641
642         if (cp->eol == CR) {
643                 tp->c_iflag |= ICRNL;   /* map CR in input to NL */
644                 tp->c_oflag |= ONLCR;   /* map NL in output to CR-NL */
645         }
646         tp->c_cc[VERASE] = cp->erase;   /* set erase character */
647         tp->c_cc[VKILL] = cp->kill;     /* set kill character */
648
649         /* Account for the presence or absence of parity bits in input. */
650
651         switch (cp->parity) {
652         case 0:                                 /* space (always 0) parity */
653                 break;
654         case 1:                                 /* odd parity */
655                 tp->c_cflag |= PARODD;
656                 /* FALLTHROUGH */
657         case 2:                                 /* even parity */
658                 tp->c_cflag |= PARENB;
659                 tp->c_iflag |= INPCK | ISTRIP;
660                 /* FALLTHROUGH */
661         case (1 | 2):                           /* no parity bit */
662                 tp->c_cflag &= ~CSIZE;
663                 tp->c_cflag |= CS7;
664                 break;
665         }
666         /* Account for upper case without lower case. */
667
668 #ifdef HANDLE_ALLCAPS
669         if (cp->capslock) {
670                 tp->c_iflag |= IUCLC;
671                 tp->c_lflag |= XCASE;
672                 tp->c_oflag |= OLCUC;
673         }
674 #endif
675         /* Optionally enable hardware flow control */
676
677 #ifdef  CRTSCTS
678         if (op->flags & F_RTSCTS)
679                 tp->c_cflag |= CRTSCTS;
680 #endif
681
682         /* Finally, make the new settings effective */
683
684         if (ioctl(0, TCSETA, tp) < 0)
685                 bb_perror_msg_and_die("%s: ioctl(TCSETA)", op->tty);
686 }
687
688
689 #ifdef  SYSV_STYLE
690 #ifdef CONFIG_FEATURE_UTMP
691 /* update_utmp - update our utmp entry */
692 static void update_utmp(char *line)
693 {
694         struct utmp ut;
695         struct utmp *utp;
696         time_t t;
697         int mypid = getpid();
698 #if ! (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1))
699         struct flock lock;
700 #endif
701
702         /*
703          * The utmp file holds miscellaneous information about things started by
704          * /sbin/init and other system-related events. Our purpose is to update
705          * the utmp entry for the current process, in particular the process type
706          * and the tty line we are listening to. Return successfully only if the
707          * utmp file can be opened for update, and if we are able to find our
708          * entry in the utmp file.
709          */
710         if (access(_PATH_UTMP, R_OK|W_OK) == -1) {
711                 close(creat(_PATH_UTMP, 0664));
712         }
713         utmpname(_PATH_UTMP);
714         setutent();
715         while ((utp = getutent())
716                    && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid))
717                 /* nothing */;
718
719         if (utp) {
720                 memcpy(&ut, utp, sizeof(ut));
721         } else {
722                 /* some inits don't initialize utmp... */
723                 memset(&ut, 0, sizeof(ut));
724                 safe_strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
725         }
726         /* endutent(); */
727
728         strcpy(ut.ut_user, "LOGIN");
729         safe_strncpy(ut.ut_line, line, sizeof(ut.ut_line));
730         if (fakehost)
731                 safe_strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
732         time(&t);
733         ut.ut_time = t;
734         ut.ut_type = LOGIN_PROCESS;
735         ut.ut_pid = mypid;
736
737         pututline(&ut);
738         endutent();
739
740 #ifdef CONFIG_FEATURE_WTMP
741         if (access(bb_path_wtmp_file, R_OK|W_OK) == -1)
742                 close(creat(bb_path_wtmp_file, 0664));
743         updwtmp(bb_path_wtmp_file, &ut);
744 #endif
745 }
746
747 #endif /* CONFIG_FEATURE_UTMP */
748 #endif /* SYSV_STYLE */
749
750
751 int getty_main(int argc, char **argv)
752 {
753         int nullfd;
754         char *logname = NULL;           /* login name, given to /bin/login */
755         // TODO: we can merge these into "struct local"
756         // (will reduce parameter passing)
757         struct chardata chardata;       /* set by get_logname() */
758         struct termio termio;           /* terminal mode bits */
759         struct options options = {
760                 0,                      /* show /etc/issue (SYSV_STYLE) */
761                 0,                      /* no timeout */
762                 _PATH_LOGIN,            /* default login program */
763                 "tty1",                 /* default tty line */
764                 "",                     /* modem init string */
765 #ifdef ISSUE
766                 ISSUE,                  /* default issue file */
767 #else
768                 NULL,
769 #endif
770                 0,                      /* no baud rates known yet */
771         };
772
773         /* Already too late because of theoretical
774          * possibility of getty --help somehow triggered
775          * inadvertently before we reach this. Oh well. */
776         close(0);
777         close(1);
778         close(2);
779         logmode = LOGMODE_NONE;
780 #ifdef __linux__
781         setsid();
782 #endif
783         /* Was "/dev/console". Why should we spam *system console*
784          * if there is a problem with getty on /dev/ttyS15?... */
785         nullfd = xopen(bb_dev_null, O_RDWR);
786         dup2(nullfd, 0);
787         dup2(nullfd, 1);
788         dup2(nullfd, 2);
789         if (nullfd > 2)
790                 close(nullfd);
791         /* We want special flavor of error_msg_and_die */
792         die_sleep = 10;
793         msg_eol = "\r\n";
794         openlog(applet_name, LOG_PID, LOG_AUTH);
795         logmode = LOGMODE_BOTH;
796
797 #ifdef DEBUGGING
798         dbf = xfopen(DEBUGTERM, "w");
799
800         {
801                 int i;
802
803                 for (i = 1; i < argc; i++) {
804                         debug(argv[i]);
805                         debug("\n");
806                 }
807         }
808 #endif
809
810         /* Parse command-line arguments. */
811         parse_args(argc, argv, &options);
812
813 #ifdef SYSV_STYLE
814 #ifdef CONFIG_FEATURE_UTMP
815         /* Update the utmp file. */
816         update_utmp(options.tty);
817 #endif
818 #endif
819
820         debug("calling open_tty\n");
821         /* Open the tty as standard { input, output, error }. */
822         open_tty(options.tty, &termio, options.flags & F_LOCAL);
823
824 #ifdef __linux__
825         {
826                 int iv;
827
828                 iv = getpid();
829                 ioctl(0, TIOCSPGRP, &iv);
830         }
831 #endif
832         /* Initialize the termio settings (raw mode, eight-bit, blocking i/o). */
833         debug("calling termio_init\n");
834         termio_init(&termio, options.speeds[FIRST_SPEED], &options);
835
836         /* write the modem init string and DON'T flush the buffers */
837         if (options.flags & F_INITSTRING) {
838                 debug("writing init string\n");
839                 write(1, options.initstring, strlen(options.initstring));
840         }
841
842         if (!(options.flags & F_LOCAL)) {
843                 /* go to blocking write mode unless -L is specified */
844                 fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK);
845         }
846
847         /* Optionally detect the baud rate from the modem status message. */
848         debug("before autobaud\n");
849         if (options.flags & F_PARSE)
850                 auto_baud(bb_common_bufsiz1, sizeof(bb_common_bufsiz1), &termio);
851
852         /* Set the optional timer. */
853         if (options.timeout)
854                 alarm(options.timeout);
855
856         /* optionally wait for CR or LF before writing /etc/issue */
857         if (options.flags & F_WAITCRLF) {
858                 char ch;
859
860                 debug("waiting for cr-lf\n");
861                 while (read(0, &ch, 1) == 1) {
862                         ch &= 0x7f;                     /* strip "parity bit" */
863 #ifdef DEBUGGING
864                         fprintf(dbf, "read %c\n", ch);
865 #endif
866                         if (ch == '\n' || ch == '\r')
867                                 break;
868                 }
869         }
870
871         chardata = init_chardata;
872         if (!(options.flags & F_NOPROMPT)) {
873                 /* Read the login name. */
874                 debug("reading login name\n");
875                 logname = get_logname(bb_common_bufsiz1, sizeof(bb_common_bufsiz1),
876                                 &options, &chardata, &termio);
877                 while (logname == NULL)
878                         next_speed(&termio, &options);
879         }
880
881         /* Disable timer. */
882
883         if (options.timeout)
884                 alarm(0);
885
886         /* Finalize the termio settings. */
887
888         termio_final(&options, &termio, &chardata);
889
890         /* Now the newline character should be properly written. */
891
892         write(1, "\n", 1);
893
894         /* Let the login program take care of password validation. */
895
896         execl(options.login, options.login, "--", logname, (char *) 0);
897         bb_error_msg_and_die("%s: can't exec %s", options.tty, options.login);
898 }