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