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