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