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