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