xref: /freebsd-11-stable/sys/kern/kern_cons.c (revision 375eb4f3cc36de31f03482b8c19f5a9198fbc534)
1 /*-
2  * Copyright (c) 1988 University of Utah.
3  * Copyright (c) 1991 The Regents of the University of California.
4  * Copyright (c) 1999 Michael Smith
5  * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
6  *
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * the Systems Programming Group of the University of Utah Computer
11  * Science Department.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	from: @(#)cons.c	7.2 (Berkeley) 5/9/91
38  */
39 
40 #include <sys/cdefs.h>
41 __FBSDID("$FreeBSD$");
42 
43 #include "opt_ddb.h"
44 #include "opt_syscons.h"
45 
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/lock.h>
49 #include <sys/mutex.h>
50 #include <sys/conf.h>
51 #include <sys/cons.h>
52 #include <sys/fcntl.h>
53 #include <sys/kdb.h>
54 #include <sys/kernel.h>
55 #include <sys/malloc.h>
56 #include <sys/msgbuf.h>
57 #include <sys/namei.h>
58 #include <sys/priv.h>
59 #include <sys/proc.h>
60 #include <sys/queue.h>
61 #include <sys/reboot.h>
62 #include <sys/sysctl.h>
63 #include <sys/sbuf.h>
64 #include <sys/tty.h>
65 #include <sys/uio.h>
66 #include <sys/vnode.h>
67 
68 #include <ddb/ddb.h>
69 
70 #include <machine/cpu.h>
71 #include <machine/clock.h>
72 
73 static MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling");
74 
75 struct cn_device {
76 	STAILQ_ENTRY(cn_device) cnd_next;
77 	struct		consdev *cnd_cn;
78 };
79 
80 #define CNDEVPATHMAX	32
81 #define CNDEVTAB_SIZE	4
82 static struct cn_device cn_devtab[CNDEVTAB_SIZE];
83 static STAILQ_HEAD(, cn_device) cn_devlist =
84     STAILQ_HEAD_INITIALIZER(cn_devlist);
85 
86 int	cons_avail_mask = 0;	/* Bit mask. Each registered low level console
87 				 * which is currently unavailable for inpit
88 				 * (i.e., if it is in graphics mode) will have
89 				 * this bit cleared.
90 				 */
91 static int cn_mute;
92 static char *consbuf;			/* buffer used by `consmsgbuf' */
93 static struct callout conscallout;	/* callout for outputting to constty */
94 struct msgbuf consmsgbuf;		/* message buffer for console tty */
95 static u_char console_pausing;		/* pause after each line during probe */
96 static char *console_pausestr=
97 "<pause; press any key to proceed to next line or '.' to end pause mode>";
98 struct tty *constty;			/* pointer to console "window" tty */
99 static struct mtx cnputs_mtx;		/* Mutex for cnputs(). */
100 static int use_cnputs_mtx = 0;		/* != 0 if cnputs_mtx locking reqd. */
101 
102 static void constty_timeout(void *arg);
103 
104 static struct consdev cons_consdev;
105 DATA_SET(cons_set, cons_consdev);
106 SET_DECLARE(cons_set, struct consdev);
107 
108 void
cninit(void)109 cninit(void)
110 {
111 	struct consdev *best_cn, *cn, **list;
112 
113 	/*
114 	 * Check if we should mute the console (for security reasons perhaps)
115 	 * It can be changes dynamically using sysctl kern.consmute
116 	 * once we are up and going.
117 	 *
118 	 */
119         cn_mute = ((boothowto & (RB_MUTE
120 			|RB_SINGLE
121 			|RB_VERBOSE
122 			|RB_ASKNAME)) == RB_MUTE);
123 
124 	/*
125 	 * Find the first console with the highest priority.
126 	 */
127 	best_cn = NULL;
128 	SET_FOREACH(list, cons_set) {
129 		cn = *list;
130 		cnremove(cn);
131 		/* Skip cons_consdev. */
132 		if (cn->cn_ops == NULL)
133 			continue;
134 		cn->cn_ops->cn_probe(cn);
135 		if (cn->cn_pri == CN_DEAD)
136 			continue;
137 		if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
138 			best_cn = cn;
139 		if (boothowto & RB_MULTIPLE) {
140 			/*
141 			 * Initialize console, and attach to it.
142 			 */
143 			cn->cn_ops->cn_init(cn);
144 			cnadd(cn);
145 		}
146 	}
147 	if (best_cn == NULL)
148 		return;
149 	if ((boothowto & RB_MULTIPLE) == 0) {
150 		best_cn->cn_ops->cn_init(best_cn);
151 		cnadd(best_cn);
152 	}
153 	if (boothowto & RB_PAUSE)
154 		console_pausing = 1;
155 	/*
156 	 * Make the best console the preferred console.
157 	 */
158 	cnselect(best_cn);
159 
160 #ifdef EARLY_PRINTF
161 	/*
162 	 * Release early console.
163 	 */
164 	early_putc = NULL;
165 #endif
166 }
167 
168 void
cninit_finish()169 cninit_finish()
170 {
171 	console_pausing = 0;
172 }
173 
174 /* add a new physical console to back the virtual console */
175 int
cnadd(struct consdev * cn)176 cnadd(struct consdev *cn)
177 {
178 	struct cn_device *cnd;
179 	int i;
180 
181 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
182 		if (cnd->cnd_cn == cn)
183 			return (0);
184 	for (i = 0; i < CNDEVTAB_SIZE; i++) {
185 		cnd = &cn_devtab[i];
186 		if (cnd->cnd_cn == NULL)
187 			break;
188 	}
189 	if (cnd->cnd_cn != NULL)
190 		return (ENOMEM);
191 	cnd->cnd_cn = cn;
192 	if (cn->cn_name[0] == '\0') {
193 		/* XXX: it is unclear if/where this print might output */
194 		printf("WARNING: console at %p has no name\n", cn);
195 	}
196 	STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
197 	if (STAILQ_FIRST(&cn_devlist) == cnd)
198 		ttyconsdev_select(cnd->cnd_cn->cn_name);
199 
200 	/* Add device to the active mask. */
201 	cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0);
202 
203 	return (0);
204 }
205 
206 void
cnremove(struct consdev * cn)207 cnremove(struct consdev *cn)
208 {
209 	struct cn_device *cnd;
210 	int i;
211 
212 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
213 		if (cnd->cnd_cn != cn)
214 			continue;
215 		if (STAILQ_FIRST(&cn_devlist) == cnd)
216 			ttyconsdev_select(NULL);
217 		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
218 		cnd->cnd_cn = NULL;
219 
220 		/* Remove this device from available mask. */
221 		for (i = 0; i < CNDEVTAB_SIZE; i++)
222 			if (cnd == &cn_devtab[i]) {
223 				cons_avail_mask &= ~(1 << i);
224 				break;
225 			}
226 #if 0
227 		/*
228 		 * XXX
229 		 * syscons gets really confused if console resources are
230 		 * freed after the system has initialized.
231 		 */
232 		if (cn->cn_term != NULL)
233 			cn->cn_ops->cn_term(cn);
234 #endif
235 		return;
236 	}
237 }
238 
239 void
cnselect(struct consdev * cn)240 cnselect(struct consdev *cn)
241 {
242 	struct cn_device *cnd;
243 
244 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
245 		if (cnd->cnd_cn != cn)
246 			continue;
247 		if (cnd == STAILQ_FIRST(&cn_devlist))
248 			return;
249 		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
250 		STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
251 		ttyconsdev_select(cnd->cnd_cn->cn_name);
252 		return;
253 	}
254 }
255 
256 void
cnavailable(struct consdev * cn,int available)257 cnavailable(struct consdev *cn, int available)
258 {
259 	int i;
260 
261 	for (i = 0; i < CNDEVTAB_SIZE; i++) {
262 		if (cn_devtab[i].cnd_cn == cn)
263 			break;
264 	}
265 	if (available) {
266 		if (i < CNDEVTAB_SIZE)
267 			cons_avail_mask |= (1 << i);
268 		cn->cn_flags &= ~CN_FLAG_NOAVAIL;
269 	} else {
270 		if (i < CNDEVTAB_SIZE)
271 			cons_avail_mask &= ~(1 << i);
272 		cn->cn_flags |= CN_FLAG_NOAVAIL;
273 	}
274 }
275 
276 int
cnunavailable(void)277 cnunavailable(void)
278 {
279 
280 	return (cons_avail_mask == 0);
281 }
282 
283 /*
284  * sysctl_kern_console() provides output parseable in conscontrol(1).
285  */
286 static int
sysctl_kern_console(SYSCTL_HANDLER_ARGS)287 sysctl_kern_console(SYSCTL_HANDLER_ARGS)
288 {
289 	struct cn_device *cnd;
290 	struct consdev *cp, **list;
291 	char *p;
292 	int delete, error;
293 	struct sbuf *sb;
294 
295 	sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND |
296 	    SBUF_INCLUDENUL);
297 	if (sb == NULL)
298 		return (ENOMEM);
299 	sbuf_clear(sb);
300 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
301 		sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name);
302 	sbuf_printf(sb, "/");
303 	SET_FOREACH(list, cons_set) {
304 		cp = *list;
305 		if (cp->cn_name[0] != '\0')
306 			sbuf_printf(sb, "%s,", cp->cn_name);
307 	}
308 	sbuf_finish(sb);
309 	error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req);
310 	if (error == 0 && req->newptr != NULL) {
311 		p = sbuf_data(sb);
312 		error = ENXIO;
313 		delete = 0;
314 		if (*p == '-') {
315 			delete = 1;
316 			p++;
317 		}
318 		SET_FOREACH(list, cons_set) {
319 			cp = *list;
320 			if (strcmp(p, cp->cn_name) != 0)
321 				continue;
322 			if (delete) {
323 				cnremove(cp);
324 				error = 0;
325 			} else {
326 				error = cnadd(cp);
327 				if (error == 0)
328 					cnselect(cp);
329 			}
330 			break;
331 		}
332 	}
333 	sbuf_delete(sb);
334 	return (error);
335 }
336 
337 SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
338 	0, 0, sysctl_kern_console, "A", "Console device control");
339 
340 /*
341  * User has changed the state of the console muting.
342  * This may require us to open or close the device in question.
343  */
344 static int
sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)345 sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
346 {
347 	int error;
348 
349 	error = sysctl_handle_int(oidp, &cn_mute, 0, req);
350 	if (error != 0 || req->newptr == NULL)
351 		return (error);
352 	return (error);
353 }
354 
355 SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
356 	0, sizeof(cn_mute), sysctl_kern_consmute, "I",
357 	"State of the console muting");
358 
359 void
cngrab()360 cngrab()
361 {
362 	struct cn_device *cnd;
363 	struct consdev *cn;
364 
365 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
366 		cn = cnd->cnd_cn;
367 		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
368 			cn->cn_ops->cn_grab(cn);
369 	}
370 }
371 
372 void
cnungrab()373 cnungrab()
374 {
375 	struct cn_device *cnd;
376 	struct consdev *cn;
377 
378 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
379 		cn = cnd->cnd_cn;
380 		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
381 			cn->cn_ops->cn_ungrab(cn);
382 	}
383 }
384 
385 void
cnresume()386 cnresume()
387 {
388 	struct cn_device *cnd;
389 	struct consdev *cn;
390 
391 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
392 		cn = cnd->cnd_cn;
393 		if (cn->cn_ops->cn_resume != NULL)
394 			cn->cn_ops->cn_resume(cn);
395 	}
396 }
397 
398 /*
399  * Low level console routines.
400  */
401 int
cngetc(void)402 cngetc(void)
403 {
404 	int c;
405 
406 	if (cn_mute)
407 		return (-1);
408 	while ((c = cncheckc()) == -1)
409 		cpu_spinwait();
410 	if (c == '\r')
411 		c = '\n';		/* console input is always ICRNL */
412 	return (c);
413 }
414 
415 int
cncheckc(void)416 cncheckc(void)
417 {
418 	struct cn_device *cnd;
419 	struct consdev *cn;
420 	int c;
421 
422 	if (cn_mute)
423 		return (-1);
424 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
425 		cn = cnd->cnd_cn;
426 		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
427 			c = cn->cn_ops->cn_getc(cn);
428 			if (c != -1)
429 				return (c);
430 		}
431 	}
432 	return (-1);
433 }
434 
435 void
cngets(char * cp,size_t size,int visible)436 cngets(char *cp, size_t size, int visible)
437 {
438 	char *lp, *end;
439 	int c;
440 
441 	cngrab();
442 
443 	lp = cp;
444 	end = cp + size - 1;
445 	for (;;) {
446 		c = cngetc() & 0177;
447 		switch (c) {
448 		case '\n':
449 		case '\r':
450 			cnputc(c);
451 			*lp = '\0';
452 			cnungrab();
453 			return;
454 		case '\b':
455 		case '\177':
456 			if (lp > cp) {
457 				if (visible)
458 					cnputs("\b \b");
459 				lp--;
460 			}
461 			continue;
462 		case '\0':
463 			continue;
464 		default:
465 			if (lp < end) {
466 				switch (visible) {
467 				case GETS_NOECHO:
468 					break;
469 				case GETS_ECHOPASS:
470 					cnputc('*');
471 					break;
472 				default:
473 					cnputc(c);
474 					break;
475 				}
476 				*lp++ = c;
477 			}
478 		}
479 	}
480 }
481 
482 void
cnputc(int c)483 cnputc(int c)
484 {
485 	struct cn_device *cnd;
486 	struct consdev *cn;
487 	char *cp;
488 
489 #ifdef EARLY_PRINTF
490 	if (early_putc != NULL) {
491 		if (c == '\n')
492 			early_putc('\r');
493 		early_putc(c);
494 		return;
495 	}
496 #endif
497 
498 	if (cn_mute || c == '\0')
499 		return;
500 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
501 		cn = cnd->cnd_cn;
502 		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
503 			if (c == '\n')
504 				cn->cn_ops->cn_putc(cn, '\r');
505 			cn->cn_ops->cn_putc(cn, c);
506 		}
507 	}
508 	if (console_pausing && c == '\n' && !kdb_active) {
509 		for (cp = console_pausestr; *cp != '\0'; cp++)
510 			cnputc(*cp);
511 		cngrab();
512 		if (cngetc() == '.')
513 			console_pausing = 0;
514 		cnungrab();
515 		cnputc('\r');
516 		for (cp = console_pausestr; *cp != '\0'; cp++)
517 			cnputc(' ');
518 		cnputc('\r');
519 	}
520 }
521 
522 void
cnputs(char * p)523 cnputs(char *p)
524 {
525 	int c;
526 	int unlock_reqd = 0;
527 
528 	if (use_cnputs_mtx) {
529 	  	/*
530 		 * NOTE: Debug prints and/or witness printouts in
531 		 * console driver clients can cause the "cnputs_mtx"
532 		 * mutex to recurse. Simply return if that happens.
533 		 */
534 		if (mtx_owned(&cnputs_mtx))
535 			return;
536 		mtx_lock_spin(&cnputs_mtx);
537 		unlock_reqd = 1;
538 	}
539 
540 	while ((c = *p++) != '\0')
541 		cnputc(c);
542 
543 	if (unlock_reqd)
544 		mtx_unlock_spin(&cnputs_mtx);
545 }
546 
547 static int consmsgbuf_size = 8192;
548 SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
549     "Console tty buffer size");
550 
551 /*
552  * Redirect console output to a tty.
553  */
554 void
constty_set(struct tty * tp)555 constty_set(struct tty *tp)
556 {
557 	int size;
558 
559 	KASSERT(tp != NULL, ("constty_set: NULL tp"));
560 	if (consbuf == NULL) {
561 		size = consmsgbuf_size;
562 		consbuf = malloc(size, M_TTYCONS, M_WAITOK);
563 		msgbuf_init(&consmsgbuf, consbuf, size);
564 		callout_init(&conscallout, 0);
565 	}
566 	constty = tp;
567 	constty_timeout(NULL);
568 }
569 
570 /*
571  * Disable console redirection to a tty.
572  */
573 void
constty_clear(void)574 constty_clear(void)
575 {
576 	int c;
577 
578 	constty = NULL;
579 	if (consbuf == NULL)
580 		return;
581 	callout_stop(&conscallout);
582 	while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
583 		cnputc(c);
584 	free(consbuf, M_TTYCONS);
585 	consbuf = NULL;
586 }
587 
588 /* Times per second to check for pending console tty messages. */
589 static int constty_wakeups_per_second = 5;
590 SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
591     &constty_wakeups_per_second, 0,
592     "Times per second to check for pending console tty messages");
593 
594 static void
constty_timeout(void * arg)595 constty_timeout(void *arg)
596 {
597 	int c;
598 
599 	if (constty != NULL) {
600 		tty_lock(constty);
601 		while ((c = msgbuf_getchar(&consmsgbuf)) != -1) {
602 			if (tty_putchar(constty, c) < 0) {
603 				tty_unlock(constty);
604 				constty = NULL;
605 				break;
606 			}
607 		}
608 
609 		if (constty != NULL)
610 			tty_unlock(constty);
611 	}
612 	if (constty != NULL) {
613 		callout_reset(&conscallout, hz / constty_wakeups_per_second,
614 		    constty_timeout, NULL);
615 	} else {
616 		/* Deallocate the constty buffer memory. */
617 		constty_clear();
618 	}
619 }
620 
621 static void
cn_drvinit(void * unused)622 cn_drvinit(void *unused)
623 {
624 
625 	mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS);
626 	use_cnputs_mtx = 1;
627 }
628 
629 SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL);
630 
631 /*
632  * Sysbeep(), if we have hardware for it
633  */
634 
635 #ifdef HAS_TIMER_SPKR
636 
637 static int beeping;
638 static struct callout beeping_timer;
639 
640 static void
sysbeepstop(void * chan)641 sysbeepstop(void *chan)
642 {
643 
644 	timer_spkr_release();
645 	beeping = 0;
646 }
647 
648 int
sysbeep(int pitch,int period)649 sysbeep(int pitch, int period)
650 {
651 
652 	if (timer_spkr_acquire()) {
653 		if (!beeping) {
654 			/* Something else owns it. */
655 			return (EBUSY);
656 		}
657 	}
658 	timer_spkr_setfreq(pitch);
659 	if (!beeping) {
660 		beeping = period;
661 		callout_reset(&beeping_timer, period, sysbeepstop, NULL);
662 	}
663 	return (0);
664 }
665 
666 static void
sysbeep_init(void * unused)667 sysbeep_init(void *unused)
668 {
669 
670 	callout_init(&beeping_timer, 1);
671 }
672 SYSINIT(sysbeep, SI_SUB_SOFTINTR, SI_ORDER_ANY, sysbeep_init, NULL);
673 #else
674 
675 /*
676  * No hardware, no sound
677  */
678 
679 int
sysbeep(int pitch __unused,int period __unused)680 sysbeep(int pitch __unused, int period __unused)
681 {
682 
683 	return (ENODEV);
684 }
685 
686 #endif
687 
688 /*
689  * Temporary support for sc(4) to vt(4) transition.
690  */
691 static unsigned vty_prefer;
692 static char vty_name[16];
693 SYSCTL_STRING(_kern, OID_AUTO, vty, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, vty_name,
694     0, "Console vty driver");
695 
696 int
vty_enabled(unsigned vty)697 vty_enabled(unsigned vty)
698 {
699 	static unsigned vty_selected = 0;
700 
701 	if (vty_selected == 0) {
702 		TUNABLE_STR_FETCH("kern.vty", vty_name, sizeof(vty_name));
703 		do {
704 #if defined(DEV_SC)
705 			if (strcmp(vty_name, "sc") == 0) {
706 				vty_selected = VTY_SC;
707 				break;
708 			}
709 #endif
710 #if defined(DEV_VT)
711 			if (strcmp(vty_name, "vt") == 0) {
712 				vty_selected = VTY_VT;
713 				break;
714 			}
715 #endif
716 			if (vty_prefer != 0) {
717 				vty_selected = vty_prefer;
718 				break;
719 			}
720 #if defined(DEV_VT)
721 			vty_selected = VTY_VT;
722 #elif defined(DEV_SC)
723 			vty_selected = VTY_SC;
724 #endif
725 		} while (0);
726 
727 		if (vty_selected == VTY_VT)
728 			strcpy(vty_name, "vt");
729 		else if (vty_selected == VTY_SC)
730 			strcpy(vty_name, "sc");
731 	}
732 	return ((vty_selected & vty) != 0);
733 }
734 
735 void
vty_set_preferred(unsigned vty)736 vty_set_preferred(unsigned vty)
737 {
738 
739 	vty_prefer = vty;
740 #if !defined(DEV_SC)
741 	vty_prefer &= ~VTY_SC;
742 #endif
743 #if !defined(DEV_VT)
744 	vty_prefer &= ~VTY_VT;
745 #endif
746 }
747 
748