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