blob: 2144c95ffd29cd2603d1ba4b2977cc6a98abc562 [file] [log] [blame]
Robert Griebl1fca5582002-06-04 20:45:46 +00001/* 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 -f 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*/
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <unistd.h>
20#include <string.h>
21#include <sys/ioctl.h>
22#include <errno.h>
23#include <sys/stat.h>
24#include <sys/signal.h>
25#include <fcntl.h>
26#include <stdarg.h>
27#include <ctype.h>
28#include <utmp.h>
29#include <getopt.h>
30#include <termios.h>
31#include "busybox.h"
32
33#define _PATH_LOGIN "/bin/login"
34
35#ifdef linux
36#include <sys/param.h>
37#define USE_SYSLOG
38#endif
39
40extern void updwtmp(const char *filename, const struct utmp *ut);
41
42 /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */
43#ifdef USE_SYSLOG
44#include <syslog.h>
45#endif
46
47
48 /*
49 * Some heuristics to find out what environment we are in: if it is not
50 * System V, assume it is SunOS 4.
51 */
52
53#ifdef LOGIN_PROCESS /* defined in System V utmp.h */
54#define SYSV_STYLE /* select System V style getty */
55#endif
56
57 /*
58 * Things you may want to modify.
59 *
60 * If ISSUE is not defined, agetty will never display the contents of the
61 * /etc/issue file. You will not want to spit out large "issue" files at the
62 * wrong baud rate. Relevant for System V only.
63 *
64 * You may disagree with the default line-editing etc. characters defined
65 * below. Note, however, that DEL cannot be used for interrupt generation
66 * and for line editing at the same time.
67 */
68
69#ifdef SYSV_STYLE
70#define ISSUE "/etc/issue" /* displayed before the login prompt */
71#include <sys/utsname.h>
72#include <time.h>
73#endif
74
75#define LOGIN " login: " /* login prompt */
76
77/* Some shorthands for control characters. */
78
79#define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */
80#define CR CTL('M') /* carriage return */
81#define NL CTL('J') /* line feed */
82#define BS CTL('H') /* back space */
83#define DEL CTL('?') /* delete */
84
85/* Defaults for line-editing etc. characters; you may want to change this. */
86
87#define DEF_ERASE DEL /* default erase character */
88#define DEF_INTR CTL('C') /* default interrupt character */
89#define DEF_QUIT CTL('\\') /* default quit char */
90#define DEF_KILL CTL('U') /* default kill char */
91#define DEF_EOF CTL('D') /* default EOF char */
92#define DEF_EOL 0
93#define DEF_SWITCH 0 /* default switch char */
94
95 /*
96 * SunOS 4.1.1 termio is broken. We must use the termios stuff instead,
97 * because the termio -> termios translation does not clear the termios
98 * CIBAUD bits. Therefore, the tty driver would sometimes report that input
99 * baud rate != output baud rate. I did not notice that problem with SunOS
100 * 4.1. We will use termios where available, and termio otherwise.
101 */
102
103/* linux 0.12 termio is broken too, if we use it c_cc[VERASE] isn't set
104 properly, but all is well if we use termios?! */
105
106#ifdef TCGETS
107#undef TCGETA
108#undef TCSETA
109#undef TCSETAW
110#define termio termios
111#define TCGETA TCGETS
112#define TCSETA TCSETS
113#define TCSETAW TCSETSW
114#endif
115
116 /*
117 * This program tries to not use the standard-i/o library. This keeps the
118 * executable small on systems that do not have shared libraries (System V
119 * Release <3).
120 */
121#ifndef BUFSIZ
122#define BUFSIZ 1024
123#endif
124
125 /*
126 * When multiple baud rates are specified on the command line, the first one
127 * we will try is the first one specified.
128 */
129
130#define FIRST_SPEED 0
131
132/* Storage for command-line options. */
133
134#define MAX_SPEED 10 /* max. nr. of baud rates */
135
136struct options {
137 int flags; /* toggle switches, see below */
138 int timeout; /* time-out period */
139 char *login; /* login program */
140 char *tty; /* name of tty */
141 char *initstring; /* modem init string */
142 char *issue; /* alternative issue file */
143 int numspeed; /* number of baud rates to try */
144 int speeds[MAX_SPEED]; /* baud rates to be tried */
145};
146
147#define F_PARSE (1<<0) /* process modem status messages */
148#define F_ISSUE (1<<1) /* display /etc/issue */
149#define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */
150#define F_LOCAL (1<<3) /* force local */
151#define F_INITSTRING (1<<4) /* initstring is set */
152#define F_WAITCRLF (1<<5) /* wait for CR or LF */
153#define F_CUSTISSUE (1<<6) /* give alternative issue file */
154#define F_NOPROMPT (1<<7) /* don't ask for login name! */
155
156/* Storage for things detected while the login name was read. */
157
158struct chardata {
159 int erase; /* erase character */
160 int kill; /* kill character */
161 int eol; /* end-of-line character */
162 int parity; /* what parity did we see */
163 int capslock; /* upper case without lower case */
164};
165
166/* Initial values for the above. */
167
168struct chardata init_chardata = {
169 DEF_ERASE, /* default erase character */
170 DEF_KILL, /* default kill character */
171 13, /* default eol char */
172 0, /* space parity */
173 0, /* no capslock */
174};
175
176struct Speedtab {
177 long speed;
178 int code;
179};
180
181static struct Speedtab speedtab[] = {
182 {50, B50},
183 {75, B75},
184 {110, B110},
185 {134, B134},
186 {150, B150},
187 {200, B200},
188 {300, B300},
189 {600, B600},
190 {1200, B1200},
191 {1800, B1800},
192 {2400, B2400},
193 {4800, B4800},
194 {9600, B9600},
195#ifdef B19200
196 {19200, B19200},
197#endif
198#ifdef B38400
199 {38400, B38400},
200#endif
201#ifdef EXTA
202 {19200, EXTA},
203#endif
204#ifdef EXTB
205 {38400, EXTB},
206#endif
207#ifdef B57600
208 {57600, B57600},
209#endif
210#ifdef B115200
211 {115200, B115200},
212#endif
213#ifdef B230400
214 {230400, B230400},
215#endif
216 {0, 0},
217};
218
219static void parse_args(int argc, char **argv, struct options *op);
220static void parse_speeds(struct options *op, char *arg);
221static void update_utmp(char *line);
222static void open_tty(char *tty, struct termio *tp, int local);
223static void termio_init(struct termio *tp, int speed, struct options *op);
224static void auto_baud(struct termio *tp);
225static void do_prompt(struct options *op, struct termio *tp);
226static void next_speed(struct termio *tp, struct options *op);
227static char *get_logname(struct options *op, struct chardata *cp,
228
229 struct termio *tp);
230static void termio_final(struct options *op, struct termio *tp,
231
232 struct chardata *cp);
233static int caps_lock(const char *s);
234static int bcode(const char *s);
235static void error(const char *fmt, ...);
236
237/* The following is used for understandable diagnostics. */
238
239/* Fake hostname for ut_host specified on command line. */
240static char *fakehost = NULL;
241
242/* ... */
243#ifdef DEBUGGING
244#define debug(s) fprintf(dbf,s); fflush(dbf)
245#define DEBUGTERM "/dev/ttyp0"
246FILE *dbf;
247#else
248#define debug(s) /* nothing */
249#endif
250
251int getty_main(int argc, char **argv)
252{
253 char *logname = NULL; /* login name, given to /bin/login */
254 struct chardata chardata; /* set by get_logname() */
255 struct termio termio; /* terminal mode bits */
256 static struct options options = {
257 F_ISSUE, /* show /etc/issue (SYSV_STYLE) */
258 0, /* no timeout */
259 _PATH_LOGIN, /* default login program */
260 "tty1", /* default tty line */
261 "", /* modem init string */
262 ISSUE, /* default issue file */
263 0, /* no baud rates known yet */
264 };
265
266#ifdef DEBUGGING
267 dbf = xfopen(DEBUGTERM, "w");
268
269 {
270 int i;
271
272 for (i = 1; i < argc; i++) {
273 debug(argv[i]);
274 debug("\n");
275 }
276 }
277#endif
278
279 /* Parse command-line arguments. */
280
281 parse_args(argc, argv, &options);
282
283#ifdef __linux__
284 setsid();
285#endif
286
287 /* Update the utmp file. */
288
289#ifdef SYSV_STYLE
290 update_utmp(options.tty);
291#endif
292
293 debug("calling open_tty\n");
294 /* Open the tty as standard { input, output, error }. */
295 open_tty(options.tty, &termio, options.flags & F_LOCAL);
296
297#ifdef __linux__
298 {
299 int iv;
300
301 iv = getpid();
302 if (ioctl(0, TIOCSPGRP, &iv) < 0)
303 perror_msg("ioctl() TIOCSPGRP call failed");
304 }
305#endif
306 /* Initialize the termio settings (raw mode, eight-bit, blocking i/o). */
307 debug("calling termio_init\n");
308 termio_init(&termio, options.speeds[FIRST_SPEED], &options);
309
310 /* write the modem init string and DON'T flush the buffers */
311 if (options.flags & F_INITSTRING) {
312 debug("writing init string\n");
313 write(1, options.initstring, strlen(options.initstring));
314 }
315
316 if (!(options.flags & F_LOCAL)) {
317 /* go to blocking write mode unless -L is specified */
318 fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK);
319 }
320
321 /* Optionally detect the baud rate from the modem status message. */
322 debug("before autobaud\n");
323 if (options.flags & F_PARSE)
324 auto_baud(&termio);
325
326 /* Set the optional timer. */
327 if (options.timeout)
328 (void) alarm((unsigned) options.timeout);
329
330 /* optionally wait for CR or LF before writing /etc/issue */
331 if (options.flags & F_WAITCRLF) {
332 char ch;
333
334 debug("waiting for cr-lf\n");
335 while (read(0, &ch, 1) == 1) {
336 ch &= 0x7f; /* strip "parity bit" */
337#ifdef DEBUGGING
338 fprintf(dbf, "read %c\n", ch);
339#endif
340 if (ch == '\n' || ch == '\r')
341 break;
342 }
343 }
344
345 chardata = init_chardata;
346 if (!(options.flags & F_NOPROMPT)) {
347 /* Read the login name. */
348 debug("reading login name\n");
349 /* while ((logname = get_logname(&options, &chardata, &termio)) == 0) */
350 while ((logname = get_logname(&options, &chardata, &termio)) ==
351 NULL) next_speed(&termio, &options);
352 }
353
354 /* Disable timer. */
355
356 if (options.timeout)
357 (void) alarm(0);
358
359 /* Finalize the termio settings. */
360
361 termio_final(&options, &termio, &chardata);
362
363 /* Now the newline character should be properly written. */
364
365 (void) write(1, "\n", 1);
366
367 /* Let the login program take care of password validation. */
368
369 (void) execl(options.login, options.login, "--", logname, (char *) 0);
370 error("%s: can't exec %s: %m", options.tty, options.login);
371 return (0); /* quiet GCC */
372}
373
374/* parse-args - parse command-line arguments */
375
376static void parse_args(int argc, char **argv, struct options *op)
377{
378 extern char *optarg; /* getopt */
379 extern int optind; /* getopt */
380 int c;
381
382 while (isascii(c = getopt(argc, argv, "I:LH:f:hil:mt:wn"))) {
383 switch (c) {
384 case 'I':
385 if (!(op->initstring = strdup(optarg))) {
386 error("can't malloc initstring");
387 break;
388 }
389 {
390 char ch, *p, *q;
391 int i;
392
393 /* copy optarg into op->initstring decoding \ddd
394 octal codes into chars */
395 q = op->initstring;
396 p = optarg;
397 while (*p) {
398 if (*p == '\\') { /* know \\ means \ */
399 p++;
400 if (*p == '\\') {
401 ch = '\\';
402 p++;
403 } else { /* handle \000 - \177 */
404 ch = 0;
405 for (i = 1; i <= 3; i++) {
406 if (*p >= '0' && *p <= '7') {
407 ch <<= 3;
408 ch += *p - '0';
409 p++;
410 } else
411 break;
412 }
413 }
414 *q++ = ch;
415 } else {
416 *q++ = *p++;
417 }
418 }
419 *q = '\0';
420 }
421 op->flags |= F_INITSTRING;
422 break;
423
424 case 'L': /* force local */
425 op->flags |= F_LOCAL;
426 break;
427 case 'H': /* fake login host */
428 fakehost = optarg;
429 break;
430 case 'f': /* custom issue file */
431 op->flags |= F_CUSTISSUE;
432 op->issue = optarg;
433 break;
434 case 'h': /* enable h/w flow control */
435 op->flags |= F_RTSCTS;
436 break;
437 case 'i': /* do not show /etc/issue */
438 op->flags &= ~F_ISSUE;
439 break;
440 case 'l':
441 op->login = optarg; /* non-default login program */
442 break;
443 case 'm': /* parse modem status message */
444 op->flags |= F_PARSE;
445 break;
446 case 'n':
447 op->flags |= F_NOPROMPT;
448 break;
449 case 't': /* time out */
450 if ((op->timeout = atoi(optarg)) <= 0)
451 error("bad timeout value: %s", optarg);
452 break;
453 case 'w':
454 op->flags |= F_WAITCRLF;
455 break;
456 default:
457 show_usage();
458 }
459 }
460 debug("after getopt loop\n");
461 if (argc < optind + 2) /* check parameter count */
462 show_usage();
463
464 /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
465 if ('0' <= argv[optind][0] && argv[optind][0] <= '9') {
466 /* a number first, assume it's a speed (BSD style) */
467 parse_speeds(op, argv[optind++]); /* baud rate(s) */
468 op->tty = argv[optind]; /* tty name */
469 } else {
470 op->tty = argv[optind++]; /* tty name */
471 parse_speeds(op, argv[optind]); /* baud rate(s) */
472 }
473
474 optind++;
475 if (argc > optind && argv[optind])
476 setenv("TERM", argv[optind], 1);
477
478 debug("exiting parseargs\n");
479}
480
481/* parse_speeds - parse alternate baud rates */
482
483static void parse_speeds(struct options *op, char *arg)
484{
485 char *cp;
486
487 debug("entered parse_speeds\n");
488 for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {
489 if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
490 error("bad speed: %s", cp);
491 if (op->numspeed > MAX_SPEED)
492 error("too many alternate speeds");
493 }
494 debug("exiting parsespeeds\n");
495}
496
497#ifdef SYSV_STYLE
498
499/* update_utmp - update our utmp entry */
500static void update_utmp(char *line)
501{
502 struct utmp ut;
503 struct utmp *utp;
504 time_t t;
505 int mypid = getpid();
506#if ! (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1))
507 struct flock lock;
508#endif
509
510 /*
511 * The utmp file holds miscellaneous information about things started by
512 * /sbin/init and other system-related events. Our purpose is to update
513 * the utmp entry for the current process, in particular the process type
514 * and the tty line we are listening to. Return successfully only if the
515 * utmp file can be opened for update, and if we are able to find our
516 * entry in the utmp file.
517 */
518 utmpname(_PATH_UTMP);
519 setutent();
520 while ((utp = getutent())
521 && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid)) /* nothing */
522 ;
523
524 if (utp) {
525 memcpy(&ut, utp, sizeof(ut));
526 } else {
527 /* some inits don't initialize utmp... */
528 memset(&ut, 0, sizeof(ut));
529 strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
530 }
531 /*endutent(); */
532
533 strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user));
534 strncpy(ut.ut_line, line, sizeof(ut.ut_line));
535 if (fakehost)
536 strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
537 time(&t);
538 ut.ut_time = t;
539 ut.ut_type = LOGIN_PROCESS;
540 ut.ut_pid = mypid;
541
542 pututline(&ut);
543 endutent();
544
545 {
546 updwtmp(_PATH_WTMP, &ut);
547 }
548}
549
550#endif
551
552/* open_tty - set up tty as standard { input, output, error } */
553static void open_tty(char *tty, struct termio *tp, int local)
554{
555 /* Get rid of the present standard { output, error} if any. */
556
557 (void) close(1);
558 (void) close(2);
559 errno = 0; /* ignore above errors */
560
561 /* Set up new standard input, unless we are given an already opened port. */
562
563 if (strcmp(tty, "-")) {
564 struct stat st;
565
566 /* Sanity checks... */
567
568 if (chdir("/dev"))
569 error("/dev: chdir() failed: %m");
570 if (stat(tty, &st) < 0)
571 error("/dev/%s: %m", tty);
572 if ((st.st_mode & S_IFMT) != S_IFCHR)
573 error("/dev/%s: not a character device", tty);
574
575 /* Open the tty as standard input. */
576
577 (void) close(0);
578 errno = 0; /* ignore close(2) errors */
579
580 debug("open(2)\n");
581 if (open(tty, O_RDWR | O_NONBLOCK, 0) != 0)
582 error("/dev/%s: cannot open as standard input: %m", tty);
583
584 } else {
585
586 /*
587 * Standard input should already be connected to an open port. Make
588 * sure it is open for read/write.
589 */
590
591 if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR)
592 error("%s: not open for read/write", tty);
593 }
594
595 /* Set up standard output and standard error file descriptors. */
596 debug("duping\n");
597 if (dup(0) != 1 || dup(0) != 2) /* set up stdout and stderr */
598 error("%s: dup problem: %m", tty); /* we have a problem */
599
600 /*
601 * The following ioctl will fail if stdin is not a tty, but also when
602 * there is noise on the modem control lines. In the latter case, the
603 * common course of action is (1) fix your cables (2) give the modem more
604 * time to properly reset after hanging up. SunOS users can achieve (2)
605 * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
606 * 5 seconds seems to be a good value.
607 */
608
609 if (ioctl(0, TCGETA, tp) < 0)
610 error("%s: ioctl: %m", tty);
611
612 /*
613 * It seems to be a terminal. Set proper protections and ownership. Mode
614 * 0622 is suitable for SYSV <4 because /bin/login does not change
615 * protections. SunOS 4 login will change the protections to 0620 (write
616 * access for group tty) after the login has succeeded.
617 */
618
619#ifdef DEBIAN
620 {
621 /* tty to root.dialout 660 */
622 struct group *gr;
623 int id;
624
625 id = (gr = getgrnam("dialout")) ? gr->gr_gid : 0;
626 chown(tty, 0, id);
627 chmod(tty, 0660);
628
629 /* vcs,vcsa to root.sys 600 */
630 if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) {
631 char *vcs, *vcsa;
632
633 if (!(vcs = strdup(tty)))
634 error("Can't malloc for vcs");
635 if (!(vcsa = malloc(strlen(tty) + 2)))
636 error("Can't malloc for vcsa");
637 strcpy(vcs, "vcs");
638 strcpy(vcs + 3, tty + 3);
639 strcpy(vcsa, "vcsa");
640 strcpy(vcsa + 4, tty + 3);
641
642 id = (gr = getgrnam("sys")) ? gr->gr_gid : 0;
643 chown(vcs, 0, id);
644 chmod(vcs, 0600);
645 chown(vcsa, 0, id);
646 chmod(vcs, 0600);
647
648 free(vcs);
649 free(vcsa);
650 }
651 }
652#else
653 (void) chown(tty, 0, 0); /* root, sys */
654 (void) chmod(tty, 0622); /* crw--w--w- */
655 errno = 0; /* ignore above errors */
656#endif
657}
658
659/* termio_init - initialize termio settings */
660
661static void termio_init(struct termio *tp, int speed, struct options *op)
662{
663
664 /*
665 * Initial termio settings: 8-bit characters, raw-mode, blocking i/o.
666 * Special characters are set after we have read the login name; all
667 * reads will be done in raw mode anyway. Errors will be dealt with
668 * lateron.
669 */
670#ifdef __linux__
671 /* flush input and output queues, important for modems! */
672 (void) ioctl(0, TCFLSH, TCIOFLUSH);
673#endif
674
675 tp->c_cflag = CS8 | HUPCL | CREAD | speed;
676 if (op->flags & F_LOCAL) {
677 tp->c_cflag |= CLOCAL;
678 }
679
680 tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0;
681 tp->c_cc[VMIN] = 1;
682 tp->c_cc[VTIME] = 0;
683
684 /* Optionally enable hardware flow control */
685
686#ifdef CRTSCTS
687 if (op->flags & F_RTSCTS)
688 tp->c_cflag |= CRTSCTS;
689#endif
690
691 (void) ioctl(0, TCSETA, tp);
692
693 /* go to blocking input even in local mode */
694 fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK);
695
696 debug("term_io 2\n");
697}
698
699/* auto_baud - extract baud rate from modem status message */
700static void auto_baud(struct termio *tp)
701{
702 int speed;
703 int vmin;
704 unsigned iflag;
705 char buf[BUFSIZ];
706 char *bp;
707 int nread;
708
709 /*
710 * This works only if the modem produces its status code AFTER raising
711 * the DCD line, and if the computer is fast enough to set the proper
712 * baud rate before the message has gone by. We expect a message of the
713 * following format:
714 *
715 * <junk><number><junk>
716 *
717 * The number is interpreted as the baud rate of the incoming call. If the
718 * modem does not tell us the baud rate within one second, we will keep
719 * using the current baud rate. It is advisable to enable BREAK
720 * processing (comma-separated list of baud rates) if the processing of
721 * modem status messages is enabled.
722 */
723
724 /*
725 * Use 7-bit characters, don't block if input queue is empty. Errors will
726 * be dealt with lateron.
727 */
728
729 iflag = tp->c_iflag;
730 tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */
731 vmin = tp->c_cc[VMIN];
732 tp->c_cc[VMIN] = 0; /* don't block if queue empty */
733 (void) ioctl(0, TCSETA, tp);
734
735 /*
736 * Wait for a while, then read everything the modem has said so far and
737 * try to extract the speed of the dial-in call.
738 */
739
740 (void) sleep(1);
741 if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) {
742 buf[nread] = '\0';
743 for (bp = buf; bp < buf + nread; bp++) {
744 if (isascii(*bp) && isdigit(*bp)) {
745 if ((speed = bcode(bp))) {
746 tp->c_cflag &= ~CBAUD;
747 tp->c_cflag |= speed;
748 }
749 break;
750 }
751 }
752 }
753 /* Restore terminal settings. Errors will be dealt with lateron. */
754
755 tp->c_iflag = iflag;
756 tp->c_cc[VMIN] = vmin;
757 (void) ioctl(0, TCSETA, tp);
758}
759
760/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
761static void do_prompt(struct options *op, struct termio *tp)
762{
763#ifdef ISSUE
764 FILE *fd;
765 int oflag;
766 int c;
767 struct utsname uts;
768
769 (void) uname(&uts);
770#endif
771
772 (void) write(1, "\r\n", 2); /* start a new line */
773#ifdef ISSUE /* optional: show /etc/issue */
774 if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) {
775 oflag = tp->c_oflag; /* save current setting */
776 tp->c_oflag |= (ONLCR | OPOST); /* map NL in output to CR-NL */
777 (void) ioctl(0, TCSETAW, tp);
778
779
780 while ((c = getc(fd)) != EOF) {
781 if (c == '\\') {
782 c = getc(fd);
783
784 switch (c) {
785 case 's':
786 (void) printf("%s", uts.sysname);
787 break;
788
789 case 'n':
790 (void) printf("%s", uts.nodename);
791 break;
792
793 case 'r':
794 (void) printf("%s", uts.release);
795 break;
796
797 case 'v':
798 (void) printf("%s", uts.version);
799 break;
800
801 case 'm':
802 (void) printf("%s", uts.machine);
803 break;
804
805 case 'o':
806 {
807 char domainname[256];
808
809 getdomainname(domainname, sizeof(domainname));
810 domainname[sizeof(domainname) - 1] = '\0';
811 printf("%s", domainname);
812 }
813 break;
814
815 case 'd':
816 case 't':
817 {
818 char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu",
819 "Fri", "Sat"
820 };
821 char *month[] = { "Jan", "Feb", "Mar", "Apr", "May",
822 "Jun", "Jul", "Aug", "Sep", "Oct",
823 "Nov", "Dec"
824 };
825 time_t now;
826 struct tm *tm;
827
828 (void) time(&now);
829 tm = localtime(&now);
830
831 if (c == 'd')
832 (void) printf("%s %s %d %d",
833 weekday[tm->tm_wday],
834 month[tm->tm_mon], tm->tm_mday,
835 tm->tm_year <
836 70 ? tm->tm_year +
837 2000 : tm->tm_year + 1900);
838 else
839 (void) printf("%02d:%02d:%02d", tm->tm_hour,
840 tm->tm_min, tm->tm_sec);
841
842 break;
843 }
844
845 case 'l':
846 (void) printf("%s", op->tty);
847 break;
848
849 case 'b':
850 {
851 int i;
852
853 for (i = 0; speedtab[i].speed; i++) {
854 if (speedtab[i].code == (tp->c_cflag & CBAUD)) {
855 printf("%ld", speedtab[i].speed);
856 break;
857 }
858 }
859 break;
860 }
861 case 'u':
862 case 'U':
863 {
864 int users = 0;
865 struct utmp *ut;
866
867 setutent();
868 while ((ut = getutent()))
869 if (ut->ut_type == USER_PROCESS)
870 users++;
871 endutent();
872 printf("%d ", users);
873 if (c == 'U')
874 printf((users == 1) ? "user" : "users");
875 break;
876 }
877 default:
878 (void) putchar(c);
879 }
880 } else
881 (void) putchar(c);
882 }
883 fflush(stdout);
884
885 tp->c_oflag = oflag; /* restore settings */
886 (void) ioctl(0, TCSETAW, tp); /* wait till output is gone */
887 (void) fclose(fd);
888 }
889#endif
890#ifdef __linux__
891 {
892 char hn[MAXHOSTNAMELEN + 1];
893
894 (void) gethostname(hn, MAXHOSTNAMELEN);
895 write(1, hn, strlen(hn));
896 }
897#endif
898 (void) write(1, LOGIN, sizeof(LOGIN) - 1); /* always show login prompt */
899}
900
901/* next_speed - select next baud rate */
902static void next_speed(struct termio *tp, struct options *op)
903{
904 static int baud_index = FIRST_SPEED; /* current speed index */
905
906 baud_index = (baud_index + 1) % op->numspeed;
907 tp->c_cflag &= ~CBAUD;
908 tp->c_cflag |= op->speeds[baud_index];
909 (void) ioctl(0, TCSETA, tp);
910}
911
912/* get_logname - get user name, establish parity, speed, erase, kill, eol */
913/* return NULL on failure, logname on success */
914static char *get_logname(struct options *op, struct chardata *cp, struct termio *tp)
915{
916 static char logname[BUFSIZ];
917 char *bp;
918 char c; /* input character, full eight bits */
919 char ascval; /* low 7 bits of input character */
920 int bits; /* # of "1" bits per character */
921 int mask; /* mask with 1 bit up */
922 static char *erase[] = { /* backspace-space-backspace */
923 "\010\040\010", /* space parity */
924 "\010\040\010", /* odd parity */
925 "\210\240\210", /* even parity */
926 "\210\240\210", /* no parity */
927 };
928
929 /* Initialize kill, erase, parity etc. (also after switching speeds). */
930
931 *cp = init_chardata;
932
933 /* Flush pending input (esp. after parsing or switching the baud rate). */
934
935 (void) sleep(1);
936 (void) ioctl(0, TCFLSH, TCIFLUSH);
937
938 /* Prompt for and read a login name. */
939
940 for (*logname = 0; *logname == 0; /* void */ ) {
941
942 /* Write issue file and prompt, with "parity" bit == 0. */
943
944 do_prompt(op, tp);
945
946 /* Read name, watch for break, parity, erase, kill, end-of-line. */
947
948 for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) {
949
950 /* Do not report trivial EINTR/EIO errors. */
951
952 if (read(0, &c, 1) < 1) {
953 if (errno == EINTR || errno == EIO)
954 exit(0);
955 error("%s: read: %m", op->tty);
956 }
957 /* Do BREAK handling elsewhere. */
958
959 if ((c == 0) && op->numspeed > 1)
960 /* return (0); */
961 return NULL;
962
963 /* Do parity bit handling. */
964
965 if (c != (ascval = (c & 0177))) { /* "parity" bit on ? */
966 for (bits = 1, mask = 1; mask & 0177; mask <<= 1)
967 if (mask & ascval)
968 bits++; /* count "1" bits */
969 cp->parity |= ((bits & 1) ? 1 : 2);
970 }
971 /* Do erase, kill and end-of-line processing. */
972
973 switch (ascval) {
974 case CR:
975 case NL:
976 *bp = 0; /* terminate logname */
977 cp->eol = ascval; /* set end-of-line char */
978 break;
979 case BS:
980 case DEL:
981 case '#':
982 cp->erase = ascval; /* set erase character */
983 if (bp > logname) {
984 (void) write(1, erase[cp->parity], 3);
985 bp--;
986 }
987 break;
988 case CTL('U'):
989 case '@':
990 cp->kill = ascval; /* set kill character */
991 while (bp > logname) {
992 (void) write(1, erase[cp->parity], 3);
993 bp--;
994 }
995 break;
996 case CTL('D'):
997 exit(0);
998 default:
999 if (!isascii(ascval) || !isprint(ascval)) {
1000 /* ignore garbage characters */ ;
1001 } else if (bp - logname >= sizeof(logname) - 1) {
1002 error("%s: input overrun", op->tty);
1003 } else {
1004 (void) write(1, &c, 1); /* echo the character */
1005 *bp++ = ascval; /* and store it */
1006 }
1007 break;
1008 }
1009 }
1010 }
1011 /* Handle names with upper case and no lower case. */
1012
1013 if ((cp->capslock = caps_lock(logname))) {
1014 for (bp = logname; *bp; bp++)
1015 if (isupper(*bp))
1016 *bp = tolower(*bp); /* map name to lower case */
1017 }
1018 return (logname);
1019}
1020
1021/* termio_final - set the final tty mode bits */
1022static void termio_final(struct options *op, struct termio *tp, struct chardata *cp)
1023{
1024 /* General terminal-independent stuff. */
1025
1026 tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */
1027 tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
1028 /* no longer| ECHOCTL | ECHOPRT */
1029 tp->c_oflag |= OPOST;
1030 /* tp->c_cflag = 0; */
1031 tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */
1032 tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */
1033 tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */
1034 tp->c_cc[VEOL] = DEF_EOL;
1035#ifdef __linux__
1036 tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */
1037#else
1038 tp->c_cc[VSWTCH] = DEF_SWITCH; /* default switch character */
1039#endif
1040
1041 /* Account for special characters seen in input. */
1042
1043 if (cp->eol == CR) {
1044 tp->c_iflag |= ICRNL; /* map CR in input to NL */
1045 tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */
1046 }
1047 tp->c_cc[VERASE] = cp->erase; /* set erase character */
1048 tp->c_cc[VKILL] = cp->kill; /* set kill character */
1049
1050 /* Account for the presence or absence of parity bits in input. */
1051
1052 switch (cp->parity) {
1053 case 0: /* space (always 0) parity */
1054 break;
1055 case 1: /* odd parity */
1056 tp->c_cflag |= PARODD;
1057 /* FALLTHROUGH */
1058 case 2: /* even parity */
1059 tp->c_cflag |= PARENB;
1060 tp->c_iflag |= INPCK | ISTRIP;
1061 /* FALLTHROUGH */
1062 case (1 | 2): /* no parity bit */
1063 tp->c_cflag &= ~CSIZE;
1064 tp->c_cflag |= CS7;
1065 break;
1066 }
1067 /* Account for upper case without lower case. */
1068
1069 if (cp->capslock) {
1070 tp->c_iflag |= IUCLC;
1071 tp->c_lflag |= XCASE;
1072 tp->c_oflag |= OLCUC;
1073 }
1074 /* Optionally enable hardware flow control */
1075
1076#ifdef CRTSCTS
1077 if (op->flags & F_RTSCTS)
1078 tp->c_cflag |= CRTSCTS;
1079#endif
1080
1081 /* Finally, make the new settings effective */
1082
1083 if (ioctl(0, TCSETA, tp) < 0)
1084 error("%s: ioctl: TCSETA: %m", op->tty);
1085}
1086
1087/* caps_lock - string contains upper case without lower case */
1088/* returns 1 if true, 0 if false */
1089static int caps_lock(const char *s)
1090{
1091 int capslock;
1092
1093 for (capslock = 0; *s; s++) {
1094 if (islower(*s))
1095 return (0);
1096 if (capslock == 0)
1097 capslock = isupper(*s);
1098 }
1099 return (capslock);
1100}
1101
1102/* bcode - convert speed string to speed code; return 0 on failure */
1103static int bcode(const char *s)
1104{
1105 struct Speedtab *sp;
1106 long speed = atol(s);
1107
1108 for (sp = speedtab; sp->speed; sp++)
1109 if (sp->speed == speed)
1110 return (sp->code);
1111 return (0);
1112}
1113
1114/* error - report errors to console or syslog; only understands %s and %m */
1115
1116#define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2)
1117
1118/*
1119 * output error messages
1120 */
1121static void error(const char *fmt, ...)
1122{
1123 va_list va_alist;
1124 char buf[256], *bp;
1125
1126#ifndef USE_SYSLOG
1127 int fd;
1128#endif
1129
1130#ifdef USE_SYSLOG
1131 buf[0] = '\0';
1132 bp = buf;
1133#else
1134 strncpy(buf, applet_name, 256);
1135 strncat(buf, ": ", 256);
1136 buf[255] = 0;
1137 bp = buf + strlen(buf);
1138#endif
1139
1140 va_start(va_alist, fmt);
1141 vsnprintf(bp, 256 - strlen(buf), fmt, va_alist);
1142 buf[255] = 0;
1143 va_end(va_alist);
1144
1145#ifdef USE_SYSLOG
1146 syslog_msg(LOG_AUTH, LOG_ERR, buf);
1147#else
1148 strncat(bp, "\r\n", 256 - strlen(buf));
1149 buf[255] = 0;
1150 if ((fd = open("/dev/console", 1)) >= 0) {
1151 write(fd, buf, strlen(buf));
1152 close(fd);
1153 }
1154#endif
1155 (void) sleep((unsigned) 10); /* be kind to init(8) */
1156 exit(1);
1157}