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 */
54void
55gettable(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
154void
155gendefaults(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
174void
175setdefaults(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
193static char **
194charnames[] = {
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
201static char *
202charvars[] = {
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
210void
211setchars(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
230void
231set_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
383out:
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
392struct 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
401struct delayval	crdelay[] = {
402	{ 1,		CR1 },
403	{ 2,		CR2 },
404	{ 3,		CR3 },
405	{ 83,		CR1 },
406	{ 166,		CR2 },
407	{ 0,		CR3 },
408};
409
410struct delayval nldelay[] = {
411	{ 1,		NL1 },		/* special, calculated */
412	{ 2,		NL2 },
413	{ 3,		NL3 },
414	{ 100,		NL2 },
415	{ 0,		NL3 },
416};
417
418struct delayval	bsdelay[] = {
419	{ 1,		BS1 },
420	{ 0,		0 },
421};
422
423struct delayval	ffdelay[] = {
424	{ 1,		FF1 },
425	{ 1750,		FF1 },
426	{ 0,		FF1 },
427};
428
429struct delayval	tbdelay[] = {
430	{ 1,		TAB1 },
431	{ 2,		TAB2 },
432	{ 3,		XTABS },	/* this is expand tabs */
433	{ 100,		TAB1 },
434	{ 0,		TAB2 },
435};
436
437int
438delaybits(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
450int
451adelay(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
461char	editedhost[MAXHOSTNAMELEN];
462
463void
464edithost(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	 */
504copyasis:
505	strlcpy(editedhost, HN, sizeof(editedhost));
506}
507
508static 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
536int
537speed(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
551void
552makeenv(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 */
582static 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
599const char *
600portselector(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 */
633const char *
634autobaud(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