]> git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/agetty.c
Imported from util-linux-2.11b 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@pld.ORG.PL>
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 utmpname(_PATH_UTMP);
568 setutent();
569 while ((utp = getutent())
570 && !(utp->ut_type == INIT_PROCESS
571 && utp->ut_pid == mypid)) /* nothing */;
572
573 if (utp) {
574 memcpy(&ut, utp, sizeof(ut));
575 } else {
576 /* some inits don't initialize utmp... */
577 memset(&ut, 0, sizeof(ut));
578 strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
579 }
580
581 strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user));
582 strncpy(ut.ut_line, line, sizeof(ut.ut_line));
583 if (fakehost)
584 strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
585 time(&t);
586 ut.ut_time = t;
587 ut.ut_type = LOGIN_PROCESS;
588 ut.ut_pid = mypid;
589
590 pututline(&ut);
591 endutent();
592
593 {
594 #ifdef HAVE_updwtmp
595 updwtmp(_PATH_WTMP, &ut);
596 #else
597 int ut_fd;
598 int lf;
599
600 if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) {
601 flock(lf, LOCK_EX);
602 if ((ut_fd = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
603 write(ut_fd, &ut, sizeof(ut));
604 close(ut_fd);
605 }
606 flock(lf, LOCK_UN);
607 close(lf);
608 }
609 #endif
610 }
611 }
612
613 #endif
614
615 /* open_tty - set up tty as standard { input, output, error } */
616 void
617 open_tty(tty, tp, local)
618 char *tty;
619 struct termio *tp;
620 int local;
621 {
622 /* Get rid of the present standard { output, error} if any. */
623
624 (void) close(1);
625 (void) close(2);
626 errno = 0; /* ignore above errors */
627
628 /* Set up new standard input, unless we are given an already opened port. */
629
630 if (strcmp(tty, "-")) {
631 struct stat st;
632
633 /* Sanity checks... */
634
635 if (chdir("/dev"))
636 error(_("/dev: chdir() failed: %m"));
637 if (stat(tty, &st) < 0)
638 error("/dev/%s: %m", tty);
639 if ((st.st_mode & S_IFMT) != S_IFCHR)
640 error(_("/dev/%s: not a character device"), tty);
641
642 /* Open the tty as standard input. */
643
644 (void) close(0);
645 errno = 0; /* ignore close(2) errors */
646
647 debug(_("open(2)\n"));
648 if (open(tty, O_RDWR|O_NONBLOCK, 0) != 0)
649 error(_("/dev/%s: cannot open as standard input: %m"), tty);
650
651 } else {
652
653 /*
654 * Standard input should already be connected to an open port. Make
655 * sure it is open for read/write.
656 */
657
658 if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR)
659 error(_("%s: not open for read/write"), tty);
660 }
661
662 /* Set up standard output and standard error file descriptors. */
663 debug(_("duping\n"));
664 if (dup(0) != 1 || dup(0) != 2) /* set up stdout and stderr */
665 error(_("%s: dup problem: %m"), tty); /* we have a problem */
666
667 /*
668 * The following ioctl will fail if stdin is not a tty, but also when
669 * there is noise on the modem control lines. In the latter case, the
670 * common course of action is (1) fix your cables (2) give the modem more
671 * time to properly reset after hanging up. SunOS users can achieve (2)
672 * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
673 * 5 seconds seems to be a good value.
674 */
675
676 if (ioctl(0, TCGETA, tp) < 0)
677 error("%s: ioctl: %m", tty);
678
679 /*
680 * It seems to be a terminal. Set proper protections and ownership. Mode
681 * 0622 is suitable for SYSV <4 because /bin/login does not change
682 * protections. SunOS 4 login will change the protections to 0620 (write
683 * access for group tty) after the login has succeeded.
684 */
685
686 (void) chown(tty, 0, 0); /* root, sys */
687 (void) chmod(tty, 0622); /* crw--w--w- */
688 errno = 0; /* ignore above errors */
689 }
690
691 /* termio_init - initialize termio settings */
692
693 char gbuf[1024];
694 char area[1024];
695
696 void
697 termio_init(tp, speed, op)
698 struct termio *tp;
699 int speed;
700 struct options *op;
701 {
702
703 /*
704 * Initial termio settings: 8-bit characters, raw-mode, blocking i/o.
705 * Special characters are set after we have read the login name; all
706 * reads will be done in raw mode anyway. Errors will be dealt with
707 * lateron.
708 */
709 #ifdef __linux__
710 /* flush input and output queues, important for modems! */
711 (void) ioctl(0, TCFLSH, TCIOFLUSH);
712 #endif
713
714 tp->c_cflag = CS8 | HUPCL | CREAD | speed;
715 if (op->flags & F_LOCAL) {
716 tp->c_cflag |= CLOCAL;
717 }
718
719 tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0;
720 tp->c_cc[VMIN] = 1;
721 tp->c_cc[VTIME] = 0;
722
723 /* Optionally enable hardware flow control */
724
725 #ifdef CRTSCTS
726 if (op->flags & F_RTSCTS)
727 tp->c_cflag |= CRTSCTS;
728 #endif
729
730 (void) ioctl(0, TCSETA, tp);
731
732 /* go to blocking input even in local mode */
733 fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK);
734
735 debug(_("term_io 2\n"));
736 }
737
738 /* auto_baud - extract baud rate from modem status message */
739 void
740 auto_baud(tp)
741 struct termio *tp;
742 {
743 int speed;
744 int vmin;
745 unsigned iflag;
746 char buf[BUFSIZ];
747 char *bp;
748 int nread;
749
750 /*
751 * This works only if the modem produces its status code AFTER raising
752 * the DCD line, and if the computer is fast enough to set the proper
753 * baud rate before the message has gone by. We expect a message of the
754 * following format:
755 *
756 * <junk><number><junk>
757 *
758 * The number is interpreted as the baud rate of the incoming call. If the
759 * modem does not tell us the baud rate within one second, we will keep
760 * using the current baud rate. It is advisable to enable BREAK
761 * processing (comma-separated list of baud rates) if the processing of
762 * modem status messages is enabled.
763 */
764
765 /*
766 * Use 7-bit characters, don't block if input queue is empty. Errors will
767 * be dealt with lateron.
768 */
769
770 iflag = tp->c_iflag;
771 tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */
772 vmin = tp->c_cc[VMIN];
773 tp->c_cc[VMIN] = 0; /* don't block if queue empty */
774 (void) ioctl(0, TCSETA, tp);
775
776 /*
777 * Wait for a while, then read everything the modem has said so far and
778 * try to extract the speed of the dial-in call.
779 */
780
781 (void) sleep(1);
782 if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) {
783 buf[nread] = '\0';
784 for (bp = buf; bp < buf + nread; bp++) {
785 if (isascii(*bp) && isdigit(*bp)) {
786 if ((speed = bcode(bp))) {
787 tp->c_cflag &= ~CBAUD;
788 tp->c_cflag |= speed;
789 }
790 break;
791 }
792 }
793 }
794 /* Restore terminal settings. Errors will be dealt with lateron. */
795
796 tp->c_iflag = iflag;
797 tp->c_cc[VMIN] = vmin;
798 (void) ioctl(0, TCSETA, tp);
799 }
800
801 /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
802 void
803 do_prompt(op, tp)
804 struct options *op;
805 struct termio *tp;
806 {
807 #ifdef ISSUE
808 FILE *fd;
809 int oflag;
810 int c;
811 struct utsname uts;
812
813 (void) uname(&uts);
814 #endif
815
816 (void) write(1, "\r\n", 2); /* start a new line */
817 #ifdef ISSUE /* optional: show /etc/issue */
818 if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) {
819 oflag = tp->c_oflag; /* save current setting */
820 tp->c_oflag |= (ONLCR | OPOST); /* map NL in output to CR-NL */
821 (void) ioctl(0, TCSETAW, tp);
822
823
824 while ((c = getc(fd)) != EOF)
825 {
826 if (c == '\\')
827 {
828 c = getc(fd);
829
830 switch (c)
831 {
832 case 's':
833 (void) printf ("%s", uts.sysname);
834 break;
835
836 case 'n':
837 (void) printf ("%s", uts.nodename);
838 break;
839
840 case 'r':
841 (void) printf ("%s", uts.release);
842 break;
843
844 case 'v':
845 (void) printf ("%s", uts.version);
846 break;
847
848 case 'm':
849 (void) printf ("%s", uts.machine);
850 break;
851
852 case 'o':
853 {
854 char domainname[256];
855 #ifdef HAVE_getdomainname
856 getdomainname(domainname, sizeof(domainname));
857 #else
858 strcpy(domainname, "unknown_domain");
859 #endif
860 domainname[sizeof(domainname)-1] = '\0';
861 printf ("%s", domainname);
862 }
863 break;
864
865 case 'd':
866 case 't':
867 {
868 char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu",
869 "Fri", "Sat" };
870 char *month[] = { "Jan", "Feb", "Mar", "Apr", "May",
871 "Jun", "Jul", "Aug", "Sep", "Oct",
872 "Nov", "Dec" };
873 time_t now;
874 struct tm *tm;
875
876 (void) time (&now);
877 tm = localtime(&now);
878
879 if (c == 'd')
880 (void) printf ("%s %s %d %d",
881 weekday[tm->tm_wday], month[tm->tm_mon],
882 tm->tm_mday,
883 tm->tm_year < 70 ? tm->tm_year + 2000 :
884 tm->tm_year + 1900);
885 else
886 (void) printf ("%02d:%02d:%02d",
887 tm->tm_hour, tm->tm_min, tm->tm_sec);
888
889 break;
890 }
891
892 case 'l':
893 (void) printf ("%s", op->tty);
894 break;
895
896 case 'b':
897 {
898 int i;
899
900 for (i = 0; speedtab[i].speed; i++) {
901 if (speedtab[i].code == (tp->c_cflag & CBAUD)) {
902 printf("%ld", speedtab[i].speed);
903 break;
904 }
905 }
906 break;
907 }
908 case 'u':
909 case 'U':
910 {
911 int users = 0;
912 struct utmp *ut;
913 setutent();
914 while ((ut = getutent()))
915 if (ut->ut_type == USER_PROCESS)
916 users++;
917 endutent();
918 printf ("%d ", users);
919 if (c == 'U')
920 printf ((users == 1) ? _("user") : _("users"));
921 break;
922 }
923 default:
924 (void) putchar(c);
925 }
926 }
927 else
928 (void) putchar(c);
929 }
930 fflush(stdout);
931
932 tp->c_oflag = oflag; /* restore settings */
933 (void) ioctl(0, TCSETAW, tp); /* wait till output is gone */
934 (void) fclose(fd);
935 }
936 #endif
937 #ifdef __linux__
938 {
939 char hn[MAXHOSTNAMELEN+1];
940
941 (void) gethostname(hn, MAXHOSTNAMELEN);
942 write(1, hn, strlen(hn));
943 }
944 #endif
945 (void) write(1, LOGIN, sizeof(LOGIN) - 1); /* always show login prompt */
946 }
947
948 /* next_speed - select next baud rate */
949 void
950 next_speed(tp, op)
951 struct termio *tp;
952 struct options *op;
953 {
954 static int baud_index = FIRST_SPEED;/* current speed index */
955
956 baud_index = (baud_index + 1) % op->numspeed;
957 tp->c_cflag &= ~CBAUD;
958 tp->c_cflag |= op->speeds[baud_index];
959 (void) ioctl(0, TCSETA, tp);
960 }
961
962 /* get_logname - get user name, establish parity, speed, erase, kill, eol */
963
964 char *get_logname(op, cp, tp)
965 struct options *op;
966 struct chardata *cp;
967 struct termio *tp;
968 {
969 static char logname[BUFSIZ];
970 char *bp;
971 char c; /* input character, full eight bits */
972 char ascval; /* low 7 bits of input character */
973 int bits; /* # of "1" bits per character */
974 int mask; /* mask with 1 bit up */
975 static char *erase[] = { /* backspace-space-backspace */
976 "\010\040\010", /* space parity */
977 "\010\040\010", /* odd parity */
978 "\210\240\210", /* even parity */
979 "\210\240\210", /* no parity */
980 };
981
982 /* Initialize kill, erase, parity etc. (also after switching speeds). */
983
984 *cp = init_chardata;
985
986 /* Flush pending input (esp. after parsing or switching the baud rate). */
987
988 (void) sleep(1);
989 (void) ioctl(0, TCFLSH, TCIFLUSH);
990
991 /* Prompt for and read a login name. */
992
993 for (*logname = 0; *logname == 0; /* void */ ) {
994
995 /* Write issue file and prompt, with "parity" bit == 0. */
996
997 do_prompt(op, tp);
998
999 /* Read name, watch for break, parity, erase, kill, end-of-line. */
1000
1001 for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) {
1002
1003 /* Do not report trivial EINTR/EIO errors. */
1004
1005 if (read(0, &c, 1) < 1) {
1006 if (errno == EINTR || errno == EIO)
1007 exit(0);
1008 error(_("%s: read: %m"), op->tty);
1009 }
1010 /* Do BREAK handling elsewhere. */
1011
1012 if ((c == 0) && op->numspeed > 1)
1013 return (0);
1014
1015 /* Do parity bit handling. */
1016
1017 if (c != (ascval = (c & 0177))) { /* "parity" bit on ? */
1018 for (bits = 1, mask = 1; mask & 0177; mask <<= 1)
1019 if (mask & ascval)
1020 bits++; /* count "1" bits */
1021 cp->parity |= ((bits & 1) ? 1 : 2);
1022 }
1023 /* Do erase, kill and end-of-line processing. */
1024
1025 switch (ascval) {
1026 case CR:
1027 case NL:
1028 *bp = 0; /* terminate logname */
1029 cp->eol = ascval; /* set end-of-line char */
1030 break;
1031 case BS:
1032 case DEL:
1033 case '#':
1034 cp->erase = ascval; /* set erase character */
1035 if (bp > logname) {
1036 (void) write(1, erase[cp->parity], 3);
1037 bp--;
1038 }
1039 break;
1040 case CTL('U'):
1041 case '@':
1042 cp->kill = ascval; /* set kill character */
1043 while (bp > logname) {
1044 (void) write(1, erase[cp->parity], 3);
1045 bp--;
1046 }
1047 break;
1048 case CTL('D'):
1049 exit(0);
1050 default:
1051 if (!isascii(ascval) || !isprint(ascval)) {
1052 /* ignore garbage characters */ ;
1053 } else if (bp - logname >= sizeof(logname) - 1) {
1054 error(_("%s: input overrun"), op->tty);
1055 } else {
1056 (void) write(1, &c, 1); /* echo the character */
1057 *bp++ = ascval; /* and store it */
1058 }
1059 break;
1060 }
1061 }
1062 }
1063 /* Handle names with upper case and no lower case. */
1064
1065 if ((cp->capslock = caps_lock(logname))) {
1066 for (bp = logname; *bp; bp++)
1067 if (isupper(*bp))
1068 *bp = tolower(*bp); /* map name to lower case */
1069 }
1070 return (logname);
1071 }
1072
1073 /* termio_final - set the final tty mode bits */
1074 void
1075 termio_final(op, tp, cp)
1076 struct options *op;
1077 struct termio *tp;
1078 struct chardata *cp;
1079 {
1080 /* General terminal-independent stuff. */
1081
1082 tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */
1083 tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK| ECHOKE;
1084 /* no longer| ECHOCTL | ECHOPRT*/
1085 tp->c_oflag |= OPOST;
1086 /* tp->c_cflag = 0; */
1087 tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */
1088 tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */
1089 tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */
1090 tp->c_cc[VEOL] = DEF_EOL;
1091 #ifdef __linux__
1092 tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */
1093 #else
1094 tp->c_cc[VSWTCH] = DEF_SWITCH; /* default switch character */
1095 #endif
1096
1097 /* Account for special characters seen in input. */
1098
1099 if (cp->eol == CR) {
1100 tp->c_iflag |= ICRNL; /* map CR in input to NL */
1101 tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */
1102 }
1103 tp->c_cc[VERASE] = cp->erase; /* set erase character */
1104 tp->c_cc[VKILL] = cp->kill; /* set kill character */
1105
1106 /* Account for the presence or absence of parity bits in input. */
1107
1108 switch (cp->parity) {
1109 case 0: /* space (always 0) parity */
1110 break;
1111 case 1: /* odd parity */
1112 tp->c_cflag |= PARODD;
1113 /* FALLTHROUGH */
1114 case 2: /* even parity */
1115 tp->c_cflag |= PARENB;
1116 tp->c_iflag |= INPCK | ISTRIP;
1117 /* FALLTHROUGH */
1118 case (1 | 2): /* no parity bit */
1119 tp->c_cflag &= ~CSIZE;
1120 tp->c_cflag |= CS7;
1121 break;
1122 }
1123 /* Account for upper case without lower case. */
1124
1125 if (cp->capslock) {
1126 tp->c_iflag |= IUCLC;
1127 tp->c_lflag |= XCASE;
1128 tp->c_oflag |= OLCUC;
1129 }
1130 /* Optionally enable hardware flow control */
1131
1132 #ifdef CRTSCTS
1133 if (op->flags & F_RTSCTS)
1134 tp->c_cflag |= CRTSCTS;
1135 #endif
1136
1137 /* Finally, make the new settings effective */
1138
1139 if (ioctl(0, TCSETA, tp) < 0)
1140 error("%s: ioctl: TCSETA: %m", op->tty);
1141 }
1142
1143 /* caps_lock - string contains upper case without lower case */
1144 int
1145 caps_lock(s)
1146 char *s;
1147 {
1148 int capslock;
1149
1150 for (capslock = 0; *s; s++) {
1151 if (islower(*s))
1152 return (0);
1153 if (capslock == 0)
1154 capslock = isupper(*s);
1155 }
1156 return (capslock);
1157 }
1158
1159 /* bcode - convert speed string to speed code; return 0 on failure */
1160 int
1161 bcode(s)
1162 char *s;
1163 {
1164 struct Speedtab *sp;
1165 long speed = atol(s);
1166
1167 for (sp = speedtab; sp->speed; sp++)
1168 if (sp->speed == speed)
1169 return (sp->code);
1170 return (0);
1171 }
1172
1173 /* usage - explain */
1174
1175 void
1176 usage()
1177 {
1178 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);
1179 exit(1);
1180 }
1181
1182 /* error - report errors to console or syslog; only understands %s and %m */
1183
1184 #define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2)
1185
1186 void
1187 error(const char *fmt, ...) {
1188 va_list ap;
1189 #ifndef USE_SYSLOG
1190 int fd;
1191 #endif
1192 char buf[BUFSIZ];
1193 char *bp;
1194
1195 /*
1196 * If the diagnostic is reported via syslog(3), the process name is
1197 * automatically prepended to the message. If we write directly to
1198 * /dev/console, we must prepend the process name ourselves.
1199 */
1200
1201 #ifdef USE_SYSLOG
1202 buf[0] = '\0';
1203 bp = buf;
1204 #else
1205 (void) str2cpy(buf, progname, ": ");
1206 bp = buf + strlen(buf);
1207 #endif
1208
1209 /*
1210 * %s expansion is done by hand. On a System V Release 2 system without
1211 * shared libraries and without syslog(3), linking with the the stdio
1212 * library would make the program three times as big...
1213 *
1214 * %m expansion is done here as well. Too bad syslog(3) does not have a
1215 * vsprintf() like interface.
1216 */
1217
1218 va_start(ap, fmt);
1219 while (*fmt) {
1220 if (strncmp(fmt, "%s", 2) == 0) {
1221 (void) strcpy(bp, va_arg(ap, char *));
1222 bp += strlen(bp);
1223 fmt += 2;
1224 } else if (strncmp(fmt, "%m", 2) == 0) {
1225 (void) strcpy(bp, strerror(errno));
1226 bp += strlen(bp);
1227 fmt += 2;
1228 } else {
1229 *bp++ = *fmt++;
1230 }
1231 }
1232 *bp = 0;
1233 va_end(ap);
1234
1235 /*
1236 * Write the diagnostic directly to /dev/console if we do not use the
1237 * syslog(3) facility.
1238 */
1239
1240 #ifdef USE_SYSLOG
1241 (void) openlog(progname, LOG_PID, LOG_AUTHPRIV);
1242 (void) syslog(LOG_ERR, "%s", buf);
1243 closelog();
1244 #else
1245 /* Terminate with CR-LF since the console mode is unknown. */
1246 (void) strcat(bp, "\r\n");
1247 if ((fd = open("/dev/console", 1)) >= 0) {
1248 (void) write(fd, buf, strlen(buf));
1249 (void) close(fd);
1250 }
1251 #endif
1252 (void) sleep((unsigned) 10); /* be kind to init(8) */
1253 exit(1);
1254 }