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