main.c revision 1.36 1 /* $NetBSD: main.c,v 1.36 2001/01/10 00:36:44 lukem Exp $ */
2
3 /*-
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37
38 #ifndef lint
39 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\n\
40 The Regents of the University of California. All rights reserved.\n");
41 #endif /* not lint */
42
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "from: @(#)main.c 8.1 (Berkeley) 6/20/93";
46 #else
47 __RCSID("$NetBSD: main.c,v 1.36 2001/01/10 00:36:44 lukem Exp $");
48 #endif
49 #endif /* not lint */
50
51 #include <sys/param.h>
52 #include <sys/stat.h>
53 #include <termios.h>
54 #include <sys/ioctl.h>
55 #include <sys/resource.h>
56 #include <sys/utsname.h>
57
58 #include <errno.h>
59 #include <fcntl.h>
60 #include <time.h>
61 #include <ctype.h>
62 #include <fcntl.h>
63 #include <pwd.h>
64 #include <setjmp.h>
65 #include <signal.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <syslog.h>
69 #include <time.h>
70 #include <unistd.h>
71 #include <util.h>
72 #include <limits.h>
73 #include <ttyent.h>
74 #include <termcap.h>
75
76 #include "gettytab.h"
77 #include "pathnames.h"
78 #include "extern.h"
79
80 extern char *__progname;
81
82 /*
83 * Set the amount of running time that getty should accumulate
84 * before deciding that something is wrong and exit.
85 */
86 #define GETTY_TIMEOUT 60 /* seconds */
87
88 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
89
90 #define PPP_FRAME 0x7e /* PPP Framing character */
91 #define PPP_STATION 0xff /* "All Station" character */
92 #define PPP_ESCAPE 0x7d /* Escape Character */
93 #define PPP_CONTROL 0x03 /* PPP Control Field */
94 #define PPP_CONTROL_ESCAPED 0x23 /* PPP Control Field, escaped */
95 #define PPP_LCP_HI 0xc0 /* LCP protocol - high byte */
96 #define PPP_LCP_LOW 0x21 /* LCP protocol - low byte */
97
98 struct termios tmode, omode;
99
100 int crmod, digit, lower, upper;
101
102 char hostname[MAXHOSTNAMELEN + 1];
103 struct utsname kerninfo;
104 char name[LOGIN_NAME_MAX];
105 char dev[] = _PATH_DEV;
106 char ttyn[32];
107 char lockfile[512];
108 uid_t ttyowner;
109 char *rawttyn;
110
111 #define OBUFSIZ 128
112 #define TABBUFSIZ 512
113
114 char defent[TABBUFSIZ];
115 char tabent[TABBUFSIZ];
116
117 char *env[128];
118
119 char partab[] = {
120 0001,0201,0201,0001,0201,0001,0001,0201,
121 0202,0004,0003,0205,0005,0206,0201,0001,
122 0201,0001,0001,0201,0001,0201,0201,0001,
123 0001,0201,0201,0001,0201,0001,0001,0201,
124 0200,0000,0000,0200,0000,0200,0200,0000,
125 0000,0200,0200,0000,0200,0000,0000,0200,
126 0000,0200,0200,0000,0200,0000,0000,0200,
127 0200,0000,0000,0200,0000,0200,0200,0000,
128 0200,0000,0000,0200,0000,0200,0200,0000,
129 0000,0200,0200,0000,0200,0000,0000,0200,
130 0000,0200,0200,0000,0200,0000,0000,0200,
131 0200,0000,0000,0200,0000,0200,0200,0000,
132 0000,0200,0200,0000,0200,0000,0000,0200,
133 0200,0000,0000,0200,0000,0200,0200,0000,
134 0200,0000,0000,0200,0000,0200,0200,0000,
135 0000,0200,0200,0000,0200,0000,0000,0201
136 };
137
138 #define ERASE tmode.c_cc[VERASE]
139 #define KILL tmode.c_cc[VKILL]
140 #define EOT tmode.c_cc[VEOF]
141
142 static void dingdong __P((int));
143 static void interrupt __P((int));
144 static void clearscreen __P((void));
145 void timeoverrun __P((int));
146
147 jmp_buf timeout;
148
149 static void
150 dingdong(signo)
151 int signo;
152 {
153
154 alarm(0);
155 signal(SIGALRM, SIG_DFL);
156 longjmp(timeout, 1);
157 }
158
159 jmp_buf intrupt;
160
161 static void
162 interrupt(signo)
163 int signo;
164 {
165
166 signal(SIGINT, interrupt);
167 longjmp(intrupt, 1);
168 }
169
170 /*
171 * Action to take when getty is running too long.
172 */
173 void
174 timeoverrun(signo)
175 int signo;
176 {
177
178 syslog(LOG_ERR, "getty exiting due to excessive running time");
179 exit(1);
180 }
181
182 int main __P((int, char **));
183 static int getname __P((void));
184 static void oflush __P((void));
185 static void prompt __P((void));
186 static void putchr __P((int));
187 static void putf __P((const char *));
188 static void putpad __P((const char *));
189 static void xputs __P((const char *));
190
191 int
192 main(argc, argv)
193 int argc;
194 char *argv[];
195 {
196 extern char **environ;
197 char *tname;
198 int repcnt = 0, failopenlogged = 0, uugetty = 0, first_time = 1;
199 struct rlimit limit;
200 struct passwd *pw;
201 int rval;
202
203 #ifdef __GNUC__
204 (void)&tname; /* XXX gcc -Wall */
205 #endif
206
207 signal(SIGINT, SIG_IGN);
208 /*
209 signal(SIGQUIT, SIG_DFL);
210 */
211 openlog("getty", LOG_PID, LOG_AUTH);
212 gethostname(hostname, sizeof(hostname));
213 hostname[sizeof(hostname) - 1] = '\0';
214 if (hostname[0] == '\0')
215 strcpy(hostname, "Amnesiac");
216 uname(&kerninfo);
217
218 if (__progname[0] == 'u' && __progname[1] == 'u')
219 uugetty = 1;
220
221 /*
222 * Find id of uucp login (if present) so we can chown tty properly.
223 */
224 if (uugetty && (pw = getpwnam("uucp")))
225 ttyowner = pw->pw_uid;
226 else
227 ttyowner = 0;
228
229 /*
230 * Limit running time to deal with broken or dead lines.
231 */
232 (void)signal(SIGXCPU, timeoverrun);
233 limit.rlim_max = RLIM_INFINITY;
234 limit.rlim_cur = GETTY_TIMEOUT;
235 (void)setrlimit(RLIMIT_CPU, &limit);
236
237 /*
238 * The following is a work around for vhangup interactions
239 * which cause great problems getting window systems started.
240 * If the tty line is "-", we do the old style getty presuming
241 * that the file descriptors are already set up for us.
242 * J. Gettys - MIT Project Athena.
243 */
244 if (argc <= 2 || strcmp(argv[2], "-") == 0) {
245 strlcpy(ttyn, ttyname(0), sizeof(ttyn));
246 }
247 else {
248 int i;
249
250 rawttyn = argv[2];
251 strlcpy(ttyn, dev, sizeof(ttyn));
252 strlcat(ttyn, argv[2], sizeof(ttyn));
253
254 if (uugetty) {
255 chown(ttyn, ttyowner, 0);
256 strcpy(lockfile, _PATH_LOCK);
257 strlcat(lockfile, argv[2], sizeof(lockfile));
258 /* wait for lockfiles to go away before we try to open */
259 if ( pidlock(lockfile, 0, 0, 0) != 0 ) {
260 syslog(LOG_ERR, "%s: can't create lockfile", ttyn);
261 exit(1);
262 }
263 unlink(lockfile);
264 }
265 if (strcmp(argv[0], "+") != 0) {
266 chown(ttyn, ttyowner, 0);
267 chmod(ttyn, 0600);
268 revoke(ttyn);
269 if (ttyaction(ttyn, "getty", "root"))
270 syslog(LOG_WARNING, "%s: ttyaction failed", ttyn);
271 /*
272 * Delay the open so DTR stays down long enough to be detected.
273 */
274 sleep(2);
275 while ((i = open(ttyn, O_RDWR)) == -1) {
276 if ((repcnt % 10 == 0) &&
277 (errno != ENXIO || !failopenlogged)) {
278 syslog(LOG_WARNING, "%s: %m", ttyn);
279 closelog();
280 failopenlogged = 1;
281 }
282 repcnt++;
283 sleep(60);
284 }
285 if (uugetty && pidlock(lockfile, 0, 0, 0) != 0) {
286 syslog(LOG_ERR, "%s: can't create lockfile", ttyn);
287 exit(1);
288 }
289 (void) chown(lockfile, ttyowner, 0);
290 login_tty(i);
291 }
292 }
293
294 /* Start with default tty settings */
295 if (tcgetattr(0, &tmode) < 0) {
296 syslog(LOG_ERR, "%s: %m", ttyn);
297 exit(1);
298 }
299 omode = tmode;
300
301 gettable("default", defent);
302 gendefaults();
303 tname = "default";
304 if (argc > 1)
305 tname = argv[1];
306 for (;;) {
307 int off;
308
309 gettable(tname, tabent);
310 if (OPset || EPset || APset)
311 APset++, OPset++, EPset++;
312 setdefaults();
313 off = 0;
314 (void)tcflush(0, TCIOFLUSH); /* clear out the crap */
315 ioctl(0, FIONBIO, &off); /* turn off non-blocking mode */
316 ioctl(0, FIOASYNC, &off); /* ditto for async mode */
317
318 if (IS)
319 cfsetispeed(&tmode, IS);
320 else if (SP)
321 cfsetispeed(&tmode, SP);
322 if (OS)
323 cfsetospeed(&tmode, OS);
324 else if (SP)
325 cfsetospeed(&tmode, SP);
326 setflags(0);
327 setchars();
328 if (tcsetattr(0, TCSANOW, &tmode) < 0) {
329 syslog(LOG_ERR, "%s: %m", ttyn);
330 exit(1);
331 }
332 if (AB) {
333 tname = autobaud();
334 continue;
335 }
336 if (PS) {
337 tname = portselector();
338 continue;
339 }
340 if (CS)
341 clearscreen();
342 if (CL && *CL)
343 putpad(CL);
344 edithost(HE);
345
346 /*
347 * If this is the first time through this, and an
348 * issue file has been given, then send it.
349 */
350 if (first_time != 0 && IF != NULL) {
351 char buf[_POSIX2_LINE_MAX];
352 FILE *fd;
353
354 if ((fd = fopen(IF, "r")) != NULL) {
355 while (fgets(buf, sizeof(buf) - 1, fd) != NULL)
356 putf(buf);
357 fclose(fd);
358 }
359 }
360 first_time = 0;
361
362 if (IM && *IM)
363 putf(IM);
364 oflush();
365 if (setjmp(timeout)) {
366 tmode.c_ispeed = tmode.c_ospeed = 0;
367 (void)tcsetattr(0, TCSANOW, &tmode);
368 exit(1);
369 }
370 if (TO) {
371 signal(SIGALRM, dingdong);
372 alarm(TO);
373 }
374 if (AL) {
375 const char *p = AL;
376 char *q = name;
377
378 while (*p && q < &name[sizeof name - 1]) {
379 if (isupper(*p))
380 upper = 1;
381 else if (islower(*p))
382 lower = 1;
383 else if (isdigit(*p))
384 digit++;
385 *q++ = *p++;
386 }
387 } else if ((rval = getname()) == 2) {
388 execle(PP, "ppplogin", ttyn, (char *) 0, env);
389 syslog(LOG_ERR, "%s: %m", PP);
390 exit(1);
391 }
392
393 if (rval || AL) {
394 int i;
395
396 oflush();
397 alarm(0);
398 signal(SIGALRM, SIG_DFL);
399 if (name[0] == '-') {
400 xputs("user names may not start with '-'.");
401 continue;
402 }
403 if (!(upper || lower || digit))
404 continue;
405 setflags(2);
406 if (crmod) {
407 tmode.c_iflag |= ICRNL;
408 tmode.c_oflag |= ONLCR;
409 }
410 #if XXX
411 if (upper || UC)
412 tmode.sg_flags |= LCASE;
413 if (lower || LC)
414 tmode.sg_flags &= ~LCASE;
415 #endif
416 if (tcsetattr(0, TCSANOW, &tmode) < 0) {
417 syslog(LOG_ERR, "%s: %m", ttyn);
418 exit(1);
419 }
420 signal(SIGINT, SIG_DFL);
421 for (i = 0; environ[i] != (char *)0; i++)
422 env[i] = environ[i];
423 makeenv(&env[i]);
424
425 limit.rlim_max = RLIM_INFINITY;
426 limit.rlim_cur = RLIM_INFINITY;
427 (void)setrlimit(RLIMIT_CPU, &limit);
428 execle(LO, "login", AL ? "-fp" : "-p", "--", name,
429 (char *)0, env);
430 syslog(LOG_ERR, "%s: %m", LO);
431 exit(1);
432 }
433 alarm(0);
434 signal(SIGALRM, SIG_DFL);
435 signal(SIGINT, SIG_IGN);
436 if (NX && *NX)
437 tname = NX;
438 unlink(lockfile);
439 }
440 }
441
442 static int
443 getname()
444 {
445 int c;
446 char *np;
447 unsigned char cs;
448 int ppp_state, ppp_connection;
449
450 /*
451 * Interrupt may happen if we use CBREAK mode
452 */
453 if (setjmp(intrupt)) {
454 signal(SIGINT, SIG_IGN);
455 return (0);
456 }
457 signal(SIGINT, interrupt);
458 setflags(1);
459 prompt();
460 if (PF > 0) {
461 oflush();
462 sleep(PF);
463 PF = 0;
464 }
465 if (tcsetattr(0, TCSANOW, &tmode) < 0) {
466 syslog(LOG_ERR, "%s: %m", ttyn);
467 exit(1);
468 }
469 crmod = digit = lower = upper = 0;
470 ppp_state = ppp_connection = 0;
471 np = name;
472 for (;;) {
473 oflush();
474 if (read(STDIN_FILENO, &cs, 1) <= 0)
475 exit(0);
476 if ((c = cs&0177) == 0)
477 return (0);
478
479 /*
480 * PPP detection state machine..
481 * Look for sequences:
482 * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
483 * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
484 * See RFC1662.
485 * Derived from code from Michael Hancock <michaelh (at) cet.co.jp>
486 * and Erik 'PPP' Olson <eriko (at) wrq.com>
487 */
488 if (PP && cs == PPP_FRAME) {
489 ppp_state = 1;
490 } else if (ppp_state == 1 && cs == PPP_STATION) {
491 ppp_state = 2;
492 } else if (ppp_state == 2 && cs == PPP_ESCAPE) {
493 ppp_state = 3;
494 } else if ((ppp_state == 2 && cs == PPP_CONTROL) ||
495 (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
496 ppp_state = 4;
497 } else if (ppp_state == 4 && cs == PPP_LCP_HI) {
498 ppp_state = 5;
499 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
500 ppp_connection = 1;
501 break;
502 } else {
503 ppp_state = 0;
504 }
505
506 if (c == EOT)
507 exit(1);
508 if (c == '\r' || c == '\n' ||
509 np >= &name[LOGIN_NAME_MAX - 1]) {
510 *np = '\0';
511 putf("\r\n");
512 break;
513 }
514 if (islower(c))
515 lower = 1;
516 else if (isupper(c))
517 upper = 1;
518 else if (c == ERASE || c == '#' || c == '\b') {
519 if (np > name) {
520 np--;
521 if (cfgetospeed(&tmode) >= 1200)
522 xputs("\b \b");
523 else
524 putchr(cs);
525 }
526 continue;
527 } else if (c == KILL || c == '@') {
528 putchr(cs);
529 putchr('\r');
530 if (cfgetospeed(&tmode) < 1200)
531 putchr('\n');
532 /* this is the way they do it down under ... */
533 else if (np > name)
534 xputs(
535 " \r");
536 prompt();
537 np = name;
538 continue;
539 } else if (isdigit(c))
540 digit++;
541 if (IG && (c <= ' ' || c > 0176))
542 continue;
543 *np++ = c;
544 putchr(cs);
545 }
546 signal(SIGINT, SIG_IGN);
547 *np = 0;
548 if (c == '\r')
549 crmod = 1;
550 if ((upper && !lower && !LC) || UC)
551 for (np = name; *np; np++)
552 if (isupper(*np))
553 *np = tolower(*np);
554 return (1 + ppp_connection);
555 }
556
557 static void
558 putpad(s)
559 const char *s;
560 {
561 int pad = 0;
562 speed_t ospeed = cfgetospeed(&tmode);
563
564 if (isdigit(*s)) {
565 while (isdigit(*s)) {
566 pad *= 10;
567 pad += *s++ - '0';
568 }
569 pad *= 10;
570 if (*s == '.' && isdigit(s[1])) {
571 pad += s[1] - '0';
572 s += 2;
573 }
574 }
575
576 xputs(s);
577 /*
578 * If no delay needed, or output speed is
579 * not comprehensible, then don't try to delay.
580 */
581 if (pad == 0 || ospeed <= 0)
582 return;
583
584 /*
585 * Round up by a half a character frame, and then do the delay.
586 * Too bad there are no user program accessible programmed delays.
587 * Transmitting pad characters slows many terminals down and also
588 * loads the system.
589 */
590 pad = (pad * ospeed + 50000) / 100000;
591 while (pad--)
592 putchr(*PC);
593 }
594
595 static void
596 xputs(s)
597 const char *s;
598 {
599 while (*s)
600 putchr(*s++);
601 }
602
603 char outbuf[OBUFSIZ];
604 int obufcnt = 0;
605
606 static void
607 putchr(cc)
608 int cc;
609 {
610 char c;
611
612 c = cc;
613 if (!NP) {
614 c |= partab[c&0177] & 0200;
615 if (OP)
616 c ^= 0200;
617 }
618 if (!UB) {
619 outbuf[obufcnt++] = c;
620 if (obufcnt >= OBUFSIZ)
621 oflush();
622 } else
623 write(STDOUT_FILENO, &c, 1);
624 }
625
626 static void
627 oflush()
628 {
629 if (obufcnt)
630 write(STDOUT_FILENO, outbuf, obufcnt);
631 obufcnt = 0;
632 }
633
634 static void
635 prompt()
636 {
637
638 putf(LM);
639 if (CO)
640 putchr('\n');
641 }
642
643 static void
644 putf(cp)
645 const char *cp;
646 {
647 extern char editedhost[];
648 time_t t;
649 char *slash, db[100];
650
651 while (*cp) {
652 if (*cp != '%') {
653 putchr(*cp++);
654 continue;
655 }
656 switch (*++cp) {
657
658 case 't':
659 slash = strrchr(ttyn, '/');
660 if (slash == NULL)
661 xputs(ttyn);
662 else
663 xputs(&slash[1]);
664 break;
665
666 case 'h':
667 xputs(editedhost);
668 break;
669
670 case 'd':
671 (void)time(&t);
672 (void)strftime(db, sizeof(db),
673 /* SCCS eats %M% */
674 "%l:%M" "%p on %A, %d %B %Y", localtime(&t));
675 xputs(db);
676 break;
677
678 case 's':
679 xputs(kerninfo.sysname);
680 break;
681
682 case 'm':
683 xputs(kerninfo.machine);
684 break;
685
686 case 'r':
687 xputs(kerninfo.release);
688 break;
689
690 case 'v':
691 xputs(kerninfo.version);
692 break;
693
694 case '%':
695 putchr('%');
696 break;
697 }
698 if (*cp)
699 cp++;
700 }
701 }
702
703 static void
704 clearscreen()
705 {
706 struct ttyent *typ;
707 struct tinfo *tinfo;
708 char *buffer = NULL;
709 char *area = NULL;
710 char *cs;
711
712 if (rawttyn == NULL)
713 return;
714
715 typ = getttynam(rawttyn);
716
717 if ((typ == NULL) || (typ->ty_type == NULL) ||
718 (typ->ty_type[0] == 0))
719 return;
720
721 if (t_getent(&tinfo, typ->ty_type) <= 0)
722 return;
723
724 cs = t_agetstr(tinfo, "cl", &buffer, &area);
725 if (cs == NULL)
726 return;
727
728 putpad(cs);
729 free(buffer);
730 }
731