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
34static char sccsid[] = "@(#)from: subr.c	8.1 (Berkeley) 6/4/93";
35#endif
36static 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 */
62void
63gettable(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
160void
161gendefaults(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
180void
181setdefaults(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
199static char **
200charnames[] = {
201	&ER, &KL, &IN, &QU, &XN, &XF, &ET, &BK,
202	&SU, &DS, &RP, &FL, &WE, &LN, 0
203};
204
205static char *
206charvars[] = {
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
214void
215setchars(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
234void
235set_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
387out:
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
396struct 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
405struct delayval	crdelay[] = {
406	{ 1,		CR1 },
407	{ 2,		CR2 },
408	{ 3,		CR3 },
409	{ 83,		CR1 },
410	{ 166,		CR2 },
411	{ 0,		CR3 },
412};
413
414struct delayval nldelay[] = {
415	{ 1,		NL1 },		/* special, calculated */
416	{ 2,		NL2 },
417	{ 3,		NL3 },
418	{ 100,		NL2 },
419	{ 0,		NL3 },
420};
421
422struct delayval	bsdelay[] = {
423	{ 1,		BS1 },
424	{ 0,		0 },
425};
426
427struct delayval	ffdelay[] = {
428	{ 1,		FF1 },
429	{ 1750,		FF1 },
430	{ 0,		FF1 },
431};
432
433struct delayval	tbdelay[] = {
434	{ 1,		TAB1 },
435	{ 2,		TAB2 },
436	{ 3,		XTABS },	/* this is expand tabs */
437	{ 100,		TAB1 },
438	{ 0,		TAB2 },
439};
440
441int
442delaybits(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
454int
455adelay(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
465char	editedhost[MAXHOSTNAMELEN];
466
467void
468edithost(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
512static 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
540int
541speed(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
555void
556makeenv(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 */
586static 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
603const char *
604portselector(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 */
637const char *
638autobaud(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