xref: /freebsd/libexec/getty/subr.c (revision f0adf7f5cdd241db2f2c817683191a6ef64a4e95)
1 /*
2  * Copyright (c) 1983, 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 #if 0
36 static char sccsid[] = "@(#)from: subr.c	8.1 (Berkeley) 6/4/93";
37 #endif
38 static const char rcsid[] =
39   "$FreeBSD$";
40 #endif /* not lint */
41 
42 /*
43  * Melbourne getty.
44  */
45 #ifdef DEBUG
46 #include <stdio.h>
47 #endif
48 #include <stdlib.h>
49 #include <string.h>
50 #include <termios.h>
51 #include <unistd.h>
52 #include <sys/ioctl.h>
53 #include <sys/param.h>
54 #include <sys/time.h>
55 #include <syslog.h>
56 
57 #include "gettytab.h"
58 #include "pathnames.h"
59 #include "extern.h"
60 
61 
62 
63 /*
64  * Get a table entry.
65  */
66 void
67 gettable(const char *name, char *buf)
68 {
69 	struct gettystrs *sp;
70 	struct gettynums *np;
71 	struct gettyflags *fp;
72 	long n;
73 	int l;
74 	char *p;
75 	char *msg = NULL;
76 	const char *dba[2];
77 
78 	static int firsttime = 1;
79 
80 	dba[0] = _PATH_GETTYTAB;
81 	dba[1] = 0;
82 
83 	if (firsttime) {
84 		/*
85 		 * we need to strdup() anything in the strings array
86 		 * initially in order to simplify things later
87 		 */
88 		for (sp = gettystrs; sp->field; sp++)
89 			if (sp->value != NULL) {
90 				/* handle these ones more carefully */
91 				if (sp >= &gettystrs[4] && sp <= &gettystrs[6])
92 					l = 2;
93 				else
94 					l = strlen(sp->value) + 1;
95 				if ((p = malloc(l)) != NULL) {
96 					strncpy(p, sp->value, l);
97 					p[l-1] = '\0';
98 				}
99 				/*
100 				 * replace, even if NULL, else we'll
101 				 * have problems with free()ing static mem
102 				 */
103 				sp->value = p;
104 			}
105 		firsttime = 0;
106 	}
107 
108 	switch (cgetent(&buf, (char **)dba, (char *)name)) {
109 	case 1:
110 		msg = "%s: couldn't resolve 'tc=' in gettytab '%s'";
111 	case 0:
112 		break;
113 	case -1:
114 		msg = "%s: unknown gettytab entry '%s'";
115 		break;
116 	case -2:
117 		msg = "%s: retrieving gettytab entry '%s': %m";
118 		break;
119 	case -3:
120 		msg = "%s: recursive 'tc=' reference gettytab entry '%s'";
121 		break;
122 	default:
123 		msg = "%s: unexpected cgetent() error for entry '%s'";
124 		break;
125 	}
126 
127 	if (msg != NULL) {
128 		syslog(LOG_ERR, msg, "getty", name);
129 		return;
130 	}
131 
132 	for (sp = gettystrs; sp->field; sp++) {
133 		if ((l = cgetstr(buf, (char*)sp->field, &p)) >= 0) {
134 			if (sp->value) {
135 				/* prefer existing value */
136 				if (strcmp(p, sp->value) != 0)
137 					free(sp->value);
138 				else {
139 					free(p);
140 					p = sp->value;
141 				}
142 			}
143 			sp->value = p;
144 		} else if (l == -1) {
145 			free(sp->value);
146 			sp->value = NULL;
147 		}
148 	}
149 
150 	for (np = gettynums; np->field; np++) {
151 		if (cgetnum(buf, (char*)np->field, &n) == -1)
152 			np->set = 0;
153 		else {
154 			np->set = 1;
155 			np->value = n;
156 		}
157 	}
158 
159 	for (fp = gettyflags; fp->field; fp++) {
160 		if (cgetcap(buf, (char *)fp->field, ':') == NULL)
161 			fp->set = 0;
162 		else {
163 			fp->set = 1;
164 			fp->value = 1 ^ fp->invrt;
165 		}
166 	}
167 
168 #ifdef DEBUG
169 	printf("name=\"%s\", buf=\"%s\"\r\n", name, buf);
170 	for (sp = gettystrs; sp->field; sp++)
171 		printf("cgetstr: %s=%s\r\n", sp->field, sp->value);
172 	for (np = gettynums; np->field; np++)
173 		printf("cgetnum: %s=%d\r\n", np->field, np->value);
174 	for (fp = gettyflags; fp->field; fp++)
175 		printf("cgetflags: %s='%c' set='%c'\r\n", fp->field,
176 		       fp->value + '0', fp->set + '0');
177 #endif /* DEBUG */
178 }
179 
180 void
181 gendefaults(void)
182 {
183 	struct gettystrs *sp;
184 	struct gettynums *np;
185 	struct gettyflags *fp;
186 
187 	for (sp = gettystrs; sp->field; sp++)
188 		if (sp->value)
189 			sp->defalt = strdup(sp->value);
190 	for (np = gettynums; np->field; np++)
191 		if (np->set)
192 			np->defalt = np->value;
193 	for (fp = gettyflags; fp->field; fp++)
194 		if (fp->set)
195 			fp->defalt = fp->value;
196 		else
197 			fp->defalt = fp->invrt;
198 }
199 
200 void
201 setdefaults(void)
202 {
203 	struct gettystrs *sp;
204 	struct gettynums *np;
205 	struct gettyflags *fp;
206 
207 	for (sp = gettystrs; sp->field; sp++)
208 		if (!sp->value)
209 			sp->value = !sp->defalt ? sp->defalt
210 						: strdup(sp->defalt);
211 	for (np = gettynums; np->field; np++)
212 		if (!np->set)
213 			np->value = np->defalt;
214 	for (fp = gettyflags; fp->field; fp++)
215 		if (!fp->set)
216 			fp->value = fp->defalt;
217 }
218 
219 static char **
220 charnames[] = {
221 	&ER, &KL, &IN, &QU, &XN, &XF, &ET, &BK,
222 	&SU, &DS, &RP, &FL, &WE, &LN, 0
223 };
224 
225 static char *
226 charvars[] = {
227 	&tmode.c_cc[VERASE], &tmode.c_cc[VKILL], &tmode.c_cc[VINTR],
228 	&tmode.c_cc[VQUIT], &tmode.c_cc[VSTART], &tmode.c_cc[VSTOP],
229 	&tmode.c_cc[VEOF], &tmode.c_cc[VEOL], &tmode.c_cc[VSUSP],
230 	&tmode.c_cc[VDSUSP], &tmode.c_cc[VREPRINT], &tmode.c_cc[VDISCARD],
231 	&tmode.c_cc[VWERASE], &tmode.c_cc[VLNEXT], 0
232 };
233 
234 void
235 setchars(void)
236 {
237 	int i;
238 	const char *p;
239 
240 	for (i = 0; charnames[i]; i++) {
241 		p = *charnames[i];
242 		if (p && *p)
243 			*charvars[i] = *p;
244 		else
245 			*charvars[i] = _POSIX_VDISABLE;
246 	}
247 }
248 
249 /* Macros to clear/set/test flags. */
250 #define	SET(t, f)	(t) |= (f)
251 #define	CLR(t, f)	(t) &= ~(f)
252 #define	ISSET(t, f)	((t) & (f))
253 
254 void
255 set_flags(int n)
256 {
257 	tcflag_t iflag, oflag, cflag, lflag;
258 
259 
260 	switch (n) {
261 	case 0:
262 		if (C0set && I0set && L0set && O0set) {
263 			tmode.c_cflag = C0;
264 			tmode.c_iflag = I0;
265 			tmode.c_lflag = L0;
266 			tmode.c_oflag = O0;
267 			return;
268 		}
269 		break;
270 	case 1:
271 		if (C1set && I1set && L1set && O1set) {
272 			tmode.c_cflag = C1;
273 			tmode.c_iflag = I1;
274 			tmode.c_lflag = L1;
275 			tmode.c_oflag = O1;
276 			return;
277 		}
278 		break;
279 	default:
280 		if (C2set && I2set && L2set && O2set) {
281 			tmode.c_cflag = C2;
282 			tmode.c_iflag = I2;
283 			tmode.c_lflag = L2;
284 			tmode.c_oflag = O2;
285 			return;
286 		}
287 		break;
288 	}
289 
290 	iflag = omode.c_iflag;
291 	oflag = omode.c_oflag;
292 	cflag = omode.c_cflag;
293 	lflag = omode.c_lflag;
294 
295 	if (NP) {
296 		CLR(cflag, CSIZE|PARENB);
297 		SET(cflag, CS8);
298 		CLR(iflag, ISTRIP|INPCK|IGNPAR);
299 	} else if (AP || EP || OP) {
300 		CLR(cflag, CSIZE);
301 		SET(cflag, CS7|PARENB);
302 		SET(iflag, ISTRIP);
303 		if (OP && !EP) {
304 			SET(iflag, INPCK|IGNPAR);
305 			SET(cflag, PARODD);
306 			if (AP)
307 				CLR(iflag, INPCK);
308 		} else if (EP && !OP) {
309 			SET(iflag, INPCK|IGNPAR);
310 			CLR(cflag, PARODD);
311 			if (AP)
312 				CLR(iflag, INPCK);
313 		} else if (AP || (EP && OP)) {
314 			CLR(iflag, INPCK|IGNPAR);
315 			CLR(cflag, PARODD);
316 		}
317 	} /* else, leave as is */
318 
319 #if 0
320 	if (UC)
321 		f |= LCASE;
322 #endif
323 
324 	if (HC)
325 		SET(cflag, HUPCL);
326 	else
327 		CLR(cflag, HUPCL);
328 
329 	if (MB)
330 		SET(cflag, MDMBUF);
331 	else
332 		CLR(cflag, MDMBUF);
333 
334 	if (HW)
335 		SET(cflag, CRTSCTS);
336 	else
337 		CLR(cflag, CRTSCTS);
338 
339 	if (NL) {
340 		SET(iflag, ICRNL);
341 		SET(oflag, ONLCR|OPOST);
342 	} else {
343 		CLR(iflag, ICRNL);
344 		CLR(oflag, ONLCR);
345 	}
346 
347 	if (!HT)
348 		SET(oflag, OXTABS|OPOST);
349 	else
350 		CLR(oflag, OXTABS);
351 
352 #ifdef XXX_DELAY
353 	SET(f, delaybits());
354 #endif
355 
356 	if (n == 1) {		/* read mode flags */
357 		if (RW) {
358 			iflag = 0;
359 			CLR(oflag, OPOST);
360 			CLR(cflag, CSIZE|PARENB);
361 			SET(cflag, CS8);
362 			lflag = 0;
363 		} else {
364 			CLR(lflag, ICANON);
365 		}
366 		goto out;
367 	}
368 
369 	if (n == 0)
370 		goto out;
371 
372 #if 0
373 	if (CB)
374 		SET(f, CRTBS);
375 #endif
376 
377 	if (CE)
378 		SET(lflag, ECHOE);
379 	else
380 		CLR(lflag, ECHOE);
381 
382 	if (CK)
383 		SET(lflag, ECHOKE);
384 	else
385 		CLR(lflag, ECHOKE);
386 
387 	if (PE)
388 		SET(lflag, ECHOPRT);
389 	else
390 		CLR(lflag, ECHOPRT);
391 
392 	if (EC)
393 		SET(lflag, ECHO);
394 	else
395 		CLR(lflag, ECHO);
396 
397 	if (XC)
398 		SET(lflag, ECHOCTL);
399 	else
400 		CLR(lflag, ECHOCTL);
401 
402 	if (DX)
403 		SET(lflag, IXANY);
404 	else
405 		CLR(lflag, IXANY);
406 
407 out:
408 	tmode.c_iflag = iflag;
409 	tmode.c_oflag = oflag;
410 	tmode.c_cflag = cflag;
411 	tmode.c_lflag = lflag;
412 }
413 
414 
415 #ifdef XXX_DELAY
416 struct delayval {
417 	unsigned	delay;		/* delay in ms */
418 	int		bits;
419 };
420 
421 /*
422  * below are random guesses, I can't be bothered checking
423  */
424 
425 struct delayval	crdelay[] = {
426 	{ 1,		CR1 },
427 	{ 2,		CR2 },
428 	{ 3,		CR3 },
429 	{ 83,		CR1 },
430 	{ 166,		CR2 },
431 	{ 0,		CR3 },
432 };
433 
434 struct delayval nldelay[] = {
435 	{ 1,		NL1 },		/* special, calculated */
436 	{ 2,		NL2 },
437 	{ 3,		NL3 },
438 	{ 100,		NL2 },
439 	{ 0,		NL3 },
440 };
441 
442 struct delayval	bsdelay[] = {
443 	{ 1,		BS1 },
444 	{ 0,		0 },
445 };
446 
447 struct delayval	ffdelay[] = {
448 	{ 1,		FF1 },
449 	{ 1750,		FF1 },
450 	{ 0,		FF1 },
451 };
452 
453 struct delayval	tbdelay[] = {
454 	{ 1,		TAB1 },
455 	{ 2,		TAB2 },
456 	{ 3,		XTABS },	/* this is expand tabs */
457 	{ 100,		TAB1 },
458 	{ 0,		TAB2 },
459 };
460 
461 int
462 delaybits(void)
463 {
464 	int f;
465 
466 	f  = adelay(CD, crdelay);
467 	f |= adelay(ND, nldelay);
468 	f |= adelay(FD, ffdelay);
469 	f |= adelay(TD, tbdelay);
470 	f |= adelay(BD, bsdelay);
471 	return (f);
472 }
473 
474 int
475 adelay(int ms, struct delayval *dp)
476 {
477 	if (ms == 0)
478 		return (0);
479 	while (dp->delay && ms > dp->delay)
480 		dp++;
481 	return (dp->bits);
482 }
483 #endif
484 
485 char	editedhost[MAXHOSTNAMELEN];
486 
487 void
488 edithost(const char *pat)
489 {
490 	const char *host = HN;
491 	char *res = editedhost;
492 
493 	if (!pat)
494 		pat = "";
495 	while (*pat) {
496 		switch (*pat) {
497 
498 		case '#':
499 			if (*host)
500 				host++;
501 			break;
502 
503 		case '@':
504 			if (*host)
505 				*res++ = *host++;
506 			break;
507 
508 		default:
509 			*res++ = *pat;
510 			break;
511 
512 		}
513 		if (res == &editedhost[sizeof editedhost - 1]) {
514 			*res = '\0';
515 			return;
516 		}
517 		pat++;
518 	}
519 	if (*host)
520 		strncpy(res, host, sizeof editedhost - (res - editedhost) - 1);
521 	else
522 		*res = '\0';
523 	editedhost[sizeof editedhost - 1] = '\0';
524 }
525 
526 static struct speedtab {
527 	int	speed;
528 	int	uxname;
529 } speedtab[] = {
530 	{ 50,	B50 },
531 	{ 75,	B75 },
532 	{ 110,	B110 },
533 	{ 134,	B134 },
534 	{ 150,	B150 },
535 	{ 200,	B200 },
536 	{ 300,	B300 },
537 	{ 600,	B600 },
538 	{ 1200,	B1200 },
539 	{ 1800,	B1800 },
540 	{ 2400,	B2400 },
541 	{ 4800,	B4800 },
542 	{ 9600,	B9600 },
543 	{ 19200, EXTA },
544 	{ 19,	EXTA },		/* for people who say 19.2K */
545 	{ 38400, EXTB },
546 	{ 38,	EXTB },
547 	{ 7200,	EXTB },		/* alternative */
548 	{ 57600, B57600 },
549 	{ 115200, B115200 },
550 	{ 230400, B230400 },
551 	{ 0 }
552 };
553 
554 int
555 speed(int val)
556 {
557 	struct speedtab *sp;
558 
559 	if (val <= B230400)
560 		return (val);
561 
562 	for (sp = speedtab; sp->speed; sp++)
563 		if (sp->speed == val)
564 			return (sp->uxname);
565 
566 	return (B300);		/* default in impossible cases */
567 }
568 
569 void
570 makeenv(char *env[])
571 {
572 	static char termbuf[128] = "TERM=";
573 	char *p, *q;
574 	char **ep;
575 
576 	ep = env;
577 	if (TT && *TT) {
578 		strlcat(termbuf, TT, sizeof(termbuf));
579 		*ep++ = termbuf;
580 	}
581 	if ((p = EV)) {
582 		q = p;
583 		while ((q = strchr(q, ','))) {
584 			*q++ = '\0';
585 			*ep++ = p;
586 			p = q;
587 		}
588 		if (*p)
589 			*ep++ = p;
590 	}
591 	*ep = (char *)0;
592 }
593 
594 /*
595  * This speed select mechanism is written for the Develcon DATASWITCH.
596  * The Develcon sends a string of the form "B{speed}\n" at a predefined
597  * baud rate. This string indicates the user's actual speed.
598  * The routine below returns the terminal type mapped from derived speed.
599  */
600 struct	portselect {
601 	const char	*ps_baud;
602 	const char	*ps_type;
603 } portspeeds[] = {
604 	{ "B110",	"std.110" },
605 	{ "B134",	"std.134" },
606 	{ "B150",	"std.150" },
607 	{ "B300",	"std.300" },
608 	{ "B600",	"std.600" },
609 	{ "B1200",	"std.1200" },
610 	{ "B2400",	"std.2400" },
611 	{ "B4800",	"std.4800" },
612 	{ "B9600",	"std.9600" },
613 	{ "B19200",	"std.19200" },
614 	{ 0 }
615 };
616 
617 const char *
618 portselector(void)
619 {
620 	char c, baud[20];
621 	const char *type = "default";
622 	struct portselect *ps;
623 	int len;
624 
625 	alarm(5*60);
626 	for (len = 0; len < sizeof (baud) - 1; len++) {
627 		if (read(STDIN_FILENO, &c, 1) <= 0)
628 			break;
629 		c &= 0177;
630 		if (c == '\n' || c == '\r')
631 			break;
632 		if (c == 'B')
633 			len = 0;	/* in case of leading garbage */
634 		baud[len] = c;
635 	}
636 	baud[len] = '\0';
637 	for (ps = portspeeds; ps->ps_baud; ps++)
638 		if (strcmp(ps->ps_baud, baud) == 0) {
639 			type = ps->ps_type;
640 			break;
641 		}
642 	sleep(2);	/* wait for connection to complete */
643 	return (type);
644 }
645 
646 /*
647  * This auto-baud speed select mechanism is written for the Micom 600
648  * portselector. Selection is done by looking at how the character '\r'
649  * is garbled at the different speeds.
650  */
651 const char *
652 autobaud(void)
653 {
654 	int rfds;
655 	struct timeval timeout;
656 	char c;
657 	const char *type = "9600-baud";
658 
659 	(void)tcflush(0, TCIOFLUSH);
660 	rfds = 1 << 0;
661 	timeout.tv_sec = 5;
662 	timeout.tv_usec = 0;
663 	if (select(32, (fd_set *)&rfds, (fd_set *)NULL,
664 	    (fd_set *)NULL, &timeout) <= 0)
665 		return (type);
666 	if (read(STDIN_FILENO, &c, sizeof(char)) != sizeof(char))
667 		return (type);
668 	timeout.tv_sec = 0;
669 	timeout.tv_usec = 20;
670 	(void) select(32, (fd_set *)NULL, (fd_set *)NULL,
671 	    (fd_set *)NULL, &timeout);
672 	(void)tcflush(0, TCIOFLUSH);
673 	switch (c & 0377) {
674 
675 	case 0200:		/* 300-baud */
676 		type = "300-baud";
677 		break;
678 
679 	case 0346:		/* 1200-baud */
680 		type = "1200-baud";
681 		break;
682 
683 	case  015:		/* 2400-baud */
684 	case 0215:
685 		type = "2400-baud";
686 		break;
687 
688 	default:		/* 4800-baud */
689 		type = "4800-baud";
690 		break;
691 
692 	case 0377:		/* 9600-baud */
693 		type = "9600-baud";
694 		break;
695 	}
696 	return (type);
697 }
698