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 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