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