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