main.c revision 1.37 1 /* $NetBSD: main.c,v 1.37 2001/02/04 22:09:01 christos 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.37 2001/02/04 22:09:01 christos 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 **environ;
81 extern char *__progname;
82 extern char editedhost[];
83
84 /*
85 * Set the amount of running time that getty should accumulate
86 * before deciding that something is wrong and exit.
87 */
88 #define GETTY_TIMEOUT 60 /* seconds */
89
90 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
91
92 #define PPP_FRAME 0x7e /* PPP Framing character */
93 #define PPP_STATION 0xff /* "All Station" character */
94 #define PPP_ESCAPE 0x7d /* Escape Character */
95 #define PPP_CONTROL 0x03 /* PPP Control Field */
96 #define PPP_CONTROL_ESCAPED 0x23 /* PPP Control Field, escaped */
97 #define PPP_LCP_HI 0xc0 /* LCP protocol - high byte */
98 #define PPP_LCP_LOW 0x21 /* LCP protocol - low byte */
99
100 struct termios tmode, omode;
101
102 int crmod, digit, lower, upper;
103
104 char hostname[MAXHOSTNAMELEN + 1];
105 struct utsname kerninfo;
106 char name[LOGIN_NAME_MAX];
107 char dev[] = _PATH_DEV;
108 char ttyn[32];
109 char lockfile[512];
110 uid_t ttyowner;
111 char *rawttyn;
112
113 #define OBUFSIZ 128
114 #define TABBUFSIZ 512
115
116 char defent[TABBUFSIZ];
117 char tabent[TABBUFSIZ];
118
119 char *env[128];
120
121 char partab[] = {
122 0001,0201,0201,0001,0201,0001,0001,0201,
123 0202,0004,0003,0205,0005,0206,0201,0001,
124 0201,0001,0001,0201,0001,0201,0201,0001,
125 0001,0201,0201,0001,0201,0001,0001,0201,
126 0200,0000,0000,0200,0000,0200,0200,0000,
127 0000,0200,0200,0000,0200,0000,0000,0200,
128 0000,0200,0200,0000,0200,0000,0000,0200,
129 0200,0000,0000,0200,0000,0200,0200,0000,
130 0200,0000,0000,0200,0000,0200,0200,0000,
131 0000,0200,0200,0000,0200,0000,0000,0200,
132 0000,0200,0200,0000,0200,0000,0000,0200,
133 0200,0000,0000,0200,0000,0200,0200,0000,
134 0000,0200,0200,0000,0200,0000,0000,0200,
135 0200,0000,0000,0200,0000,0200,0200,0000,
136 0200,0000,0000,0200,0000,0200,0200,0000,
137 0000,0200,0200,0000,0200,0000,0000,0201
138 };
139
140 #define ERASE tmode.c_cc[VERASE]
141 #define KILL tmode.c_cc[VKILL]
142 #define EOT tmode.c_cc[VEOF]
143
144 static void dingdong __P((int));
145 static void interrupt __P((int));
146 static void clearscreen __P((void));
147 void timeoverrun __P((int));
148
149 jmp_buf timeout;
150
151 static void
152 dingdong(signo)
153 int signo;
154 {
155
156 alarm(0);
157 signal(SIGALRM, SIG_DFL);
158 longjmp(timeout, 1);
159 }
160
161 jmp_buf intrupt;
162
163 static void
164 interrupt(signo)
165 int signo;
166 {
167
168 signal(SIGINT, interrupt);
169 longjmp(intrupt, 1);
170 }
171
172 /*
173 * Action to take when getty is running too long.
174 */
175 void
176 timeoverrun(signo)
177 int signo;
178 {
179
180 syslog(LOG_ERR, "getty exiting due to excessive running time");
181 exit(1);
182 }
183
184 int main __P((int, char **));
185 static int getname __P((void));
186 static void oflush __P((void));
187 static void prompt __P((void));
188 static void putchr __P((int));
189 static void putf __P((const char *));
190 static void putpad __P((const char *));
191 static void xputs __P((const char *));
192
193 int
194 main(argc, argv)
195 int argc;
196 char *argv[];
197 {
198 char *tname;
199 int repcnt = 0, failopenlogged = 0, uugetty = 0, first_time = 1;
200 struct rlimit limit;
201 struct passwd *pw;
202 int rval;
203
204 #ifdef __GNUC__
205 (void)&tname; /* XXX gcc -Wall */
206 #endif
207
208 signal(SIGINT, SIG_IGN);
209 /*
210 signal(SIGQUIT, SIG_DFL);
211 */
212 openlog("getty", LOG_PID, LOG_AUTH);
213 gethostname(hostname, sizeof(hostname));
214 hostname[sizeof(hostname) - 1] = '\0';
215 if (hostname[0] == '\0')
216 strcpy(hostname, "Amnesiac");
217 uname(&kerninfo);
218
219 if (__progname[0] == 'u' && __progname[1] == 'u')
220 uugetty = 1;
221
222 /*
223 * Find id of uucp login (if present) so we can chown tty properly.
224 */
225 if (uugetty && (pw = getpwnam("uucp")))
226 ttyowner = pw->pw_uid;
227 else
228 ttyowner = 0;
229
230 /*
231 * Limit running time to deal with broken or dead lines.
232 */
233 (void)signal(SIGXCPU, timeoverrun);
234 limit.rlim_max = RLIM_INFINITY;
235 limit.rlim_cur = GETTY_TIMEOUT;
236 (void)setrlimit(RLIMIT_CPU, &limit);
237
238 /*
239 * The following is a work around for vhangup interactions
240 * which cause great problems getting window systems started.
241 * If the tty line is "-", we do the old style getty presuming
242 * that the file descriptors are already set up for us.
243 * J. Gettys - MIT Project Athena.
244 */
245 if (argc <= 2 || strcmp(argv[2], "-") == 0) {
246 strlcpy(ttyn, ttyname(0), sizeof(ttyn));
247 }
248 else {
249 int i;
250
251 rawttyn = argv[2];
252 strlcpy(ttyn, dev, sizeof(ttyn));
253 strlcat(ttyn, argv[2], sizeof(ttyn));
254
255 if (uugetty) {
256 chown(ttyn, ttyowner, 0);
257 strcpy(lockfile, _PATH_LOCK);
258 strlcat(lockfile, argv[2], sizeof(lockfile));
259 /* wait for lockfiles to go away before we try to open */
260 if ( pidlock(lockfile, 0, 0, 0) != 0 ) {
261 syslog(LOG_ERR, "%s: can't create lockfile", ttyn);
262 exit(1);
263 }
264 unlink(lockfile);
265 }
266 if (strcmp(argv[0], "+") != 0) {
267 chown(ttyn, ttyowner, 0);
268 chmod(ttyn, 0600);
269 revoke(ttyn);
270 if (ttyaction(ttyn, "getty", "root"))
271 syslog(LOG_WARNING, "%s: ttyaction failed", ttyn);
272 /*
273 * Delay the open so DTR stays down long enough to be detected.
274 */
275 sleep(2);
276 while ((i = open(ttyn, O_RDWR)) == -1) {
277 if ((repcnt % 10 == 0) &&
278 (errno != ENXIO || !failopenlogged)) {
279 syslog(LOG_WARNING, "%s: %m", ttyn);
280 closelog();
281 failopenlogged = 1;
282 }
283 repcnt++;
284 sleep(60);
285 }
286 if (uugetty && pidlock(lockfile, 0, 0, 0) != 0) {
287 syslog(LOG_ERR, "%s: can't create lockfile", ttyn);
288 exit(1);
289 }
290 (void) chown(lockfile, ttyowner, 0);
291 login_tty(i);
292 }
293 }
294
295 /* Start with default tty settings */
296 if (tcgetattr(0, &tmode) < 0) {
297 syslog(LOG_ERR, "%s: %m", ttyn);
298 exit(1);
299 }
300 omode = tmode;
301
302 gettable("default", defent);
303 gendefaults();
304 tname = "default";
305 if (argc > 1)
306 tname = argv[1];
307 for (;;) {
308 int off;
309
310 gettable(tname, tabent);
311 if (OPset || EPset || APset)
312 APset++, OPset++, EPset++;
313 setdefaults();
314 off = 0;
315 (void)tcflush(0, TCIOFLUSH); /* clear out the crap */
316 ioctl(0, FIONBIO, &off); /* turn off non-blocking mode */
317 ioctl(0, FIOASYNC, &off); /* ditto for async mode */
318
319 if (IS)
320 cfsetispeed(&tmode, IS);
321 else if (SP)
322 cfsetispeed(&tmode, SP);
323 if (OS)
324 cfsetospeed(&tmode, OS);
325 else if (SP)
326 cfsetospeed(&tmode, SP);
327 setflags(0);
328 setchars();
329 if (tcsetattr(0, TCSANOW, &tmode) < 0) {
330 syslog(LOG_ERR, "%s: %m", ttyn);
331 exit(1);
332 }
333 if (AB) {
334 tname = autobaud();
335 continue;
336 }
337 if (PS) {
338 tname = portselector();
339 continue;
340 }
341 if (CS)
342 clearscreen();
343 if (CL && *CL)
344 putpad(CL);
345 edithost(HE);
346
347 /*
348 * If this is the first time through this, and an
349 * issue file has been given, then send it.
350 */
351 if (first_time != 0 && IF != NULL) {
352 char buf[_POSIX2_LINE_MAX];
353 FILE *fd;
354
355 if ((fd = fopen(IF, "r")) != NULL) {
356 while (fgets(buf, sizeof(buf) - 1, fd) != NULL)
357 putf(buf);
358 fclose(fd);
359 }
360 }
361 first_time = 0;
362
363 if (IM && *IM)
364 putf(IM);
365 oflush();
366 if (setjmp(timeout)) {
367 tmode.c_ispeed = tmode.c_ospeed = 0;
368 (void)tcsetattr(0, TCSANOW, &tmode);
369 exit(1);
370 }
371 if (TO) {
372 signal(SIGALRM, dingdong);
373 alarm(TO);
374 }
375 if (AL) {
376 const char *p = AL;
377 char *q = name;
378
379 while (*p && q < &name[sizeof name - 1]) {
380 if (isupper(*p))
381 upper = 1;
382 else if (islower(*p))
383 lower = 1;
384 else if (isdigit(*p))
385 digit++;
386 *q++ = *p++;
387 }
388 } else if ((rval = getname()) == 2) {
389 execle(PP, "ppplogin", ttyn, (char *) 0, env);
390 syslog(LOG_ERR, "%s: %m", PP);
391 exit(1);
392 }
393
394 if (rval || AL) {
395 int i;
396
397 oflush();
398 alarm(0);
399 signal(SIGALRM, SIG_DFL);
400 if (name[0] == '-') {
401 xputs("user names may not start with '-'.");
402 continue;
403 }
404 if (!(upper || lower || digit))
405 continue;
406 setflags(2);
407 if (crmod) {
408 tmode.c_iflag |= ICRNL;
409 tmode.c_oflag |= ONLCR;
410 }
411 #if XXX
412 if (upper || UC)
413 tmode.sg_flags |= LCASE;
414 if (lower || LC)
415 tmode.sg_flags &= ~LCASE;
416 #endif
417 if (tcsetattr(0, TCSANOW, &tmode) < 0) {
418 syslog(LOG_ERR, "%s: %m", ttyn);
419 exit(1);
420 }
421 signal(SIGINT, SIG_DFL);
422 for (i = 0; environ[i] != (char *)0; i++)
423 env[i] = environ[i];
424 makeenv(&env[i]);
425
426 limit.rlim_max = RLIM_INFINITY;
427 limit.rlim_cur = RLIM_INFINITY;
428 (void)setrlimit(RLIMIT_CPU, &limit);
429 execle(LO, "login", AL ? "-fp" : "-p", "--", name,
430 (char *)0, env);
431 syslog(LOG_ERR, "%s: %m", LO);
432 exit(1);
433 }
434 alarm(0);
435 signal(SIGALRM, SIG_DFL);
436 signal(SIGINT, SIG_IGN);
437 if (NX && *NX)
438 tname = NX;
439 unlink(lockfile);
440 }
441 }
442
443 static int
444 getname()
445 {
446 int c;
447 char *np;
448 unsigned char cs;
449 int ppp_state, ppp_connection;
450
451 /*
452 * Interrupt may happen if we use CBREAK mode
453 */
454 if (setjmp(intrupt)) {
455 signal(SIGINT, SIG_IGN);
456 return (0);
457 }
458 signal(SIGINT, interrupt);
459 setflags(1);
460 prompt();
461 if (PF > 0) {
462 oflush();
463 sleep(PF);
464 PF = 0;
465 }
466 if (tcsetattr(0, TCSANOW, &tmode) < 0) {
467 syslog(LOG_ERR, "%s: %m", ttyn);
468 exit(1);
469 }
470 crmod = digit = lower = upper = 0;
471 ppp_state = ppp_connection = 0;
472 np = name;
473 for (;;) {
474 oflush();
475 if (read(STDIN_FILENO, &cs, 1) <= 0)
476 exit(0);
477 if ((c = cs&0177) == 0)
478 return (0);
479
480 /*
481 * PPP detection state machine..
482 * Look for sequences:
483 * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
484 * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
485 * See RFC1662.
486 * Derived from code from Michael Hancock <michaelh (at) cet.co.jp>
487 * and Erik 'PPP' Olson <eriko (at) wrq.com>
488 */
489 if (PP && cs == PPP_FRAME) {
490 ppp_state = 1;
491 } else if (ppp_state == 1 && cs == PPP_STATION) {
492 ppp_state = 2;
493 } else if (ppp_state == 2 && cs == PPP_ESCAPE) {
494 ppp_state = 3;
495 } else if ((ppp_state == 2 && cs == PPP_CONTROL) ||
496 (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
497 ppp_state = 4;
498 } else if (ppp_state == 4 && cs == PPP_LCP_HI) {
499 ppp_state = 5;
500 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
501 ppp_connection = 1;
502 break;
503 } else {
504 ppp_state = 0;
505 }
506
507 if (c == EOT)
508 exit(1);
509 if (c == '\r' || c == '\n' ||
510 np >= &name[LOGIN_NAME_MAX - 1]) {
511 *np = '\0';
512 putf("\r\n");
513 break;
514 }
515 if (islower(c))
516 lower = 1;
517 else if (isupper(c))
518 upper = 1;
519 else if (c == ERASE || c == '#' || c == '\b') {
520 if (np > name) {
521 np--;
522 if (cfgetospeed(&tmode) >= 1200)
523 xputs("\b \b");
524 else
525 putchr(cs);
526 }
527 continue;
528 } else if (c == KILL || c == '@') {
529 putchr(cs);
530 putchr('\r');
531 if (cfgetospeed(&tmode) < 1200)
532 putchr('\n');
533 /* this is the way they do it down under ... */
534 else if (np > name)
535 xputs(
536 " \r");
537 prompt();
538 np = name;
539 continue;
540 } else if (isdigit(c))
541 digit++;
542 if (IG && (c <= ' ' || c > 0176))
543 continue;
544 *np++ = c;
545 putchr(cs);
546 }
547 signal(SIGINT, SIG_IGN);
548 *np = 0;
549 if (c == '\r')
550 crmod = 1;
551 if ((upper && !lower && !LC) || UC)
552 for (np = name; *np; np++)
553 if (isupper(*np))
554 *np = tolower(*np);
555 return (1 + ppp_connection);
556 }
557
558 static void
559 putpad(s)
560 const char *s;
561 {
562 int pad = 0;
563 speed_t ospeed = cfgetospeed(&tmode);
564
565 if (isdigit(*s)) {
566 while (isdigit(*s)) {
567 pad *= 10;
568 pad += *s++ - '0';
569 }
570 pad *= 10;
571 if (*s == '.' && isdigit(s[1])) {
572 pad += s[1] - '0';
573 s += 2;
574 }
575 }
576
577 xputs(s);
578 /*
579 * If no delay needed, or output speed is
580 * not comprehensible, then don't try to delay.
581 */
582 if (pad == 0 || ospeed <= 0)
583 return;
584
585 /*
586 * Round up by a half a character frame, and then do the delay.
587 * Too bad there are no user program accessible programmed delays.
588 * Transmitting pad characters slows many terminals down and also
589 * loads the system.
590 */
591 pad = (pad * ospeed + 50000) / 100000;
592 while (pad--)
593 putchr(*PC);
594 }
595
596 static void
597 xputs(s)
598 const char *s;
599 {
600 while (*s)
601 putchr(*s++);
602 }
603
604 char outbuf[OBUFSIZ];
605 int obufcnt = 0;
606
607 static void
608 putchr(cc)
609 int cc;
610 {
611 char c;
612
613 c = cc;
614 if (!NP) {
615 c |= partab[c&0177] & 0200;
616 if (OP)
617 c ^= 0200;
618 }
619 if (!UB) {
620 outbuf[obufcnt++] = c;
621 if (obufcnt >= OBUFSIZ)
622 oflush();
623 } else
624 write(STDOUT_FILENO, &c, 1);
625 }
626
627 static void
628 oflush()
629 {
630 if (obufcnt)
631 write(STDOUT_FILENO, outbuf, obufcnt);
632 obufcnt = 0;
633 }
634
635 static void
636 prompt()
637 {
638
639 putf(LM);
640 if (CO)
641 putchr('\n');
642 }
643
644 static void
645 putf(cp)
646 const char *cp;
647 {
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