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