Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/kern/kern_cons.c
39475 views
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 <[email protected]>
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
if (cn_mute || c == '\0')
529
return;
530
531
#ifdef EARLY_PRINTF
532
if (early_putc != NULL) {
533
if (c == '\n')
534
early_putc('\r');
535
early_putc(c);
536
return;
537
}
538
#endif
539
540
STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
541
cn = cnd->cnd_cn;
542
if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
543
if (c == '\n')
544
cn->cn_ops->cn_putc(cn, '\r');
545
cn->cn_ops->cn_putc(cn, c);
546
}
547
}
548
if (console_pausing && c == '\n' && !kdb_active) {
549
for (cp = console_pausestr; *cp != '\0'; cp++)
550
cnputc(*cp);
551
cngrab();
552
if (cngetc() == '.')
553
console_pausing = false;
554
cnungrab();
555
cnputc('\r');
556
for (cp = console_pausestr; *cp != '\0'; cp++)
557
cnputc(' ');
558
cnputc('\r');
559
}
560
}
561
562
void
563
cnputsn(const char *p, size_t n)
564
{
565
size_t i;
566
bool unlock_reqd = false;
567
568
if (mtx_initialized(&cnputs_mtx)) {
569
/*
570
* NOTE: Debug prints and/or witness printouts in
571
* console driver clients can cause the "cnputs_mtx"
572
* mutex to recurse. Simply return if that happens.
573
*/
574
if (mtx_owned(&cnputs_mtx))
575
return;
576
mtx_lock_spin(&cnputs_mtx);
577
unlock_reqd = true;
578
}
579
580
for (i = 0; i < n; i++)
581
cnputc(p[i]);
582
583
if (unlock_reqd)
584
mtx_unlock_spin(&cnputs_mtx);
585
}
586
587
void
588
cnputs(const char *p)
589
{
590
cnputsn(p, strlen(p));
591
}
592
593
static unsigned int consmsgbuf_size = 65536;
594
SYSCTL_UINT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RWTUN, &consmsgbuf_size,
595
0, "Console tty buffer size");
596
597
/*
598
* Redirect console output to a tty.
599
*/
600
int
601
constty_set(struct tty *tp)
602
{
603
int size = consmsgbuf_size;
604
void *buf = NULL;
605
606
tty_assert_locked(tp);
607
if (constty == tp)
608
return (0);
609
if (constty != NULL)
610
return (EBUSY);
611
612
if (consbuf == NULL) {
613
tty_unlock(tp);
614
buf = malloc(size, M_TTYCONS, M_WAITOK);
615
tty_lock(tp);
616
}
617
mtx_lock(&constty_mtx);
618
if (constty != NULL) {
619
mtx_unlock(&constty_mtx);
620
free(buf, M_TTYCONS);
621
return (EBUSY);
622
}
623
if (consbuf == NULL) {
624
consbuf = buf;
625
msgbuf_init(&consmsgbuf, buf, size);
626
} else
627
free(buf, M_TTYCONS);
628
constty = tp;
629
mtx_unlock(&constty_mtx);
630
631
callout_init_mtx(&conscallout, tty_getlock(tp), 0);
632
constty_timeout(tp);
633
return (0);
634
}
635
636
/*
637
* Disable console redirection to a tty.
638
*/
639
int
640
constty_clear(struct tty *tp)
641
{
642
int c;
643
644
tty_assert_locked(tp);
645
if (constty != tp)
646
return (ENXIO);
647
callout_stop(&conscallout);
648
mtx_lock(&constty_mtx);
649
constty = NULL;
650
mtx_unlock(&constty_mtx);
651
while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
652
cnputc(c);
653
/* We never free consbuf because it can still be in use. */
654
return (0);
655
}
656
657
/* Times per second to check for pending console tty messages. */
658
static int constty_wakeups_per_second = 15;
659
SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
660
&constty_wakeups_per_second, 0,
661
"Times per second to check for pending console tty messages");
662
663
static void
664
constty_timeout(void *arg)
665
{
666
struct tty *tp = arg;
667
int c;
668
669
tty_assert_locked(tp);
670
while ((c = msgbuf_getchar(&consmsgbuf)) != -1) {
671
if (tty_putchar(tp, c) < 0) {
672
constty_clear(tp);
673
return;
674
}
675
}
676
callout_reset_sbt(&conscallout, SBT_1S / constty_wakeups_per_second,
677
0, constty_timeout, tp, C_PREL(1));
678
}
679
680
/*
681
* Sysbeep(), if we have hardware for it
682
*/
683
684
#ifdef HAS_TIMER_SPKR
685
686
static bool beeping;
687
static struct callout beeping_timer;
688
689
static void
690
sysbeepstop(void *chan)
691
{
692
693
timer_spkr_release();
694
beeping = false;
695
}
696
697
int
698
sysbeep(int pitch, sbintime_t duration)
699
{
700
701
if (timer_spkr_acquire()) {
702
if (!beeping) {
703
/* Something else owns it. */
704
return (EBUSY);
705
}
706
}
707
timer_spkr_setfreq(pitch);
708
if (!beeping) {
709
beeping = true;
710
callout_reset_sbt(&beeping_timer, duration, 0, sysbeepstop,
711
NULL, C_PREL(5));
712
}
713
return (0);
714
}
715
716
static void
717
sysbeep_init(void *unused)
718
{
719
720
callout_init(&beeping_timer, 1);
721
}
722
SYSINIT(sysbeep, SI_SUB_SOFTINTR, SI_ORDER_ANY, sysbeep_init, NULL);
723
#else
724
725
/*
726
* No hardware, no sound
727
*/
728
729
int
730
sysbeep(int pitch __unused, sbintime_t duration __unused)
731
{
732
733
return (ENODEV);
734
}
735
736
#endif
737
738
/*
739
* Temporary support for sc(4) to vt(4) transition.
740
*/
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 defined(DEV_VT)
766
vty_selected = VTY_VT;
767
#elif defined(DEV_SC)
768
vty_selected = VTY_SC;
769
#endif
770
} while (0);
771
772
if (vty_selected == VTY_VT)
773
strcpy(vty_name, "vt");
774
else if (vty_selected == VTY_SC)
775
strcpy(vty_name, "sc");
776
}
777
return ((vty_selected & vty) != 0);
778
}
779
780