xref: /freebsd/libexec/getty/main.c (revision 04c9749ff0148ec8f73b150cec8bc2c094a5d31a)
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 { /* maybe blocking open */
274 			if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 )))
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 	tmode.c_cflag |= (NC ? CLOCAL : 0);
297 	omode = tmode;
298 
299 	for (;;) {
300 
301 		/*
302 		 * if a delay was specified then sleep for that
303 		 * number of seconds before writing the initial prompt
304 		 */
305 		if (first_sleep && DE) {
306 		    sleep(DE);
307 		    /* remove any noise */
308 		    (void)tcflush(STDIN_FILENO, TCIOFLUSH);
309 		}
310 		first_sleep = 0;
311 
312 		setttymode(tname, 0);
313 		if (AB) {
314 			tname = autobaud();
315 			continue;
316 		}
317 		if (PS) {
318 			tname = portselector();
319 			continue;
320 		}
321 		if (CL && *CL)
322 			putpad(CL);
323 		edithost(HE);
324 
325 		/* if this is the first time through this, and an
326 		   issue file has been given, then send it */
327 		if (first_time && IF) {
328 			int fd;
329 
330 			if ((fd = open(IF, O_RDONLY)) != -1) {
331 				char * cp;
332 
333 				while ((cp = getline(fd)) != NULL) {
334 					  putf(cp);
335 				}
336 				close(fd);
337 			}
338 		}
339 		first_time = 0;
340 
341 		if (IM && *IM)
342 			putf(IM);
343 		if (setjmp(timeout)) {
344 			cfsetispeed(&tmode, B0);
345 			cfsetospeed(&tmode, B0);
346 			(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
347 			exit(1);
348 		}
349 		if (TO) {
350 			signal(SIGALRM, dingdong);
351 			alarm(TO);
352 		}
353 		if (AL) {
354 			const char *p = AL;
355 			char *q = name;
356 			int n = sizeof name;
357 
358 			while (*p && q < &name[sizeof name - 1]) {
359 				if (isupper(*p))
360 					upper = 1;
361 				else if (islower(*p))
362 					lower = 1;
363 				else if (isdigit(*p))
364 					digit++;
365 				*q++ = *p++;
366 			}
367 		} else
368 			rval = getname();
369 		if (rval == 2) {
370 			oflush();
371 			alarm(0);
372 			limit.rlim_max = RLIM_INFINITY;
373 			limit.rlim_cur = RLIM_INFINITY;
374 			(void)setrlimit(RLIMIT_CPU, &limit);
375 			execle(PP, "ppplogin", ttyn, (char *) 0, env);
376 			syslog(LOG_ERR, "%s: %m", PP);
377 			exit(1);
378 		} else if (rval || AL) {
379 			register int i;
380 
381 			oflush();
382 			alarm(0);
383 			signal(SIGALRM, SIG_DFL);
384 			if (name[0] == '-') {
385 				puts("user names may not start with '-'.");
386 				continue;
387 			}
388 			if (!(upper || lower || digit))
389 				continue;
390 			set_flags(2);
391 			if (crmod) {
392 				tmode.c_iflag |= ICRNL;
393 				tmode.c_oflag |= ONLCR;
394 			}
395 #if REALLY_OLD_TTYS
396 			if (upper || UC)
397 				tmode.sg_flags |= LCASE;
398 			if (lower || LC)
399 				tmode.sg_flags &= ~LCASE;
400 #endif
401 			if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
402 				syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
403 				exit(1);
404 			}
405 			signal(SIGINT, SIG_DFL);
406 			for (i = 0; environ[i] != (char *)0; i++)
407 				env[i] = environ[i];
408 			makeenv(&env[i]);
409 
410 			limit.rlim_max = RLIM_INFINITY;
411 			limit.rlim_cur = RLIM_INFINITY;
412 			(void)setrlimit(RLIMIT_CPU, &limit);
413 			execle(LO, "login", AL ? "-fp" : "-p", name,
414 			    (char *) 0, env);
415 			syslog(LOG_ERR, "%s: %m", LO);
416 			exit(1);
417 		}
418 		alarm(0);
419 		signal(SIGALRM, SIG_DFL);
420 		signal(SIGINT, SIG_IGN);
421 		if (NX && *NX)
422 			tname = NX;
423 	}
424 }
425 
426 static int
427 opentty(const char *ttyn, int flags)
428 {
429 	int i, j = 0;
430 	int failopenlogged = 0;
431 
432 	while (j < 10 && (i = open(ttyn, flags)) == -1)
433 	{
434 		if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) {
435 			syslog(LOG_ERR, "open %s: %m", ttyn);
436 			failopenlogged = 1;
437 		}
438 		j++;
439 		sleep(60);
440 	}
441 	if (i == -1) {
442 		syslog(LOG_ERR, "open %s: %m", ttyn);
443 		return 0;
444 	}
445 	else {
446 		login_tty(i);
447 		return 1;
448 	}
449 }
450 
451 static void
452 setdefttymode(tname)
453 	const char * tname;
454 {
455 	if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
456 		syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
457 		exit(1);
458 	}
459 	tmode.c_iflag = TTYDEF_IFLAG;
460         tmode.c_oflag = TTYDEF_OFLAG;
461         tmode.c_lflag = TTYDEF_LFLAG;
462         tmode.c_cflag = TTYDEF_CFLAG;
463         omode = tmode;
464 	setttymode(tname, 1);
465 }
466 
467 static void
468 setttymode(tname, raw)
469 	const char * tname;
470 	int raw;
471 {
472 	int off = 0;
473 
474 	gettable(tname, tabent);
475 	if (OPset || EPset || APset)
476 		APset++, OPset++, EPset++;
477 	setdefaults();
478 	(void)tcflush(STDIN_FILENO, TCIOFLUSH);	/* clear out the crap */
479 	ioctl(STDIN_FILENO, FIONBIO, &off);	/* turn off non-blocking mode */
480 	ioctl(STDIN_FILENO, FIOASYNC, &off);	/* ditto for async mode */
481 
482 	if (IS)
483 		cfsetispeed(&tmode, speed(IS));
484 	else if (SP)
485 		cfsetispeed(&tmode, speed(SP));
486 	if (OS)
487 		cfsetospeed(&tmode, speed(OS));
488 	else if (SP)
489 		cfsetospeed(&tmode, speed(SP));
490 	set_flags(0);
491 	setchars();
492 	if (raw)
493 		cfmakeraw(&tmode);
494 	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
495 		syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
496 		exit(1);
497 	}
498 }
499 
500 
501 static int
502 getname()
503 {
504 	register int c;
505 	register char *np;
506 	unsigned char cs;
507 	int ppp_state = 0;
508 	int ppp_connection = 0;
509 
510 	/*
511 	 * Interrupt may happen if we use CBREAK mode
512 	 */
513 	if (setjmp(intrupt)) {
514 		signal(SIGINT, SIG_IGN);
515 		return (0);
516 	}
517 	signal(SIGINT, interrupt);
518 	set_flags(1);
519 	prompt();
520 	oflush();
521 	if (PF > 0) {
522 		sleep(PF);
523 		PF = 0;
524 	}
525 	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
526 		syslog(LOG_ERR, "%s: %m", ttyn);
527 		exit(1);
528 	}
529 	crmod = digit = lower = upper = 0;
530 	np = name;
531 	for (;;) {
532 		oflush();
533 		if (read(STDIN_FILENO, &cs, 1) <= 0)
534 			exit(0);
535 		if ((c = cs&0177) == 0)
536 			return (0);
537 
538 		/* PPP detection state machine..
539 		   Look for sequences:
540 		   PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
541 		   PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
542 		   See RFC1662.
543 		   Derived from code from Michael Hancock, <michaelh@cet.co.jp>
544 		   and Erik 'PPP' Olson, <eriko@wrq.com>
545 		 */
546 
547 		if (PP && (cs == PPP_FRAME)) {
548 			ppp_state = 1;
549 		} else if (ppp_state == 1 && cs == PPP_STATION) {
550 			ppp_state = 2;
551 		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
552 			ppp_state = 3;
553 		} else if ((ppp_state == 2 && cs == PPP_CONTROL)
554 			|| (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
555 			ppp_state = 4;
556 		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
557 			ppp_state = 5;
558 		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
559 			ppp_connection = 1;
560 			break;
561 		} else {
562 			ppp_state = 0;
563 		}
564 
565 		if (c == EOT || c == CTRL('d'))
566 			exit(1);
567 		if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
568 			putf("\r\n");
569 			break;
570 		}
571 		if (islower(c))
572 			lower = 1;
573 		else if (isupper(c))
574 			upper = 1;
575 		else if (c == ERASE || c == '\b' || c == 0177) {
576 			if (np > name) {
577 				np--;
578 				if (cfgetospeed(&tmode) >= 1200)
579 					puts("\b \b");
580 				else
581 					putchr(cs);
582 			}
583 			continue;
584 		} else if (c == KILL || c == CTRL('u')) {
585 			putchr('\r');
586 			if (cfgetospeed(&tmode) < 1200)
587 				putchr('\n');
588 			/* this is the way they do it down under ... */
589 			else if (np > name)
590 				puts("                                     \r");
591 			prompt();
592 			np = name;
593 			continue;
594 		} else if (isdigit(c))
595 			digit++;
596 		if (IG && (c <= ' ' || c > 0176))
597 			continue;
598 		*np++ = c;
599 		putchr(cs);
600 	}
601 	signal(SIGINT, SIG_IGN);
602 	*np = 0;
603 	if (c == '\r')
604 		crmod = 1;
605 	if ((upper && !lower && !LC) || UC)
606 		for (np = name; *np; np++)
607 			if (isupper(*np))
608 				*np = tolower(*np);
609 	return (1 + ppp_connection);
610 }
611 
612 static void
613 putpad(s)
614 	register const char *s;
615 {
616 	register pad = 0;
617 	speed_t ospeed = cfgetospeed(&tmode);
618 
619 	if (isdigit(*s)) {
620 		while (isdigit(*s)) {
621 			pad *= 10;
622 			pad += *s++ - '0';
623 		}
624 		pad *= 10;
625 		if (*s == '.' && isdigit(s[1])) {
626 			pad += s[1] - '0';
627 			s += 2;
628 		}
629 	}
630 
631 	puts(s);
632 	/*
633 	 * If no delay needed, or output speed is
634 	 * not comprehensible, then don't try to delay.
635 	 */
636 	if (pad == 0 || ospeed <= 0)
637 		return;
638 
639 	/*
640 	 * Round up by a half a character frame, and then do the delay.
641 	 * Too bad there are no user program accessible programmed delays.
642 	 * Transmitting pad characters slows many terminals down and also
643 	 * loads the system.
644 	 */
645 	pad = (pad * ospeed + 50000) / 100000;
646 	while (pad--)
647 		putchr(*PC);
648 }
649 
650 static void
651 puts(s)
652 	register const char *s;
653 {
654 	while (*s)
655 		putchr(*s++);
656 }
657 
658 char	outbuf[OBUFSIZ];
659 int	obufcnt = 0;
660 
661 static void
662 putchr(cc)
663 	int cc;
664 {
665 	char c;
666 
667 	c = cc;
668 	if (!NP) {
669 		c |= partab[c&0177] & 0200;
670 		if (OP)
671 			c ^= 0200;
672 	}
673 	if (!UB) {
674 		outbuf[obufcnt++] = c;
675 		if (obufcnt >= OBUFSIZ)
676 			oflush();
677 	} else
678 		write(STDOUT_FILENO, &c, 1);
679 }
680 
681 static void
682 oflush()
683 {
684 	if (obufcnt)
685 		write(STDOUT_FILENO, outbuf, obufcnt);
686 	obufcnt = 0;
687 }
688 
689 static void
690 prompt()
691 {
692 
693 	putf(LM);
694 	if (CO)
695 		putchr('\n');
696 }
697 
698 
699 static char *
700 getline(fd)
701 	int fd;
702 {
703 	int i = 0;
704 	static char linebuf[512];
705 
706 	/*
707 	 * This is certainly slow, but it avoids having to include
708 	 * stdio.h unnecessarily. Issue files should be small anyway.
709 	 */
710 	while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
711 		if (linebuf[i] == '\n') {
712 			/* Don't rely on newline mode, assume raw */
713 			linebuf[i++] = '\r';
714 			linebuf[i++] = '\n';
715 			linebuf[i] = '\0';
716 			return linebuf;
717 		}
718 		++i;
719 	}
720 	linebuf[i] = '\0';
721 	return i ? linebuf : 0;
722 }
723 
724 static void
725 putf(cp)
726 	register const char *cp;
727 {
728 	extern char editedhost[];
729 	time_t t;
730 	char *slash, db[100];
731 
732 	static struct utsname kerninfo;
733 
734 	if (!*kerninfo.sysname)
735 		uname(&kerninfo);
736 
737 	while (*cp) {
738 		if (*cp != '%') {
739 			putchr(*cp++);
740 			continue;
741 		}
742 		switch (*++cp) {
743 
744 		case 't':
745 			slash = strrchr(ttyn, '/');
746 			if (slash == (char *) 0)
747 				puts(ttyn);
748 			else
749 				puts(&slash[1]);
750 			break;
751 
752 		case 'h':
753 			puts(editedhost);
754 			break;
755 
756 		case 'd': {
757 			t = (time_t)0;
758 			(void)time(&t);
759 			if (Lo)
760 				(void)setlocale(LC_TIME, Lo);
761 			(void)strftime(db, sizeof(db), "%+", localtime(&t));
762 			puts(db);
763 			break;
764 
765 		case 's':
766 			puts(kerninfo.sysname);
767 			break;
768 
769 		case 'm':
770 			puts(kerninfo.machine);
771 			break;
772 
773 		case 'r':
774 			puts(kerninfo.release);
775 			break;
776 
777 		case 'v':
778 			puts(kerninfo.version);
779 			break;
780 		}
781 
782 		case '%':
783 			putchr('%');
784 			break;
785 		}
786 		cp++;
787 	}
788 }
789