xref: /freebsd/libexec/getty/main.c (revision 6829dae12bb055451fa467da4589c43bd03b1e64)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
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. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #ifndef lint
33 static const char copyright[] =
34 "@(#) Copyright (c) 1980, 1993\n\
35 	The Regents of the University of California.  All rights reserved.\n";
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)from: main.c	8.1 (Berkeley) 6/20/93";
41 #endif
42 #endif /* not lint */
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45 
46 #include <sys/param.h>
47 #include <sys/ioctl.h>
48 #include <sys/time.h>
49 #include <sys/resource.h>
50 #include <sys/stat.h>
51 #include <sys/ttydefaults.h>
52 #include <sys/utsname.h>
53 
54 #include <ctype.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <locale.h>
58 #include <libutil.h>
59 #include <setjmp.h>
60 #include <signal.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <syslog.h>
64 #include <termios.h>
65 #include <time.h>
66 #include <unistd.h>
67 
68 #include "gettytab.h"
69 #include "extern.h"
70 #include "pathnames.h"
71 
72 /*
73  * Set the amount of running time that getty should accumulate
74  * before deciding that something is wrong and exit.
75  */
76 #define GETTY_TIMEOUT	60 /* seconds */
77 
78 #undef CTRL
79 #define CTRL(x)  (x&037)
80 
81 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
82 
83 #define PPP_FRAME           0x7e  /* PPP Framing character */
84 #define PPP_STATION         0xff  /* "All Station" character */
85 #define PPP_ESCAPE          0x7d  /* Escape Character */
86 #define PPP_CONTROL         0x03  /* PPP Control Field */
87 #define PPP_CONTROL_ESCAPED 0x23  /* PPP Control Field, escaped */
88 #define PPP_LCP_HI          0xc0  /* LCP protocol - high byte */
89 #define PPP_LCP_LOW         0x21  /* LCP protocol - low byte */
90 
91 /* original mode; flags've been reset using values from <sys/ttydefaults.h> */
92 struct termios omode;
93 /* current mode */
94 struct termios tmode;
95 
96 static int crmod, digit, lower, upper;
97 
98 char	hostname[MAXHOSTNAMELEN];
99 static char	name[MAXLOGNAME*3];
100 static char	dev[] = _PATH_DEV;
101 static char	ttyn[32];
102 
103 #define	OBUFSIZ		128
104 #define	TABBUFSIZ	512
105 
106 static char	defent[TABBUFSIZ];
107 static char	tabent[TABBUFSIZ];
108 static const char	*tname;
109 
110 static char	*env[128];
111 
112 static char partab[] = {
113 	0001,0201,0201,0001,0201,0001,0001,0201,
114 	0202,0004,0003,0205,0005,0206,0201,0001,
115 	0201,0001,0001,0201,0001,0201,0201,0001,
116 	0001,0201,0201,0001,0201,0001,0001,0201,
117 	0200,0000,0000,0200,0000,0200,0200,0000,
118 	0000,0200,0200,0000,0200,0000,0000,0200,
119 	0000,0200,0200,0000,0200,0000,0000,0200,
120 	0200,0000,0000,0200,0000,0200,0200,0000,
121 	0200,0000,0000,0200,0000,0200,0200,0000,
122 	0000,0200,0200,0000,0200,0000,0000,0200,
123 	0000,0200,0200,0000,0200,0000,0000,0200,
124 	0200,0000,0000,0200,0000,0200,0200,0000,
125 	0000,0200,0200,0000,0200,0000,0000,0200,
126 	0200,0000,0000,0200,0000,0200,0200,0000,
127 	0200,0000,0000,0200,0000,0200,0200,0000,
128 	0000,0200,0200,0000,0200,0000,0000,0201
129 };
130 
131 #define	ERASE	tmode.c_cc[VERASE]
132 #define	KILL	tmode.c_cc[VKILL]
133 #define	EOT	tmode.c_cc[VEOF]
134 
135 #define	puts	Gputs
136 
137 static void	defttymode(void);
138 static void	dingdong(int);
139 static void	dogettytab(void);
140 static int	getname(void);
141 static void	interrupt(int);
142 static void	oflush(void);
143 static void	prompt(void);
144 static void	putchr(int);
145 static void	putf(const char *);
146 static void	putpad(const char *);
147 static void	puts(const char *);
148 static void	timeoverrun(int);
149 static char	*get_line(int);
150 static void	setttymode(int);
151 static int	opentty(const char *, int);
152 
153 static jmp_buf timeout;
154 
155 static void
156 dingdong(int signo __unused)
157 {
158 	alarm(0);
159 	longjmp(timeout, 1);
160 }
161 
162 static jmp_buf	intrupt;
163 
164 static void
165 interrupt(int signo __unused)
166 {
167 	longjmp(intrupt, 1);
168 }
169 
170 /*
171  * Action to take when getty is running too long.
172  */
173 static void
174 timeoverrun(int signo __unused)
175 {
176 
177 	syslog(LOG_ERR, "getty exiting due to excessive running time");
178 	exit(1);
179 }
180 
181 int
182 main(int argc, char *argv[])
183 {
184 	int first_sleep = 1, first_time = 1;
185 	struct rlimit limit;
186 	int rval;
187 
188 	signal(SIGINT, SIG_IGN);
189 	signal(SIGQUIT, SIG_IGN);
190 
191 	openlog("getty", LOG_CONS|LOG_PID, LOG_AUTH);
192 	gethostname(hostname, sizeof(hostname) - 1);
193 	hostname[sizeof(hostname) - 1] = '\0';
194 	if (hostname[0] == '\0')
195 		strcpy(hostname, "Amnesiac");
196 
197 	/*
198 	 * Limit running time to deal with broken or dead lines.
199 	 */
200 	(void)signal(SIGXCPU, timeoverrun);
201 	limit.rlim_max = RLIM_INFINITY;
202 	limit.rlim_cur = GETTY_TIMEOUT;
203 	(void)setrlimit(RLIMIT_CPU, &limit);
204 
205 	gettable("default", defent);
206 	gendefaults();
207 	tname = "default";
208 	if (argc > 1)
209 		tname = argv[1];
210 
211 	/*
212 	 * The following is a work around for vhangup interactions
213 	 * which cause great problems getting window systems started.
214 	 * If the tty line is "-", we do the old style getty presuming
215 	 * that the file descriptors are already set up for us.
216 	 * J. Gettys - MIT Project Athena.
217 	 */
218 	if (argc <= 2 || strcmp(argv[2], "-") == 0)
219 	    strcpy(ttyn, ttyname(STDIN_FILENO));
220 	else {
221 	    strcpy(ttyn, dev);
222 	    strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
223 	    if (strcmp(argv[0], "+") != 0) {
224 		chown(ttyn, 0, 0);
225 		chmod(ttyn, 0600);
226 		revoke(ttyn);
227 
228 		/*
229 		 * Do the first scan through gettytab.
230 		 * Terminal mode parameters will be wrong until
231 		 * defttymode() called, but they're irrelevant for
232 		 * the initial setup of the terminal device.
233 		 */
234 		dogettytab();
235 
236 		/*
237 		 * Init or answer modem sequence has been specified.
238 		 */
239 		if (IC || AC) {
240 			if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
241 				exit(1);
242 			defttymode();
243 			setttymode(1);
244 		}
245 
246 		if (IC) {
247 			if (getty_chat(IC, CT, DC) > 0) {
248 				syslog(LOG_ERR, "modem init problem on %s", ttyn);
249 				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
250 				exit(1);
251 			}
252 		}
253 
254 		if (AC) {
255 			fd_set rfds;
256 			struct timeval to;
257 			int i;
258 
259 			FD_ZERO(&rfds);
260 			FD_SET(0, &rfds);
261         		to.tv_sec = RT;
262         		to.tv_usec = 0;
263 			i = select(32, &rfds, NULL, NULL, RT ? &to : NULL);
264         		if (i < 0) {
265 				syslog(LOG_ERR, "select %s: %m", ttyn);
266 			} else if (i == 0) {
267 				syslog(LOG_NOTICE, "recycle tty %s", ttyn);
268 				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
269 				exit(0);  /* recycle for init */
270 			}
271 			i = getty_chat(AC, CT, DC);
272 			if (i > 0) {
273 				syslog(LOG_ERR, "modem answer problem on %s", ttyn);
274 				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
275 				exit(1);
276 			}
277 		} else { /* maybe blocking open */
278 			if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 )))
279 				exit(1);
280 		}
281 	    }
282 	}
283 
284 	defttymode();
285 	for (;;) {
286 
287 		/*
288 		 * if a delay was specified then sleep for that
289 		 * number of seconds before writing the initial prompt
290 		 */
291 		if (first_sleep && DE) {
292 		    sleep(DE);
293 		    /* remove any noise */
294 		    (void)tcflush(STDIN_FILENO, TCIOFLUSH);
295 		}
296 		first_sleep = 0;
297 
298 		setttymode(0);
299 		if (AB) {
300 			tname = autobaud();
301 			dogettytab();
302 			continue;
303 		}
304 		if (PS) {
305 			tname = portselector();
306 			dogettytab();
307 			continue;
308 		}
309 		if (CL && *CL)
310 			putpad(CL);
311 		edithost(HE);
312 
313 		/* if this is the first time through this, and an
314 		   issue file has been given, then send it */
315 		if (first_time && IF) {
316 			int fd;
317 
318 			if ((fd = open(IF, O_RDONLY)) != -1) {
319 				char * cp;
320 
321 				while ((cp = get_line(fd)) != NULL) {
322 					  putf(cp);
323 				}
324 				close(fd);
325 			}
326 		}
327 		first_time = 0;
328 
329 		if (IMP && *IMP && !(PL && PP))
330 			system(IMP);
331 		if (IM && *IM && !(PL && PP))
332 			putf(IM);
333 		if (setjmp(timeout)) {
334 			cfsetispeed(&tmode, B0);
335 			cfsetospeed(&tmode, B0);
336 			(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
337 			exit(1);
338 		}
339 		if (TO) {
340 			signal(SIGALRM, dingdong);
341 			alarm(TO);
342 		}
343 
344 		rval = 0;
345 		if (AL) {
346 			const char *p = AL;
347 			char *q = name;
348 
349 			while (*p && q < &name[sizeof name - 1]) {
350 				if (isupper(*p))
351 					upper = 1;
352 				else if (islower(*p))
353 					lower = 1;
354 				else if (isdigit(*p))
355 					digit = 1;
356 				*q++ = *p++;
357 			}
358 		} else if (!(PL && PP))
359 			rval = getname();
360 		if (rval == 2 || (PL && PP)) {
361 			oflush();
362 			alarm(0);
363 			limit.rlim_max = RLIM_INFINITY;
364 			limit.rlim_cur = RLIM_INFINITY;
365 			(void)setrlimit(RLIMIT_CPU, &limit);
366 			execle(PP, "ppplogin", ttyn, (char *) 0, env);
367 			syslog(LOG_ERR, "%s: %m", PP);
368 			exit(1);
369 		} else if (rval || AL) {
370 			int i;
371 
372 			oflush();
373 			alarm(0);
374 			signal(SIGALRM, SIG_DFL);
375 			if (name[0] == '\0')
376 				continue;
377 			if (name[0] == '-') {
378 				puts("user names may not start with '-'.");
379 				continue;
380 			}
381 			if (!(upper || lower || digit)) {
382 				if (AL) {
383 					syslog(LOG_ERR,
384 					    "invalid auto-login name: %s", AL);
385 					exit(1);
386 				} else
387 					continue;
388 			}
389 			set_flags(2);
390 			if (crmod) {
391 				tmode.c_iflag |= ICRNL;
392 				tmode.c_oflag |= ONLCR;
393 			}
394 #if REALLY_OLD_TTYS
395 			if (upper || UC)
396 				tmode.sg_flags |= LCASE;
397 			if (lower || LC)
398 				tmode.sg_flags &= ~LCASE;
399 #endif
400 			if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
401 				syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
402 				exit(1);
403 			}
404 			signal(SIGINT, SIG_DFL);
405 			for (i = 0; environ[i] != (char *)0; i++)
406 				env[i] = environ[i];
407 			makeenv(&env[i]);
408 
409 			limit.rlim_max = RLIM_INFINITY;
410 			limit.rlim_cur = RLIM_INFINITY;
411 			(void)setrlimit(RLIMIT_CPU, &limit);
412 			execle(LO, "login", AL ? "-fp" : "-p", name,
413 			    (char *) 0, env);
414 			syslog(LOG_ERR, "%s: %m", LO);
415 			exit(1);
416 		}
417 		alarm(0);
418 		signal(SIGALRM, SIG_DFL);
419 		signal(SIGINT, SIG_IGN);
420 		if (NX && *NX) {
421 			tname = NX;
422 			dogettytab();
423 		}
424 	}
425 }
426 
427 static int
428 opentty(const char *tty, int flags)
429 {
430 	int failopenlogged = 0, i, saved_errno;
431 
432 	while ((i = open(tty, flags)) == -1)
433 	{
434 		saved_errno = errno;
435 		if (!failopenlogged) {
436 			syslog(LOG_ERR, "open %s: %m", tty);
437 			failopenlogged = 1;
438 		}
439 		if (saved_errno == ENOENT)
440 			return 0;
441 		sleep(60);
442 	}
443 	if (login_tty(i) < 0) {
444 		if (daemon(0,0) < 0) {
445 			syslog(LOG_ERR,"daemon: %m");
446 			close(i);
447 			return 0;
448 		}
449 		if (login_tty(i) < 0) {
450 			syslog(LOG_ERR, "login_tty %s: %m", tty);
451 			close(i);
452 			return 0;
453 		}
454 	}
455 	return 1;
456 }
457 
458 static void
459 defttymode(void)
460 {
461 	struct termios def;
462 
463 	/* Start with default tty settings. */
464 	if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
465 		syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
466 		exit(1);
467 	}
468 	omode = tmode; /* fill c_cc for dogettytab() */
469 	dogettytab();
470 	/*
471 	 * Don't rely on the driver too much, and initialize crucial
472 	 * things according to <sys/ttydefaults.h>.  Avoid clobbering
473 	 * the c_cc[] settings however, the console drivers might wish
474 	 * to leave their idea of the preferred VERASE key value
475 	 * there.
476 	 */
477 	cfmakesane(&def);
478 	tmode.c_iflag = def.c_iflag;
479 	tmode.c_oflag = def.c_oflag;
480 	tmode.c_lflag = def.c_lflag;
481 	tmode.c_cflag = def.c_cflag;
482 	if (NC)
483 		tmode.c_cflag |= CLOCAL;
484 	omode = tmode;
485 }
486 
487 static void
488 setttymode(int raw)
489 {
490 	int off = 0;
491 
492 	(void)tcflush(STDIN_FILENO, TCIOFLUSH);	/* clear out the crap */
493 	ioctl(STDIN_FILENO, FIONBIO, &off);	/* turn off non-blocking mode */
494 	ioctl(STDIN_FILENO, FIOASYNC, &off);	/* ditto for async mode */
495 
496 	if (IS)
497 		cfsetispeed(&tmode, speed(IS));
498 	else if (SP)
499 		cfsetispeed(&tmode, speed(SP));
500 	if (OS)
501 		cfsetospeed(&tmode, speed(OS));
502 	else if (SP)
503 		cfsetospeed(&tmode, speed(SP));
504 	set_flags(0);
505 	setchars();
506 	if (raw)
507 		cfmakeraw(&tmode);
508 	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
509 		syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
510 		exit(1);
511 	}
512 }
513 
514 
515 static int
516 getname(void)
517 {
518 	int c;
519 	char *np;
520 	unsigned char cs;
521 	int ppp_state = 0;
522 	int ppp_connection = 0;
523 
524 	/*
525 	 * Interrupt may happen if we use CBREAK mode
526 	 */
527 	if (setjmp(intrupt)) {
528 		signal(SIGINT, SIG_IGN);
529 		return (0);
530 	}
531 	signal(SIGINT, interrupt);
532 	set_flags(1);
533 	prompt();
534 	oflush();
535 	if (PF > 0) {
536 		sleep(PF);
537 		PF = 0;
538 	}
539 	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
540 		syslog(LOG_ERR, "%s: %m", ttyn);
541 		exit(1);
542 	}
543 	crmod = digit = lower = upper = 0;
544 	np = name;
545 	for (;;) {
546 		oflush();
547 		if (read(STDIN_FILENO, &cs, 1) <= 0)
548 			exit(0);
549 		if ((c = cs&0177) == 0)
550 			return (0);
551 
552 		/* PPP detection state machine..
553 		   Look for sequences:
554 		   PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
555 		   PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
556 		   See RFC1662.
557 		   Derived from code from Michael Hancock, <michaelh@cet.co.jp>
558 		   and Erik 'PPP' Olson, <eriko@wrq.com>
559 		 */
560 
561 		if (PP && (cs == PPP_FRAME)) {
562 			ppp_state = 1;
563 		} else if (ppp_state == 1 && cs == PPP_STATION) {
564 			ppp_state = 2;
565 		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
566 			ppp_state = 3;
567 		} else if ((ppp_state == 2 && cs == PPP_CONTROL)
568 			|| (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
569 			ppp_state = 4;
570 		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
571 			ppp_state = 5;
572 		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
573 			ppp_connection = 1;
574 			break;
575 		} else {
576 			ppp_state = 0;
577 		}
578 
579 		if (c == EOT || c == CTRL('d'))
580 			exit(0);
581 		if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
582 			putf("\r\n");
583 			break;
584 		}
585 		if (islower(c))
586 			lower = 1;
587 		else if (isupper(c))
588 			upper = 1;
589 		else if (c == ERASE || c == '\b' || c == 0177) {
590 			if (np > name) {
591 				np--;
592 				if (cfgetospeed(&tmode) >= 1200)
593 					puts("\b \b");
594 				else
595 					putchr(cs);
596 			}
597 			continue;
598 		} else if (c == KILL || c == CTRL('u')) {
599 			putchr('\r');
600 			if (cfgetospeed(&tmode) < 1200)
601 				putchr('\n');
602 			/* this is the way they do it down under ... */
603 			else if (np > name)
604 				puts("                                     \r");
605 			prompt();
606 			digit = lower = upper = 0;
607 			np = name;
608 			continue;
609 		} else if (isdigit(c))
610 			digit = 1;
611 		if (IG && (c <= ' ' || c > 0176))
612 			continue;
613 		*np++ = c;
614 		putchr(cs);
615 	}
616 	signal(SIGINT, SIG_IGN);
617 	*np = 0;
618 	if (c == '\r')
619 		crmod = 1;
620 	if ((upper && !lower && !LC) || UC)
621 		for (np = name; *np; np++)
622 			if (isupper(*np))
623 				*np = tolower(*np);
624 	return (1 + ppp_connection);
625 }
626 
627 static void
628 putpad(const char *s)
629 {
630 	int pad = 0;
631 	speed_t ospeed = cfgetospeed(&tmode);
632 
633 	if (isdigit(*s)) {
634 		while (isdigit(*s)) {
635 			pad *= 10;
636 			pad += *s++ - '0';
637 		}
638 		pad *= 10;
639 		if (*s == '.' && isdigit(s[1])) {
640 			pad += s[1] - '0';
641 			s += 2;
642 		}
643 	}
644 
645 	puts(s);
646 	/*
647 	 * If no delay needed, or output speed is
648 	 * not comprehensible, then don't try to delay.
649 	 */
650 	if (pad == 0 || ospeed <= 0)
651 		return;
652 
653 	/*
654 	 * Round up by a half a character frame, and then do the delay.
655 	 * Too bad there are no user program accessible programmed delays.
656 	 * Transmitting pad characters slows many terminals down and also
657 	 * loads the system.
658 	 */
659 	pad = (pad * ospeed + 50000) / 100000;
660 	while (pad--)
661 		putchr(*PC);
662 }
663 
664 static void
665 puts(const char *s)
666 {
667 	while (*s)
668 		putchr(*s++);
669 }
670 
671 static char	outbuf[OBUFSIZ];
672 static int	obufcnt = 0;
673 
674 static void
675 putchr(int cc)
676 {
677 	char c;
678 
679 	c = cc;
680 	if (!NP) {
681 		c |= partab[c&0177] & 0200;
682 		if (OP)
683 			c ^= 0200;
684 	}
685 	if (!UB) {
686 		outbuf[obufcnt++] = c;
687 		if (obufcnt >= OBUFSIZ)
688 			oflush();
689 	} else
690 		write(STDOUT_FILENO, &c, 1);
691 }
692 
693 static void
694 oflush(void)
695 {
696 	if (obufcnt)
697 		write(STDOUT_FILENO, outbuf, obufcnt);
698 	obufcnt = 0;
699 }
700 
701 static void
702 prompt(void)
703 {
704 
705 	putf(LM);
706 	if (CO)
707 		putchr('\n');
708 }
709 
710 
711 static char *
712 get_line(int fd)
713 {
714 	size_t i = 0;
715 	static char linebuf[512];
716 
717 	/*
718 	 * This is certainly slow, but it avoids having to include
719 	 * stdio.h unnecessarily. Issue files should be small anyway.
720 	 */
721 	while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
722 		if (linebuf[i] == '\n') {
723 			/* Don't rely on newline mode, assume raw */
724 			linebuf[i++] = '\r';
725 			linebuf[i++] = '\n';
726 			linebuf[i] = '\0';
727 			return linebuf;
728 		}
729 		++i;
730 	}
731 	linebuf[i] = '\0';
732 	return i ? linebuf : 0;
733 }
734 
735 static void
736 putf(const char *cp)
737 {
738 	time_t t;
739 	char *slash, db[100];
740 
741 	static struct utsname kerninfo;
742 
743 	if (!*kerninfo.sysname)
744 		uname(&kerninfo);
745 
746 	while (*cp) {
747 		if (*cp != '%') {
748 			putchr(*cp++);
749 			continue;
750 		}
751 		switch (*++cp) {
752 
753 		case 't':
754 			slash = strrchr(ttyn, '/');
755 			if (slash == (char *) 0)
756 				puts(ttyn);
757 			else
758 				puts(&slash[1]);
759 			break;
760 
761 		case 'h':
762 			puts(editedhost);
763 			break;
764 
765 		case 'd': {
766 			t = (time_t)0;
767 			(void)time(&t);
768 			if (Lo)
769 				(void)setlocale(LC_TIME, Lo);
770 			(void)strftime(db, sizeof(db), DF, localtime(&t));
771 			puts(db);
772 			break;
773 
774 		case 's':
775 			puts(kerninfo.sysname);
776 			break;
777 
778 		case 'm':
779 			puts(kerninfo.machine);
780 			break;
781 
782 		case 'r':
783 			puts(kerninfo.release);
784 			break;
785 
786 		case 'v':
787 			puts(kerninfo.version);
788 			break;
789 		}
790 
791 		case '%':
792 			putchr('%');
793 			break;
794 		}
795 		cp++;
796 	}
797 }
798 
799 /*
800  * Read a gettytab database entry and perform necessary quirks.
801  */
802 static void
803 dogettytab(void)
804 {
805 
806 	/* Read the database entry. */
807 	gettable(tname, tabent);
808 
809 	/*
810 	 * Avoid inheriting the parity values from the default entry
811 	 * if any of them is set in the current entry.
812 	 * Mixing different parity settings is unreasonable.
813 	 */
814 	if (OPset || EPset || APset || NPset)
815 		OPset = EPset = APset = NPset = 1;
816 
817 	/* Fill in default values for unset capabilities. */
818 	setdefaults();
819 }
820