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