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