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