aboutsummaryrefslogtreecommitdiff
path: root/getty.c
diff options
context:
space:
mode:
authorandersen <andersen@69ca8d6d-28ef-0310-b511-8ec308f3f277>2001-08-21 16:18:59 +0000
committerandersen <andersen@69ca8d6d-28ef-0310-b511-8ec308f3f277>2001-08-21 16:18:59 +0000
commit6e33165982a8ba691371185cb898643ec9e78ba6 (patch)
tree9b048f58d1c11600df69323327124425a2f78f63 /getty.c
parentf55eed1a6ff910575e85e3bfa9cbae62039f8901 (diff)
downloadbusybox-w32-6e33165982a8ba691371185cb898643ec9e78ba6.tar.gz
busybox-w32-6e33165982a8ba691371185cb898643ec9e78ba6.tar.bz2
busybox-w32-6e33165982a8ba691371185cb898643ec9e78ba6.zip
Initial merge of all tinylogin applets that do not require crypt.
There is some optimization that can be done to better use libbb in these applets. There is also redundancy between stty and getty which could be eliminated. -Erik git-svn-id: svn://busybox.net/trunk/busybox@3318 69ca8d6d-28ef-0310-b511-8ec308f3f277
Diffstat (limited to 'getty.c')
-rw-r--r--getty.c1230
1 files changed, 1230 insertions, 0 deletions
diff --git a/getty.c b/getty.c
new file mode 100644
index 000000000..a5a0dc717
--- /dev/null
+++ b/getty.c
@@ -0,0 +1,1230 @@
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 -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
219void parse_args(int argc, char **argv, struct options *op);
220void parse_speeds(struct options *op, char *arg);
221void update_utmp(char *line);
222void open_tty(char *tty, struct termio *tp, int local);
223void termio_init(struct termio *tp, int speed, struct options *op);
224void auto_baud(struct termio *tp);
225void do_prompt(struct options *op, struct termio *tp);
226void next_speed(struct termio *tp, struct options *op);
227char *get_logname(struct options *op, struct chardata *cp,
228
229 struct termio *tp);
230void termio_final(struct options *op, struct termio *tp,
231
232 struct chardata *cp);
233int caps_lock(char *s);
234int bcode(char *s);
235static void error(const char *fmt, ...);
236
237/* The following is used for understandable diagnostics. */
238
239char *progname;
240
241/* Fake hostname for ut_host specified on command line. */
242char *fakehost = NULL;
243
244/* ... */
245#ifdef DEBUGGING
246#define debug(s) fprintf(dbf,s); fflush(dbf)
247#define DEBUGTERM "/dev/ttyp0"
248FILE *dbf;
249#else
250#define debug(s) /* nothing */
251#endif
252
253int getty_main(int argc, char **argv)
254{
255 char *logname = NULL; /* login name, given to /bin/login */
256 struct chardata chardata; /* set by get_logname() */
257 struct termio termio; /* terminal mode bits */
258 static struct options options = {
259 F_ISSUE, /* show /etc/issue (SYSV_STYLE) */
260 0, /* no timeout */
261 _PATH_LOGIN, /* default login program */
262 "tty1", /* default tty line */
263 "", /* modem init string */
264 ISSUE, /* default issue file */
265 0, /* no baud rates known yet */
266 };
267
268 /* The BSD-style init command passes us a useless process name. */
269
270#ifdef SYSV_STYLE
271 progname = argv[0];
272#else
273 progname = "getty";
274#endif
275
276#ifdef DEBUGGING
277 dbf = xfopen(DEBUGTERM, "w");
278
279 {
280 int i;
281
282 for (i = 1; i < argc; i++) {
283 debug(argv[i]);
284 debug("\n");
285 }
286 }
287#endif
288
289 /* Parse command-line arguments. */
290
291 parse_args(argc, argv, &options);
292
293#ifdef __linux__
294 setsid();
295#endif
296
297 /* Update the utmp file. */
298
299#ifdef SYSV_STYLE
300 update_utmp(options.tty);
301#endif
302
303 debug("calling open_tty\n");
304 /* Open the tty as standard { input, output, error }. */
305 open_tty(options.tty, &termio, options.flags & F_LOCAL);
306
307#ifdef __linux__
308 {
309 int iv;
310
311 iv = getpid();
312 if (ioctl(0, TIOCSPGRP, &iv) < 0)
313 perror_msg("ioctl() TIOCSPGRP call failed");
314 }
315#endif
316 /* Initialize the termio settings (raw mode, eight-bit, blocking i/o). */
317 debug("calling termio_init\n");
318 termio_init(&termio, options.speeds[FIRST_SPEED], &options);
319
320 /* write the modem init string and DON'T flush the buffers */
321 if (options.flags & F_INITSTRING) {
322 debug("writing init string\n");
323 write(1, options.initstring, strlen(options.initstring));
324 }
325
326 if (!(options.flags & F_LOCAL)) {
327 /* go to blocking write mode unless -L is specified */
328 fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK);
329 }
330
331 /* Optionally detect the baud rate from the modem status message. */
332 debug("before autobaud\n");
333 if (options.flags & F_PARSE)
334 auto_baud(&termio);
335
336 /* Set the optional timer. */
337 if (options.timeout)
338 (void) alarm((unsigned) options.timeout);
339
340 /* optionally wait for CR or LF before writing /etc/issue */
341 if (options.flags & F_WAITCRLF) {
342 char ch;
343
344 debug("waiting for cr-lf\n");
345 while (read(0, &ch, 1) == 1) {
346 ch &= 0x7f; /* strip "parity bit" */
347#ifdef DEBUGGING
348 fprintf(dbf, "read %c\n", ch);
349#endif
350 if (ch == '\n' || ch == '\r')
351 break;
352 }
353 }
354
355 chardata = init_chardata;
356 if (!(options.flags & F_NOPROMPT)) {
357 /* Read the login name. */
358 debug("reading login name\n");
359 /* while ((logname = get_logname(&options, &chardata, &termio)) == 0) */
360 while ((logname = get_logname(&options, &chardata, &termio)) ==
361 NULL) next_speed(&termio, &options);
362 }
363
364 /* Disable timer. */
365
366 if (options.timeout)
367 (void) alarm(0);
368
369 /* Finalize the termio settings. */
370
371 termio_final(&options, &termio, &chardata);
372
373 /* Now the newline character should be properly written. */
374
375 (void) write(1, "\n", 1);
376
377 /* Let the login program take care of password validation. */
378
379 (void) execl(options.login, options.login, "--", logname, (char *) 0);
380 error("%s: can't exec %s: %m", options.tty, options.login);
381 return (0); /* quiet GCC */
382}
383
384/* parse-args - parse command-line arguments */
385
386void parse_args(argc, argv, op)
387int argc;
388char **argv;
389struct options *op;
390{
391 extern char *optarg; /* getopt */
392 extern int optind; /* getopt */
393 int c;
394
395 while (isascii(c = getopt(argc, argv, "I:LH:f:hil:mt:wn"))) {
396 switch (c) {
397 case 'I':
398 if (!(op->initstring = malloc(strlen(optarg)))) {
399 error("can't malloc initstring");
400 break;
401 }
402 {
403 char ch, *p, *q;
404 int i;
405
406 /* copy optarg into op->initstring decoding \ddd
407 octal codes into chars */
408 q = op->initstring;
409 p = optarg;
410 while (*p) {
411 if (*p == '\\') { /* know \\ means \ */
412 p++;
413 if (*p == '\\') {
414 ch = '\\';
415 p++;
416 } else { /* handle \000 - \177 */
417 ch = 0;
418 for (i = 1; i <= 3; i++) {
419 if (*p >= '0' && *p <= '7') {
420 ch <<= 3;
421 ch += *p - '0';
422 p++;
423 } else
424 break;
425 }
426 }
427 *q++ = ch;
428 } else {
429 *q++ = *p++;
430 }
431 }
432 *q = '\0';
433 }
434 op->flags |= F_INITSTRING;
435 break;
436
437 case 'L': /* force local */
438 op->flags |= F_LOCAL;
439 break;
440 case 'H': /* fake login host */
441 fakehost = optarg;
442 break;
443 case 'f': /* custom issue file */
444 op->flags |= F_CUSTISSUE;
445 op->issue = optarg;
446 break;
447 case 'h': /* enable h/w flow control */
448 op->flags |= F_RTSCTS;
449 break;
450 case 'i': /* do not show /etc/issue */
451 op->flags &= ~F_ISSUE;
452 break;
453 case 'l':
454 op->login = optarg; /* non-default login program */
455 break;
456 case 'm': /* parse modem status message */
457 op->flags |= F_PARSE;
458 break;
459 case 'n':
460 op->flags |= F_NOPROMPT;
461 break;
462 case 't': /* time out */
463 if ((op->timeout = atoi(optarg)) <= 0)
464 error("bad timeout value: %s", optarg);
465 break;
466 case 'w':
467 op->flags |= F_WAITCRLF;
468 break;
469 default:
470 show_usage();
471 }
472 }
473 debug("after getopt loop\n");
474 if (argc < optind + 2) /* check parameter count */
475 show_usage();
476
477 /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
478 if ('0' <= argv[optind][0] && argv[optind][0] <= '9') {
479 /* a number first, assume it's a speed (BSD style) */
480 parse_speeds(op, argv[optind++]); /* baud rate(s) */
481 op->tty = argv[optind]; /* tty name */
482 } else {
483 op->tty = argv[optind++]; /* tty name */
484 parse_speeds(op, argv[optind]); /* baud rate(s) */
485 }
486
487 optind++;
488 if (argc > optind && argv[optind])
489 setenv("TERM", argv[optind], 1);
490
491 debug("exiting parseargs\n");
492}
493
494/* parse_speeds - parse alternate baud rates */
495
496void parse_speeds(op, arg)
497struct options *op;
498char *arg;
499{
500 char *strtok();
501 char *cp;
502
503 debug("entered parse_speeds\n");
504 for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {
505 if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
506 error("bad speed: %s", cp);
507 if (op->numspeed > MAX_SPEED)
508 error("too many alternate speeds");
509 }
510 debug("exiting parsespeeds\n");
511}
512
513#ifdef SYSV_STYLE
514
515/* update_utmp - update our utmp entry */
516void update_utmp(line)
517char *line;
518{
519 struct utmp ut;
520 time_t t;
521 int mypid = getpid();
522 long time();
523 long lseek();
524 struct utmp *utp;
525
526#if ! (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1))
527 struct flock lock;
528#endif
529
530 /*
531 * The utmp file holds miscellaneous information about things started by
532 * /sbin/init and other system-related events. Our purpose is to update
533 * the utmp entry for the current process, in particular the process type
534 * and the tty line we are listening to. Return successfully only if the
535 * utmp file can be opened for update, and if we are able to find our
536 * entry in the utmp file.
537 */
538
539#ifdef __linux__
540 utmpname(_PATH_UTMP);
541 setutent();
542 while ((utp = getutent())
543 && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid)) /* nothing */
544 ;
545
546 if (utp) {
547 memcpy(&ut, utp, sizeof(ut));
548 } else {
549 /* some inits don't initialize utmp... */
550 memset(&ut, 0, sizeof(ut));
551 strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
552 }
553 /*endutent(); */
554
555 strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user));
556 strncpy(ut.ut_line, line, sizeof(ut.ut_line));
557 if (fakehost)
558 strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
559 time(&t);
560 ut.ut_time = t;
561 ut.ut_type = LOGIN_PROCESS;
562 ut.ut_pid = mypid;
563
564 pututline(&ut);
565 endutent();
566
567 {
568 updwtmp(_PATH_WTMP, &ut);
569 }
570#else /* not __linux__ */
571 {
572 int ut_fd;
573
574 if ((ut_fd = open(UTMP_FILE, 2)) < 0) {
575 error("%s: open for update: %m"), UTMP_FILE;
576 } else {
577 long ut_size = sizeof(ut); /* avoid nonsense */
578
579 while (read(ut_fd, (char *) &ut, sizeof(ut)) == sizeof(ut)) {
580 if (ut.ut_type == INIT_PROCESS && ut.ut_pid == mypid) {
581 ut.ut_type = LOGIN_PROCESS;
582 ut.ut_time = time((long *) 0);
583 (void) strncpy(ut.ut_name, "LOGIN", sizeof(ut.ut_name));
584 (void) strncpy(ut.ut_line, line, sizeof(ut.ut_line));
585 if (fakehost)
586 (void) strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
587 (void) lseek(ut_fd, -ut_size, 1);
588 (void) write(ut_fd, (char *) &ut, sizeof(ut));
589 (void) close(ut_fd);
590 return;
591 }
592 }
593 error("%s: no utmp entry", line);
594 }
595 }
596#endif /* __linux__ */
597}
598
599#endif
600
601/* open_tty - set up tty as standard { input, output, error } */
602void open_tty(tty, tp, local)
603char *tty;
604struct termio *tp;
605int local;
606{
607 /* Get rid of the present standard { output, error} if any. */
608
609 (void) close(1);
610 (void) close(2);
611 errno = 0; /* ignore above errors */
612
613 /* Set up new standard input, unless we are given an already opened port. */
614
615 if (strcmp(tty, "-")) {
616 struct stat st;
617
618 /* Sanity checks... */
619
620 if (chdir("/dev"))
621 error("/dev: chdir() failed: %m");
622 if (stat(tty, &st) < 0)
623 error("/dev/%s: %m", tty);
624 if ((st.st_mode & S_IFMT) != S_IFCHR)
625 error("/dev/%s: not a character device", tty);
626
627 /* Open the tty as standard input. */
628
629 (void) close(0);
630 errno = 0; /* ignore close(2) errors */
631
632 debug("open(2)\n");
633 if (open(tty, O_RDWR | O_NONBLOCK, 0) != 0)
634 error("/dev/%s: cannot open as standard input: %m", tty);
635
636 } else {
637
638 /*
639 * Standard input should already be connected to an open port. Make
640 * sure it is open for read/write.
641 */
642
643 if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR)
644 error("%s: not open for read/write", tty);
645 }
646
647 /* Set up standard output and standard error file descriptors. */
648 debug("duping\n");
649 if (dup(0) != 1 || dup(0) != 2) /* set up stdout and stderr */
650 error("%s: dup problem: %m", tty); /* we have a problem */
651
652 /*
653 * The following ioctl will fail if stdin is not a tty, but also when
654 * there is noise on the modem control lines. In the latter case, the
655 * common course of action is (1) fix your cables (2) give the modem more
656 * time to properly reset after hanging up. SunOS users can achieve (2)
657 * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
658 * 5 seconds seems to be a good value.
659 */
660
661 if (ioctl(0, TCGETA, tp) < 0)
662 error("%s: ioctl: %m", tty);
663
664 /*
665 * It seems to be a terminal. Set proper protections and ownership. Mode
666 * 0622 is suitable for SYSV <4 because /bin/login does not change
667 * protections. SunOS 4 login will change the protections to 0620 (write
668 * access for group tty) after the login has succeeded.
669 */
670
671#ifdef DEBIAN
672 {
673 /* tty to root.dialout 660 */
674 struct group *gr;
675 int id;
676
677 id = (gr = getgrnam("dialout")) ? gr->gr_gid : 0;
678 chown(tty, 0, id);
679 chmod(tty, 0660);
680
681 /* vcs,vcsa to root.sys 600 */
682 if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) {
683 char *vcs, *vcsa;
684
685 if (!(vcs = malloc(strlen(tty))))
686 error("Can't malloc for vcs");
687 if (!(vcsa = malloc(strlen(tty) + 1)))
688 error("Can't malloc for vcsa");
689 strcpy(vcs, "vcs");
690 strcpy(vcs + 3, tty + 3);
691 strcpy(vcsa, "vcsa");
692 strcpy(vcsa + 4, tty + 3);
693
694 id = (gr = getgrnam("sys")) ? gr->gr_gid : 0;
695 chown(vcs, 0, id);
696 chmod(vcs, 0600);
697 chown(vcsa, 0, id);
698 chmod(vcs, 0600);
699
700 free(vcs);
701 free(vcsa);
702 }
703 }
704#else
705 (void) chown(tty, 0, 0); /* root, sys */
706 (void) chmod(tty, 0622); /* crw--w--w- */
707 errno = 0; /* ignore above errors */
708#endif
709}
710
711/* termio_init - initialize termio settings */
712
713char gbuf[1024];
714char area[1024];
715
716void termio_init(tp, speed, op)
717struct termio *tp;
718int speed;
719struct options *op;
720{
721
722 /*
723 * Initial termio settings: 8-bit characters, raw-mode, blocking i/o.
724 * Special characters are set after we have read the login name; all
725 * reads will be done in raw mode anyway. Errors will be dealt with
726 * lateron.
727 */
728#ifdef __linux__
729 /* flush input and output queues, important for modems! */
730 (void) ioctl(0, TCFLSH, TCIOFLUSH);
731#endif
732
733 tp->c_cflag = CS8 | HUPCL | CREAD | speed;
734 if (op->flags & F_LOCAL) {
735 tp->c_cflag |= CLOCAL;
736 }
737
738 tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0;
739 tp->c_cc[VMIN] = 1;
740 tp->c_cc[VTIME] = 0;
741
742 /* Optionally enable hardware flow control */
743
744#ifdef CRTSCTS
745 if (op->flags & F_RTSCTS)
746 tp->c_cflag |= CRTSCTS;
747#endif
748
749 (void) ioctl(0, TCSETA, tp);
750
751 /* go to blocking input even in local mode */
752 fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK);
753
754 debug("term_io 2\n");
755}
756
757/* auto_baud - extract baud rate from modem status message */
758void auto_baud(tp)
759struct termio *tp;
760{
761 int speed;
762 int vmin;
763 unsigned iflag;
764 char buf[BUFSIZ];
765 char *bp;
766 int nread;
767
768 /*
769 * This works only if the modem produces its status code AFTER raising
770 * the DCD line, and if the computer is fast enough to set the proper
771 * baud rate before the message has gone by. We expect a message of the
772 * following format:
773 *
774 * <junk><number><junk>
775 *
776 * The number is interpreted as the baud rate of the incoming call. If the
777 * modem does not tell us the baud rate within one second, we will keep
778 * using the current baud rate. It is advisable to enable BREAK
779 * processing (comma-separated list of baud rates) if the processing of
780 * modem status messages is enabled.
781 */
782
783 /*
784 * Use 7-bit characters, don't block if input queue is empty. Errors will
785 * be dealt with lateron.
786 */
787
788 iflag = tp->c_iflag;
789 tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */
790 vmin = tp->c_cc[VMIN];
791 tp->c_cc[VMIN] = 0; /* don't block if queue empty */
792 (void) ioctl(0, TCSETA, tp);
793
794 /*
795 * Wait for a while, then read everything the modem has said so far and
796 * try to extract the speed of the dial-in call.
797 */
798
799 (void) sleep(1);
800 if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) {
801 buf[nread] = '\0';
802 for (bp = buf; bp < buf + nread; bp++) {
803 if (isascii(*bp) && isdigit(*bp)) {
804 if ((speed = bcode(bp))) {
805 tp->c_cflag &= ~CBAUD;
806 tp->c_cflag |= speed;
807 }
808 break;
809 }
810 }
811 }
812 /* Restore terminal settings. Errors will be dealt with lateron. */
813
814 tp->c_iflag = iflag;
815 tp->c_cc[VMIN] = vmin;
816 (void) ioctl(0, TCSETA, tp);
817}
818
819/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
820void do_prompt(op, tp)
821struct options *op;
822struct termio *tp;
823{
824#ifdef ISSUE
825 FILE *fd;
826 int oflag;
827 int c;
828 struct utsname uts;
829
830 (void) uname(&uts);
831#endif
832
833 (void) write(1, "\r\n", 2); /* start a new line */
834#ifdef ISSUE /* optional: show /etc/issue */
835 if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) {
836 oflag = tp->c_oflag; /* save current setting */
837 tp->c_oflag |= (ONLCR | OPOST); /* map NL in output to CR-NL */
838 (void) ioctl(0, TCSETAW, tp);
839
840
841 while ((c = getc(fd)) != EOF) {
842 if (c == '\\') {
843 c = getc(fd);
844
845 switch (c) {
846 case 's':
847 (void) printf("%s", uts.sysname);
848 break;
849
850 case 'n':
851 (void) printf("%s", uts.nodename);
852 break;
853
854 case 'r':
855 (void) printf("%s", uts.release);
856 break;
857
858 case 'v':
859 (void) printf("%s", uts.version);
860 break;
861
862 case 'm':
863 (void) printf("%s", uts.machine);
864 break;
865
866 case 'o':
867 {
868 char domainname[256];
869
870 getdomainname(domainname, sizeof(domainname));
871 domainname[sizeof(domainname) - 1] = '\0';
872 printf("%s", domainname);
873 }
874 break;
875
876 case 'd':
877 case 't':
878 {
879 char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu",
880 "Fri", "Sat"
881 };
882 char *month[] = { "Jan", "Feb", "Mar", "Apr", "May",
883 "Jun", "Jul", "Aug", "Sep", "Oct",
884 "Nov", "Dec"
885 };
886 time_t now;
887 struct tm *tm;
888
889 (void) time(&now);
890 tm = localtime(&now);
891
892 if (c == 'd')
893 (void) printf("%s %s %d %d",
894 weekday[tm->tm_wday],
895 month[tm->tm_mon], tm->tm_mday,
896 tm->tm_year <
897 70 ? tm->tm_year +
898 2000 : tm->tm_year + 1900);
899 else
900 (void) printf("%02d:%02d:%02d", tm->tm_hour,
901 tm->tm_min, tm->tm_sec);
902
903 break;
904 }
905
906 case 'l':
907 (void) printf("%s", op->tty);
908 break;
909
910 case 'b':
911 {
912 int i;
913
914 for (i = 0; speedtab[i].speed; i++) {
915 if (speedtab[i].code == (tp->c_cflag & CBAUD)) {
916 printf("%ld", speedtab[i].speed);
917 break;
918 }
919 }
920 break;
921 }
922 case 'u':
923 case 'U':
924 {
925 int users = 0;
926 struct utmp *ut;
927
928 setutent();
929 while ((ut = getutent()))
930 if (ut->ut_type == USER_PROCESS)
931 users++;
932 endutent();
933 printf("%d ", users);
934 if (c == 'U')
935 printf((users == 1) ? "user" : "users");
936 break;
937 }
938 default:
939 (void) putchar(c);
940 }
941 } else
942 (void) putchar(c);
943 }
944 fflush(stdout);
945
946 tp->c_oflag = oflag; /* restore settings */
947 (void) ioctl(0, TCSETAW, tp); /* wait till output is gone */
948 (void) fclose(fd);
949 }
950#endif
951#ifdef __linux__
952 {
953 char hn[MAXHOSTNAMELEN + 1];
954
955 (void) gethostname(hn, MAXHOSTNAMELEN);
956 write(1, hn, strlen(hn));
957 }
958#endif
959 (void) write(1, LOGIN, sizeof(LOGIN) - 1); /* always show login prompt */
960}
961
962/* next_speed - select next baud rate */
963void next_speed(tp, op)
964struct termio *tp;
965struct options *op;
966{
967 static int baud_index = FIRST_SPEED; /* current speed index */
968
969 baud_index = (baud_index + 1) % op->numspeed;
970 tp->c_cflag &= ~CBAUD;
971 tp->c_cflag |= op->speeds[baud_index];
972 (void) ioctl(0, TCSETA, tp);
973}
974
975/* get_logname - get user name, establish parity, speed, erase, kill, eol */
976/* return NULL on failure, logname on success */
977char *get_logname(op, cp, tp)
978struct options *op;
979struct chardata *cp;
980struct termio *tp;
981{
982 static char logname[BUFSIZ];
983 char *bp;
984 char c; /* input character, full eight bits */
985 char ascval; /* low 7 bits of input character */
986 int bits; /* # of "1" bits per character */
987 int mask; /* mask with 1 bit up */
988 static char *erase[] = { /* backspace-space-backspace */
989 "\010\040\010", /* space parity */
990 "\010\040\010", /* odd parity */
991 "\210\240\210", /* even parity */
992 "\210\240\210", /* no parity */
993 };
994
995 /* Initialize kill, erase, parity etc. (also after switching speeds). */
996
997 *cp = init_chardata;
998
999 /* Flush pending input (esp. after parsing or switching the baud rate). */
1000
1001 (void) sleep(1);
1002 (void) ioctl(0, TCFLSH, TCIFLUSH);
1003
1004 /* Prompt for and read a login name. */
1005
1006 for (*logname = 0; *logname == 0; /* void */ ) {
1007
1008 /* Write issue file and prompt, with "parity" bit == 0. */
1009
1010 do_prompt(op, tp);
1011
1012 /* Read name, watch for break, parity, erase, kill, end-of-line. */
1013
1014 for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) {
1015
1016 /* Do not report trivial EINTR/EIO errors. */
1017
1018 if (read(0, &c, 1) < 1) {
1019 if (errno == EINTR || errno == EIO)
1020 exit(0);
1021 error("%s: read: %m", op->tty);
1022 }
1023 /* Do BREAK handling elsewhere. */
1024
1025 if ((c == 0) && op->numspeed > 1)
1026 /* return (0); */
1027 return NULL;
1028
1029 /* Do parity bit handling. */
1030
1031 if (c != (ascval = (c & 0177))) { /* "parity" bit on ? */
1032 for (bits = 1, mask = 1; mask & 0177; mask <<= 1)
1033 if (mask & ascval)
1034 bits++; /* count "1" bits */
1035 cp->parity |= ((bits & 1) ? 1 : 2);
1036 }
1037 /* Do erase, kill and end-of-line processing. */
1038
1039 switch (ascval) {
1040 case CR:
1041 case NL:
1042 *bp = 0; /* terminate logname */
1043 cp->eol = ascval; /* set end-of-line char */
1044 break;
1045 case BS:
1046 case DEL:
1047 case '#':
1048 cp->erase = ascval; /* set erase character */
1049 if (bp > logname) {
1050 (void) write(1, erase[cp->parity], 3);
1051 bp--;
1052 }
1053 break;
1054 case CTL('U'):
1055 case '@':
1056 cp->kill = ascval; /* set kill character */
1057 while (bp > logname) {
1058 (void) write(1, erase[cp->parity], 3);
1059 bp--;
1060 }
1061 break;
1062 case CTL('D'):
1063 exit(0);
1064 default:
1065 if (!isascii(ascval) || !isprint(ascval)) {
1066 /* ignore garbage characters */ ;
1067 } else if (bp - logname >= sizeof(logname) - 1) {
1068 error("%s: input overrun", op->tty);
1069 } else {
1070 (void) write(1, &c, 1); /* echo the character */
1071 *bp++ = ascval; /* and store it */
1072 }
1073 break;
1074 }
1075 }
1076 }
1077 /* Handle names with upper case and no lower case. */
1078
1079 if ((cp->capslock = caps_lock(logname))) {
1080 for (bp = logname; *bp; bp++)
1081 if (isupper(*bp))
1082 *bp = tolower(*bp); /* map name to lower case */
1083 }
1084 return (logname);
1085}
1086
1087/* termio_final - set the final tty mode bits */
1088void termio_final(op, tp, cp)
1089struct options *op;
1090struct termio *tp;
1091struct chardata *cp;
1092{
1093 /* General terminal-independent stuff. */
1094
1095 tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */
1096 tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
1097 /* no longer| ECHOCTL | ECHOPRT */
1098 tp->c_oflag |= OPOST;
1099 /* tp->c_cflag = 0; */
1100 tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */
1101 tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */
1102 tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */
1103 tp->c_cc[VEOL] = DEF_EOL;
1104#ifdef __linux__
1105 tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */
1106#else
1107 tp->c_cc[VSWTCH] = DEF_SWITCH; /* default switch character */
1108#endif
1109
1110 /* Account for special characters seen in input. */
1111
1112 if (cp->eol == CR) {
1113 tp->c_iflag |= ICRNL; /* map CR in input to NL */
1114 tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */
1115 }
1116 tp->c_cc[VERASE] = cp->erase; /* set erase character */
1117 tp->c_cc[VKILL] = cp->kill; /* set kill character */
1118
1119 /* Account for the presence or absence of parity bits in input. */
1120
1121 switch (cp->parity) {
1122 case 0: /* space (always 0) parity */
1123 break;
1124 case 1: /* odd parity */
1125 tp->c_cflag |= PARODD;
1126 /* FALLTHROUGH */
1127 case 2: /* even parity */
1128 tp->c_cflag |= PARENB;
1129 tp->c_iflag |= INPCK | ISTRIP;
1130 /* FALLTHROUGH */
1131 case (1 | 2): /* no parity bit */
1132 tp->c_cflag &= ~CSIZE;
1133 tp->c_cflag |= CS7;
1134 break;
1135 }
1136 /* Account for upper case without lower case. */
1137
1138 if (cp->capslock) {
1139 tp->c_iflag |= IUCLC;
1140 tp->c_lflag |= XCASE;
1141 tp->c_oflag |= OLCUC;
1142 }
1143 /* Optionally enable hardware flow control */
1144
1145#ifdef CRTSCTS
1146 if (op->flags & F_RTSCTS)
1147 tp->c_cflag |= CRTSCTS;
1148#endif
1149
1150 /* Finally, make the new settings effective */
1151
1152 if (ioctl(0, TCSETA, tp) < 0)
1153 error("%s: ioctl: TCSETA: %m", op->tty);
1154}
1155
1156/* caps_lock - string contains upper case without lower case */
1157/* returns 1 if true, 0 if false */
1158int caps_lock(s)
1159char *s;
1160{
1161 int capslock;
1162
1163 for (capslock = 0; *s; s++) {
1164 if (islower(*s))
1165 return (0);
1166 if (capslock == 0)
1167 capslock = isupper(*s);
1168 }
1169 return (capslock);
1170}
1171
1172/* bcode - convert speed string to speed code; return 0 on failure */
1173int bcode(s)
1174char *s;
1175{
1176 struct Speedtab *sp;
1177 long speed = atol(s);
1178
1179 for (sp = speedtab; sp->speed; sp++)
1180 if (sp->speed == speed)
1181 return (sp->code);
1182 return (0);
1183}
1184
1185/* error - report errors to console or syslog; only understands %s and %m */
1186
1187#define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2)
1188
1189/*
1190 * output error messages
1191 */
1192static void error(const char *fmt, ...)
1193{
1194 va_list va_alist;
1195 char buf[256], *bp;
1196
1197#ifndef USE_SYSLOG
1198 int fd;
1199#endif
1200
1201#ifdef USE_SYSLOG
1202 buf[0] = '\0';
1203 bp = buf;
1204#else
1205 strncpy(buf, progname, 256);
1206 strncat(buf, ": ", 256);
1207 buf[255] = 0;
1208 bp = buf + strlen(buf);
1209#endif
1210
1211 va_start(va_alist, fmt);
1212 vsnprintf(bp, 256 - strlen(buf), fmt, va_alist);
1213 buf[255] = 0;
1214 va_end(va_alist);
1215
1216#ifdef USE_SYSLOG
1217 openlog(progname, LOG_PID, LOG_AUTH);
1218 syslog(LOG_ERR, buf);
1219 closelog();
1220#else
1221 strncat(bp, "\r\n", 256 - strlen(buf));
1222 buf[255] = 0;
1223 if ((fd = open("/dev/console", 1)) >= 0) {
1224 write(fd, buf, strlen(buf));
1225 close(fd);
1226 }
1227#endif
1228 (void) sleep((unsigned) 10); /* be kind to init(8) */
1229 exit(1);
1230}