]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/kern_cons.c
cons: Add boot option to mute boot messages after banner
[FreeBSD/FreeBSD.git] / sys / kern / kern_cons.c
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
40 #include <sys/cdefs.h>
41 #include "opt_ddb.h"
42 #include "opt_syscons.h"
43
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/lock.h>
47 #include <sys/mutex.h>
48 #include <sys/conf.h>
49 #include <sys/cons.h>
50 #include <sys/fcntl.h>
51 #include <sys/kbio.h>
52 #include <sys/kdb.h>
53 #include <sys/kernel.h>
54 #include <sys/malloc.h>
55 #include <sys/msgbuf.h>
56 #include <sys/namei.h>
57 #include <sys/priv.h>
58 #include <sys/proc.h>
59 #include <sys/queue.h>
60 #include <sys/reboot.h>
61 #include <sys/sysctl.h>
62 #include <sys/sbuf.h>
63 #include <sys/tslog.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 <dev/kbd/kbdreg.h>
71
72 #include <machine/cpu.h>
73 #include <machine/clock.h>
74
75 /*
76  * Check for 'options EARLY_PRINTF' that may have been used in old kernel
77  * config files. If you are hitting this error you should update your
78  * config to use 'options EARLY_PRINTF=<device name>', e.g. with the
79  * Arm pl011 use:
80  *
81  * options EARLY_PRINTF=pl011
82  */
83 #if CHECK_EARLY_PRINTF(1)
84 #error Update your config to use 'options EARLY_PRINTF=<device name>'
85 #endif
86
87 static MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling");
88
89 struct cn_device {
90         STAILQ_ENTRY(cn_device) cnd_next;
91         struct          consdev *cnd_cn;
92 };
93
94 #define CNDEVPATHMAX    32
95 #define CNDEVTAB_SIZE   4
96 static struct cn_device cn_devtab[CNDEVTAB_SIZE];
97 static STAILQ_HEAD(, cn_device) cn_devlist =
98     STAILQ_HEAD_INITIALIZER(cn_devlist);
99
100 int     cons_avail_mask = 0;    /* Bit mask. Each registered low level console
101                                  * which is currently unavailable for inpit
102                                  * (i.e., if it is in graphics mode) will have
103                                  * this bit cleared.
104                                  */
105
106 int cn_mute;
107 SYSCTL_INT(_kern, OID_AUTO, consmute, CTLFLAG_RW, &cn_mute, 0,
108     "State of the console muting");
109
110 static char *consbuf;                   /* buffer used by `consmsgbuf' */
111 static struct callout conscallout;      /* callout for outputting to constty */
112 struct msgbuf consmsgbuf;               /* message buffer for console tty */
113 static bool console_pausing;            /* pause after each line during probe */
114 static const char console_pausestr[] =
115 "<pause; press any key to proceed to next line or '.' to end pause mode>";
116 struct tty *constty;                    /* pointer to console "window" tty */
117 static struct mtx constty_mtx;          /* Mutex for constty assignment. */
118 MTX_SYSINIT(constty_mtx, &constty_mtx, "constty_mtx", MTX_DEF);
119 static struct mtx cnputs_mtx;           /* Mutex for cnputs(). */
120 MTX_SYSINIT(cnputs_mtx, &cnputs_mtx, "cnputs_mtx", MTX_SPIN | MTX_NOWITNESS);
121
122 static void constty_timeout(void *arg);
123
124 static struct consdev cons_consdev;
125 DATA_SET(cons_set, cons_consdev);
126 SET_DECLARE(cons_set, struct consdev);
127
128 /*
129  * Stub for configurations that don't actually have a keyboard driver. Inclusion
130  * of kbd.c is contingent on any number of keyboard/console drivers being
131  * present in the kernel; rather than trying to catch them all, we'll just
132  * maintain this weak kbdinit that will be overridden by the strong version in
133  * kbd.c if it's present.
134  */
135 __weak_symbol void
136 kbdinit(void)
137 {
138
139 }
140
141 static void
142 mute_console(void *data __unused)
143 {
144
145         if ((boothowto & (RB_MUTEMSGS | RB_VERBOSE)) == RB_MUTEMSGS) {
146                 printf("-- Muting boot messages --\n");
147                 cn_mute = 1;
148         }
149 }
150
151 SYSINIT(mute_console, SI_SUB_COPYRIGHT, SI_ORDER_ANY, mute_console, NULL);
152
153 void
154 cninit(void)
155 {
156         struct consdev *best_cn, *cn, **list;
157
158         TSENTER();
159         /*
160          * Check if we should mute the console (for security reasons perhaps)
161          * It can be changes dynamically using sysctl kern.consmute
162          * once we are up and going.
163          * 
164          */
165         cn_mute = ((boothowto & (RB_MUTE
166                         |RB_SINGLE
167                         |RB_VERBOSE
168                         |RB_ASKNAME)) == RB_MUTE);
169
170         /*
171          * Bring up the kbd layer just in time for cnprobe.  Console drivers
172          * have a dependency on kbd being ready, so this fits nicely between the
173          * machdep callers of cninit() and MI probing/initialization of consoles
174          * here.
175          */
176         kbdinit();
177
178         /*
179          * Find the first console with the highest priority.
180          */
181         best_cn = NULL;
182         SET_FOREACH(list, cons_set) {
183                 cn = *list;
184                 cnremove(cn);
185                 /* Skip cons_consdev. */
186                 if (cn->cn_ops == NULL)
187                         continue;
188                 cn->cn_ops->cn_probe(cn);
189                 if (cn->cn_pri == CN_DEAD)
190                         continue;
191                 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
192                         best_cn = cn;
193                 if (boothowto & RB_MULTIPLE) {
194                         /*
195                          * Initialize console, and attach to it.
196                          */
197                         cn->cn_ops->cn_init(cn);
198                         cnadd(cn);
199                 }
200         }
201         if (best_cn == NULL)
202                 return;
203         if ((boothowto & RB_MULTIPLE) == 0) {
204                 best_cn->cn_ops->cn_init(best_cn);
205                 cnadd(best_cn);
206         }
207         if (boothowto & RB_PAUSE)
208                 console_pausing = true;
209         /*
210          * Make the best console the preferred console.
211          */
212         cnselect(best_cn);
213
214 #ifdef EARLY_PRINTF
215         /*
216          * Release early console.
217          */
218         early_putc = NULL;
219 #endif
220         TSEXIT();
221 }
222
223 void
224 cninit_finish(void)
225 {
226         console_pausing = false;
227
228
229 /* add a new physical console to back the virtual console */
230 int
231 cnadd(struct consdev *cn)
232 {
233         struct cn_device *cnd;
234         int i;
235
236         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
237                 if (cnd->cnd_cn == cn)
238                         return (0);
239         for (i = 0; i < CNDEVTAB_SIZE; i++) {
240                 cnd = &cn_devtab[i];
241                 if (cnd->cnd_cn == NULL)
242                         break;
243         }
244         if (cnd->cnd_cn != NULL)
245                 return (ENOMEM);
246         cnd->cnd_cn = cn;
247         if (cn->cn_name[0] == '\0') {
248                 /* XXX: it is unclear if/where this print might output */
249                 printf("WARNING: console at %p has no name\n", cn);
250         }
251         STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
252         if (STAILQ_FIRST(&cn_devlist) == cnd)
253                 ttyconsdev_select(cnd->cnd_cn->cn_name);
254
255         /* Add device to the active mask. */
256         cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0);
257
258         return (0);
259 }
260
261 void
262 cnremove(struct consdev *cn)
263 {
264         struct cn_device *cnd;
265         int i;
266
267         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
268                 if (cnd->cnd_cn != cn)
269                         continue;
270                 if (STAILQ_FIRST(&cn_devlist) == cnd)
271                         ttyconsdev_select(NULL);
272                 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
273                 cnd->cnd_cn = NULL;
274
275                 /* Remove this device from available mask. */
276                 for (i = 0; i < CNDEVTAB_SIZE; i++) 
277                         if (cnd == &cn_devtab[i]) {
278                                 cons_avail_mask &= ~(1 << i);
279                                 break;
280                         }
281 #if 0
282                 /*
283                  * XXX
284                  * syscons gets really confused if console resources are
285                  * freed after the system has initialized.
286                  */
287                 if (cn->cn_term != NULL)
288                         cn->cn_ops->cn_term(cn);
289 #endif
290                 return;
291         }
292 }
293
294 void
295 cnselect(struct consdev *cn)
296 {
297         struct cn_device *cnd;
298
299         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
300                 if (cnd->cnd_cn != cn)
301                         continue;
302                 if (cnd == STAILQ_FIRST(&cn_devlist))
303                         return;
304                 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
305                 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
306                 ttyconsdev_select(cnd->cnd_cn->cn_name);
307                 return;
308         }
309 }
310
311 void
312 cnavailable(struct consdev *cn, int available)
313 {
314         int i;
315
316         for (i = 0; i < CNDEVTAB_SIZE; i++) {
317                 if (cn_devtab[i].cnd_cn == cn)
318                         break;
319         }
320         if (available) {
321                 if (i < CNDEVTAB_SIZE)
322                         cons_avail_mask |= (1 << i); 
323                 cn->cn_flags &= ~CN_FLAG_NOAVAIL;
324         } else {
325                 if (i < CNDEVTAB_SIZE)
326                         cons_avail_mask &= ~(1 << i);
327                 cn->cn_flags |= CN_FLAG_NOAVAIL;
328         }
329 }
330
331 int
332 cnunavailable(void)
333 {
334
335         return (cons_avail_mask == 0);
336 }
337
338 /*
339  * sysctl_kern_console() provides output parseable in conscontrol(1).
340  */
341 static int
342 sysctl_kern_console(SYSCTL_HANDLER_ARGS)
343 {
344         struct cn_device *cnd;
345         struct consdev *cp, **list;
346         char *p;
347         bool delete;
348         int error;
349         struct sbuf *sb;
350
351         sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND |
352             SBUF_INCLUDENUL);
353         if (sb == NULL)
354                 return (ENOMEM);
355         sbuf_clear(sb);
356         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
357                 sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name);
358         sbuf_putc(sb, '/');
359         SET_FOREACH(list, cons_set) {
360                 cp = *list;
361                 if (cp->cn_name[0] != '\0')
362                         sbuf_printf(sb, "%s,", cp->cn_name);
363         }
364         sbuf_finish(sb);
365         error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req);
366         if (error == 0 && req->newptr != NULL) {
367                 p = sbuf_data(sb);
368                 error = ENXIO;
369                 delete = false;
370                 if (*p == '-') {
371                         delete = true;
372                         p++;
373                 }
374                 SET_FOREACH(list, cons_set) {
375                         cp = *list;
376                         if (strcmp(p, cp->cn_name) != 0)
377                                 continue;
378                         if (delete) {
379                                 cnremove(cp);
380                                 error = 0;
381                         } else {
382                                 error = cnadd(cp);
383                                 if (error == 0)
384                                         cnselect(cp);
385                         }
386                         break;
387                 }
388         }
389         sbuf_delete(sb);
390         return (error);
391 }
392
393 SYSCTL_PROC(_kern, OID_AUTO, console,
394     CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 0, 0,
395     sysctl_kern_console, "A",
396     "Console device control");
397
398 void
399 cngrab(void)
400 {
401         struct cn_device *cnd;
402         struct consdev *cn;
403
404         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
405                 cn = cnd->cnd_cn;
406                 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
407                         cn->cn_ops->cn_grab(cn);
408         }
409 }
410
411 void
412 cnungrab(void)
413 {
414         struct cn_device *cnd;
415         struct consdev *cn;
416
417         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
418                 cn = cnd->cnd_cn;
419                 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
420                         cn->cn_ops->cn_ungrab(cn);
421         }
422 }
423
424 void
425 cnresume(void)
426 {
427         struct cn_device *cnd;
428         struct consdev *cn;
429
430         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
431                 cn = cnd->cnd_cn;
432                 if (cn->cn_ops->cn_resume != NULL)
433                         cn->cn_ops->cn_resume(cn);
434         }
435 }
436
437 /*
438  * Low level console routines.
439  */
440 int
441 cngetc(void)
442 {
443         int c;
444
445         if (cn_mute)
446                 return (-1);
447         while ((c = cncheckc()) == -1)
448                 cpu_spinwait();
449         if (c == '\r')
450                 c = '\n';               /* console input is always ICRNL */
451         return (c);
452 }
453
454 int
455 cncheckc(void)
456 {
457         struct cn_device *cnd;
458         struct consdev *cn;
459         int c;
460
461         if (cn_mute)
462                 return (-1);
463         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
464                 cn = cnd->cnd_cn;
465                 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
466                         c = cn->cn_ops->cn_getc(cn);
467                         if (c != -1)
468                                 return (c);
469                 }
470         }
471         return (-1);
472 }
473
474 void
475 cngets(char *cp, size_t size, int visible)
476 {
477         char *lp, *end;
478         int c;
479
480         cngrab();
481
482         lp = cp;
483         end = cp + size - 1;
484         for (;;) {
485                 c = cngetc() & 0177;
486                 switch (c) {
487                 case '\n':
488                 case '\r':
489                         cnputc(c);
490                         *lp = '\0';
491                         cnungrab();
492                         return;
493                 case '\b':
494                 case '\177':
495                         if (lp > cp) {
496                                 if (visible)
497                                         cnputs("\b \b");
498                                 lp--;
499                         }
500                         continue;
501                 case '\0':
502                         continue;
503                 default:
504                         if (lp < end) {
505                                 switch (visible) {
506                                 case GETS_NOECHO:
507                                         break;
508                                 case GETS_ECHOPASS:
509                                         cnputc('*');
510                                         break;
511                                 default:
512                                         cnputc(c);
513                                         break;
514                                 }
515                                 *lp++ = c;
516                         }
517                 }
518         }
519 }
520
521 void
522 cnputc(int c)
523 {
524         struct cn_device *cnd;
525         struct consdev *cn;
526         const char *cp;
527
528 #ifdef EARLY_PRINTF
529         if (early_putc != NULL) {
530                 if (c == '\n')
531                         early_putc('\r');
532                 early_putc(c);
533                 return;
534         }
535 #endif
536
537         if (cn_mute || c == '\0')
538                 return;
539         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
540                 cn = cnd->cnd_cn;
541                 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
542                         if (c == '\n')
543                                 cn->cn_ops->cn_putc(cn, '\r');
544                         cn->cn_ops->cn_putc(cn, c);
545                 }
546         }
547         if (console_pausing && c == '\n' && !kdb_active) {
548                 for (cp = console_pausestr; *cp != '\0'; cp++)
549                         cnputc(*cp);
550                 cngrab();
551                 if (cngetc() == '.')
552                         console_pausing = false;
553                 cnungrab();
554                 cnputc('\r');
555                 for (cp = console_pausestr; *cp != '\0'; cp++)
556                         cnputc(' ');
557                 cnputc('\r');
558         }
559 }
560
561 void
562 cnputsn(const char *p, size_t n)
563 {
564         size_t i;
565         bool unlock_reqd = false;
566
567         if (mtx_initialized(&cnputs_mtx)) {
568                 /*
569                  * NOTE: Debug prints and/or witness printouts in
570                  * console driver clients can cause the "cnputs_mtx"
571                  * mutex to recurse. Simply return if that happens.
572                  */
573                 if (mtx_owned(&cnputs_mtx))
574                         return;
575                 mtx_lock_spin(&cnputs_mtx);
576                 unlock_reqd = true;
577         }
578
579         for (i = 0; i < n; i++)
580                 cnputc(p[i]);
581
582         if (unlock_reqd)
583                 mtx_unlock_spin(&cnputs_mtx);
584 }
585
586 void
587 cnputs(const char *p)
588 {
589         cnputsn(p, strlen(p));
590 }
591
592 static unsigned int consmsgbuf_size = 65536;
593 SYSCTL_UINT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RWTUN, &consmsgbuf_size,
594     0, "Console tty buffer size");
595
596 /*
597  * Redirect console output to a tty.
598  */
599 int
600 constty_set(struct tty *tp)
601 {
602         int size = consmsgbuf_size;
603         void *buf = NULL;
604
605         tty_assert_locked(tp);
606         if (constty == tp)
607                 return (0);
608         if (constty != NULL)
609                 return (EBUSY);
610
611         if (consbuf == NULL) {
612                 tty_unlock(tp);
613                 buf = malloc(size, M_TTYCONS, M_WAITOK);
614                 tty_lock(tp);
615         }
616         mtx_lock(&constty_mtx);
617         if (constty != NULL) {
618                 mtx_unlock(&constty_mtx);
619                 free(buf, M_TTYCONS);
620                 return (EBUSY);
621         }
622         if (consbuf == NULL) {
623                 consbuf = buf;
624                 msgbuf_init(&consmsgbuf, buf, size);
625         } else
626                 free(buf, M_TTYCONS);
627         constty = tp;
628         mtx_unlock(&constty_mtx);
629
630         callout_init_mtx(&conscallout, tty_getlock(tp), 0);
631         constty_timeout(tp);
632         return (0);
633 }
634
635 /*
636  * Disable console redirection to a tty.
637  */
638 int
639 constty_clear(struct tty *tp)
640 {
641         int c;
642
643         tty_assert_locked(tp);
644         if (constty != tp)
645                 return (ENXIO);
646         callout_stop(&conscallout);
647         mtx_lock(&constty_mtx);
648         constty = NULL;
649         mtx_unlock(&constty_mtx);
650         while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
651                 cnputc(c);
652         /* We never free consbuf because it can still be in use. */
653         return (0);
654 }
655
656 /* Times per second to check for pending console tty messages. */
657 static int constty_wakeups_per_second = 15;
658 SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
659     &constty_wakeups_per_second, 0,
660     "Times per second to check for pending console tty messages");
661
662 static void
663 constty_timeout(void *arg)
664 {
665         struct tty *tp = arg;
666         int c;
667
668         tty_assert_locked(tp);
669         while ((c = msgbuf_getchar(&consmsgbuf)) != -1) {
670                 if (tty_putchar(tp, c) < 0) {
671                         constty_clear(tp);
672                         return;
673                 }
674         }
675         callout_reset_sbt(&conscallout, SBT_1S / constty_wakeups_per_second,
676             0, constty_timeout, tp, C_PREL(1));
677 }
678
679 /*
680  * Sysbeep(), if we have hardware for it
681  */
682
683 #ifdef HAS_TIMER_SPKR
684
685 static bool beeping;
686 static struct callout beeping_timer;
687
688 static void
689 sysbeepstop(void *chan)
690 {
691
692         timer_spkr_release();
693         beeping = false;
694 }
695
696 int
697 sysbeep(int pitch, sbintime_t duration)
698 {
699
700         if (timer_spkr_acquire()) {
701                 if (!beeping) {
702                         /* Something else owns it. */
703                         return (EBUSY);
704                 }
705         }
706         timer_spkr_setfreq(pitch);
707         if (!beeping) {
708                 beeping = true;
709                 callout_reset_sbt(&beeping_timer, duration, 0, sysbeepstop,
710                     NULL, C_PREL(5));
711         }
712         return (0);
713 }
714
715 static void
716 sysbeep_init(void *unused)
717 {
718
719         callout_init(&beeping_timer, 1);
720 }
721 SYSINIT(sysbeep, SI_SUB_SOFTINTR, SI_ORDER_ANY, sysbeep_init, NULL);
722 #else
723
724 /*
725  * No hardware, no sound
726  */
727
728 int
729 sysbeep(int pitch __unused, sbintime_t duration __unused)
730 {
731
732         return (ENODEV);
733 }
734
735 #endif
736
737 /*
738  * Temporary support for sc(4) to vt(4) transition.
739  */
740 static unsigned vty_prefer;
741 static char vty_name[16];
742 SYSCTL_STRING(_kern, OID_AUTO, vty, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, vty_name,
743     0, "Console vty driver");
744
745 int
746 vty_enabled(unsigned vty)
747 {
748         static unsigned vty_selected = 0;
749
750         if (vty_selected == 0) {
751                 TUNABLE_STR_FETCH("kern.vty", vty_name, sizeof(vty_name));
752                 do {
753 #if defined(DEV_SC)
754                         if (strcmp(vty_name, "sc") == 0) {
755                                 vty_selected = VTY_SC;
756                                 break;
757                         }
758 #endif
759 #if defined(DEV_VT)
760                         if (strcmp(vty_name, "vt") == 0) {
761                                 vty_selected = VTY_VT;
762                                 break;
763                         }
764 #endif
765                         if (vty_prefer != 0) {
766                                 vty_selected = vty_prefer;
767                                 break;
768                         }
769 #if defined(DEV_VT)
770                         vty_selected = VTY_VT;
771 #elif defined(DEV_SC)
772                         vty_selected = VTY_SC;
773 #endif
774                 } while (0);
775
776                 if (vty_selected == VTY_VT)
777                         strcpy(vty_name, "vt");
778                 else if (vty_selected == VTY_SC)
779                         strcpy(vty_name, "sc");
780         }
781         return ((vty_selected & vty) != 0);
782 }
783
784 void
785 vty_set_preferred(unsigned vty)
786 {
787
788         vty_prefer = vty;
789 #if !defined(DEV_SC)
790         vty_prefer &= ~VTY_SC;
791 #endif
792 #if !defined(DEV_VT)
793         vty_prefer &= ~VTY_VT;
794 #endif
795 }