1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 #pragma ident "%Z%%M% %I% %E% SMI" 26 27 /* 28 * Listen thread creates a console thread whenever there is a tcp client 29 * made a conection to its port. In the console thread, if there are 30 * multiple consoles in the group, client will be asked for a console selection. 31 * a write thread for a console is created when first client connects to a 32 * selected console and console thread becomes read thread for the client. 33 */ 34 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 #include <sys/types.h> 40 #include <sys/socket.h> 41 #include <netinet/in.h> 42 #include <thread.h> 43 #include <synch.h> 44 #include <signal.h> 45 #include <assert.h> 46 #include <ctype.h> 47 #include <syslog.h> 48 #include <libintl.h> 49 #include <netdb.h> 50 #include "vntsd.h" 51 #include "chars.h" 52 53 /* display domain names in the group */ 54 static boolean_t 55 display_domain_name(vntsd_cons_t *consp, int *fd) 56 { 57 char buf[VNTSD_LINE_LEN]; 58 char *status; 59 60 61 if (consp->clientpq != NULL) { 62 status = gettext("connected"); 63 } else if (consp->status & VNTSD_CONS_DELETED) { 64 status = gettext("removing..."); 65 } else { 66 status = gettext("online"); 67 } 68 69 (void) snprintf(buf, sizeof (buf), "%-20d%-30s%-25s%s", 70 consp->cons_no, consp->domain_name, status, vntsd_eol); 71 72 return (vntsd_write_fd(*fd, buf, strlen(buf)) != VNTSD_SUCCESS); 73 } 74 75 /* output connected message to tcp client */ 76 static int 77 write_connect_msg(vntsd_client_t *clientp, char *group_name, 78 char *domain_name) 79 { 80 81 int rv = VNTSD_SUCCESS; 82 char buf[VNTSD_LINE_LEN]; 83 84 if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) != 85 VNTSD_SUCCESS) { 86 return (rv); 87 } 88 89 (void) snprintf(buf, sizeof (buf), 90 gettext("Connecting to console \"%s\" in group \"%s\" ...."), 91 domain_name, group_name); 92 93 if ((rv = vntsd_write_line(clientp, buf)) != VNTSD_SUCCESS) { 94 return (rv); 95 } 96 97 if ((rv = vntsd_write_line(clientp, 98 gettext("Press ~? for control options .."))) != 99 VNTSD_SUCCESS) { 100 return (rv); 101 } 102 103 return (VNTSD_SUCCESS); 104 } 105 106 static int 107 create_write_thread(vntsd_cons_t *consp) 108 { 109 110 assert(consp); 111 112 /* create write thread for the console */ 113 (void) mutex_lock(&consp->lock); 114 if (thr_create(NULL, 0, (thr_func_t)vntsd_write_thread, 115 (void *)consp, NULL, &consp->wr_tid)) { 116 117 DERR(stderr, "t@%d create_rd_wr_thread@%d: " 118 "create write thread failed\n", 119 thr_self(), consp->cons_no); 120 (void) close(consp->vcc_fd); 121 consp->vcc_fd = -1; 122 (void) mutex_unlock(&consp->lock); 123 124 return (VNTSD_ERR_CREATE_WR_THR); 125 } 126 (void) mutex_unlock(&consp->lock); 127 return (VNTSD_SUCCESS); 128 } 129 130 /* Display all domain consoles in a group. */ 131 static int 132 list_all_domains(vntsd_group_t *groupp, vntsd_client_t *clientp) 133 { 134 char vntsd_line[VNTSD_LINE_LEN]; 135 int rv = VNTSD_SUCCESS; 136 137 if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) 138 != VNTSD_SUCCESS) { 139 return (rv); 140 } 141 142 /* 143 * TRANSLATION_NOTE 144 * The following three strings of the form "DOMAIN .." are table 145 * headers and should be all uppercase. 146 */ 147 (void) snprintf(vntsd_line, sizeof (vntsd_line), 148 "%-20s%-30s%-25s", 149 gettext("DOMAIN ID"), gettext("DOMAIN NAME"), 150 gettext("DOMAIN STATE")); 151 152 if ((rv = vntsd_write_line(clientp, vntsd_line)) != VNTSD_SUCCESS) { 153 return (rv); 154 } 155 156 (void) mutex_lock(&groupp->lock); 157 158 if (vntsd_que_find(groupp->conspq, (compare_func_t)display_domain_name, 159 &(clientp->sockfd)) != NULL) { 160 rv = VNTSD_ERR_WRITE_CLIENT; 161 } 162 163 (void) mutex_unlock(&groupp->lock); 164 165 return (rv); 166 } 167 168 /* display help */ 169 static int 170 display_help(vntsd_client_t *clientp) 171 { 172 int rv = VNTSD_SUCCESS; 173 char *bufp; 174 175 if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) 176 != VNTSD_SUCCESS) { 177 return (rv); 178 } 179 180 /* 181 * TRANSLATION_NOTE 182 * The following three strings of the form ". -- ..." are help 183 * messages for single character commands. Do not translate the 184 * character before the --. 185 */ 186 bufp = gettext("h -- this help"); 187 188 if ((rv = vntsd_write_line(clientp, bufp)) != VNTSD_SUCCESS) { 189 return (rv); 190 } 191 192 bufp = gettext("l -- list of consoles"); 193 194 if ((rv = vntsd_write_line(clientp, bufp)) != VNTSD_SUCCESS) { 195 return (rv); 196 } 197 198 bufp = gettext("q -- quit"); 199 200 if ((rv = vntsd_write_line(clientp, bufp)) != VNTSD_SUCCESS) { 201 return (rv); 202 } 203 204 /* 205 * TRANSLATION_NOTE 206 * In the following string, "id" is a short mnemonic for 207 * "identifier" and both occurrences should be translated. 208 */ 209 210 bufp = gettext("c{id}, n{name} -- connect to a console of domain {id}" 211 " or domain {name}"); 212 213 if ((rv = vntsd_write_line(clientp, bufp)) != VNTSD_SUCCESS) { 214 return (rv); 215 } 216 217 return (VNTSD_SUCCESS); 218 } 219 220 /* cons_by_name() - find a console structure according to a ldom's name */ 221 static boolean_t 222 cons_by_name(vntsd_cons_t *consp, char *name) 223 { 224 if (consp->status & VNTSD_CONS_DELETED) { 225 return (B_FALSE); 226 } 227 return (strcmp(consp->domain_name, name) == 0); 228 } 229 230 /* name_to_cons_no - convert a ldom's name to its consno */ 231 static int 232 name_to_cons_no(vntsd_group_t *groupp, char *name) 233 { 234 vntsd_cons_t *consp; 235 236 consp = (vntsd_cons_t *)vntsd_que_find(groupp->conspq, 237 (compare_func_t)cons_by_name, name); 238 239 if (consp == NULL) { 240 return (-1); 241 } 242 243 return (consp->cons_no); 244 } 245 246 /* select a console to connect */ 247 static int 248 select_cons(vntsd_group_t *groupp, vntsd_cons_t **consp, 249 vntsd_client_t *clientp, char c) 250 { 251 int cons_no = -1; 252 int n; 253 int i; 254 char buf[VNTSD_LINE_LEN]; 255 int rv; 256 257 258 259 (void) mutex_lock(&groupp->lock); 260 if (groupp->num_cons == 0) { 261 (void) mutex_unlock(&groupp->lock); 262 /* no console in this group */ 263 return (VNTSD_STATUS_NO_CONS); 264 } 265 (void) mutex_unlock(&groupp->lock); 266 267 268 /* c{id} or n{name} */ 269 270 n = VNTSD_LINE_LEN; 271 272 if ((rv = vntsd_read_line(clientp, buf, &n)) != VNTSD_SUCCESS) { 273 return (rv); 274 } 275 276 /* parse command */ 277 for (i = 0; i < n; i++) { 278 switch (c) { 279 280 case 'c': 281 /* c{id} or c {id} */ 282 if (isspace(buf[i])) { 283 continue; 284 } 285 286 if (!isdigit(buf[i])) { 287 return (VNTSD_ERR_INVALID_INPUT); 288 } 289 290 cons_no = atoi(buf + i); 291 break; 292 293 case 'n': 294 /* n{name) or n {name} */ 295 if (isspace(buf[i])) { 296 continue; 297 } 298 299 buf[n-1] = 0; 300 cons_no = name_to_cons_no(groupp, buf+i); 301 break; 302 303 default: 304 /* should never get here */ 305 return (VNTSD_ERR_INVALID_INPUT); 306 307 } 308 309 /* got user selection */ 310 break; 311 } 312 313 if (cons_no < 0) { 314 return (VNTSD_ERR_INVALID_INPUT); 315 } 316 317 /* get selected console */ 318 (void) mutex_lock(&groupp->lock); 319 320 *consp = (vntsd_cons_t *)vntsd_que_find(groupp->conspq, 321 (compare_func_t)vntsd_cons_by_consno, &cons_no); 322 323 if (*consp == NULL) { 324 /* during console selection, the console has been deleted */ 325 (void) mutex_unlock(&groupp->lock); 326 327 return (VNTSD_ERR_INVALID_INPUT); 328 } 329 if ((*consp)->status & VNTSD_CONS_DELETED) { 330 return (VNTSD_ERR_INVALID_INPUT); 331 } 332 333 (void) mutex_unlock(&groupp->lock); 334 335 return (VNTSD_SUCCESS); 336 } 337 338 /* compare if there is a match console in the gorup */ 339 static boolean_t 340 find_cons_in_group(vntsd_cons_t *consp_in_group, vntsd_cons_t *consp) 341 { 342 if (consp_in_group == consp) { 343 return (B_TRUE); 344 } else { 345 return (B_FALSE); 346 } 347 } 348 349 /* connect a client to a console */ 350 static int 351 connect_cons(vntsd_cons_t *consp, vntsd_client_t *clientp) 352 { 353 int rv, rv1; 354 vntsd_group_t *groupp; 355 356 assert(consp); 357 groupp = consp->group; 358 assert(groupp); 359 assert(clientp); 360 361 (void) mutex_lock(&groupp->lock); 362 363 /* check if console is valid */ 364 consp = vntsd_que_find(groupp->conspq, 365 (compare_func_t)find_cons_in_group, consp); 366 367 if (consp == NULL) { 368 (void) mutex_unlock(&groupp->lock); 369 return (VNTSD_STATUS_NO_CONS); 370 } 371 if (consp->status & VNTSD_CONS_DELETED) { 372 (void) mutex_unlock(&groupp->lock); 373 return (VNTSD_STATUS_NO_CONS); 374 } 375 376 (void) mutex_lock(&consp->lock); 377 (void) mutex_lock(&clientp->lock); 378 379 380 clientp->cons = consp; 381 382 /* enable daemon cmd */ 383 clientp->status &= ~VNTSD_CLIENT_DISABLE_DAEMON_CMD; 384 385 if (consp->clientpq == NULL && consp->vcc_fd == -1) { 386 387 /* 388 * the first connection to a console - a writer 389 * and the console has not opened. 390 */ 391 consp->vcc_fd = vntsd_open_vcc(consp->dev_name, consp->cons_no); 392 if (consp->vcc_fd < 0) { 393 (void) mutex_unlock(&clientp->lock); 394 (void) mutex_unlock(&consp->lock); 395 (void) mutex_unlock(&groupp->lock); 396 assert(consp->group); 397 return (vntsd_vcc_err(consp)); 398 } 399 } 400 401 (void) mutex_unlock(&clientp->lock); 402 403 /* 404 * move the client from group's no console selected queue 405 * to cons queue 406 */ 407 408 rv = vntsd_que_rm(&groupp->no_cons_clientpq, clientp); 409 assert(rv == VNTSD_SUCCESS); 410 411 rv = vntsd_que_append(&consp->clientpq, clientp); 412 (void) mutex_unlock(&groupp->lock); 413 414 if (rv != VNTSD_SUCCESS) { 415 if (consp->clientpq->handle == clientp) { 416 /* writer */ 417 (void) close(consp->vcc_fd); 418 consp->vcc_fd = -1; 419 } 420 421 (void) mutex_unlock(&consp->lock); 422 return (rv); 423 } 424 425 (void) mutex_unlock(&consp->lock); 426 427 if (consp->clientpq->handle == clientp) { 428 /* create a write thread */ 429 rv = create_write_thread(consp); 430 if (rv != VNTSD_SUCCESS) { 431 return (rv); 432 } 433 } 434 435 /* write connecting message */ 436 if ((rv = write_connect_msg(clientp, consp->group->group_name, 437 consp->domain_name)) != VNTSD_SUCCESS) { 438 return (rv); 439 } 440 441 /* process input from client */ 442 rv = vntsd_read(clientp); 443 444 /* client disconnected from the console */ 445 (void) mutex_lock(&groupp->lock); 446 447 /* remove client from console queue */ 448 (void) mutex_lock(&consp->lock); 449 rv1 = vntsd_que_rm(&consp->clientpq, clientp); 450 assert(rv1 == VNTSD_SUCCESS); 451 452 /* append client to group's no console selected queue */ 453 rv1 = vntsd_que_append(&groupp->no_cons_clientpq, clientp); 454 (void) mutex_unlock(&groupp->lock); 455 456 if (consp->clientpq == NULL) { 457 /* clean up console since there is no client connected to it */ 458 assert(consp->vcc_fd != -1); 459 460 /* force write thread to exit */ 461 assert(consp->wr_tid != (thread_t)-1); 462 (void) thr_kill(consp->wr_tid, SIGUSR1); 463 (void) mutex_unlock(&consp->lock); 464 (void) thr_join(consp->wr_tid, NULL, NULL); 465 (void) mutex_lock(&consp->lock); 466 } 467 468 if (consp->status & VNTSD_CONS_SIG_WAIT) { 469 /* console is waiting for client to disconnect */ 470 (void) cond_signal(&consp->cvp); 471 } 472 473 (void) mutex_unlock(&consp->lock); 474 475 return (rv1 == VNTSD_SUCCESS ? rv : rv1); 476 477 } 478 479 /* read command line input */ 480 static int 481 read_cmd(vntsd_client_t *clientp, char *prompt, char *cmd) 482 { 483 int rv; 484 485 /* disable daemon special command */ 486 (void) mutex_lock(&clientp->lock); 487 clientp->status |= VNTSD_CLIENT_DISABLE_DAEMON_CMD; 488 (void) mutex_unlock(&clientp->lock); 489 490 if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) 491 != VNTSD_SUCCESS) { 492 return (rv); 493 } 494 495 if ((rv = vntsd_write_client(clientp, prompt, strlen(prompt))) 496 != VNTSD_SUCCESS) { 497 return (rv); 498 } 499 500 if ((rv = vntsd_read_data(clientp, cmd)) != VNTSD_SUCCESS) { 501 return (rv); 502 } 503 if (*cmd == BS) { 504 return (VNTSD_SUCCESS); 505 } 506 507 rv = vntsd_write_client(clientp, cmd, 1); 508 509 *cmd = tolower(*cmd); 510 511 return (rv); 512 } 513 514 /* reset client for selecting a console in the group */ 515 static void 516 client_init(vntsd_client_t *clientp) 517 { 518 (void) mutex_lock(&clientp->lock); 519 clientp->cons = NULL; 520 clientp->status = 0; 521 (void) mutex_unlock(&clientp->lock); 522 } 523 /* is there any connection to a given console? */ 524 static boolean_t 525 is_client_que_empty(vntsd_cons_t *consp) 526 { 527 boolean_t has_client = B_FALSE; 528 529 (void) mutex_lock(&consp->lock); 530 531 if (consp->clientpq != NULL) 532 has_client = B_TRUE; 533 534 (void) mutex_unlock(&consp->lock); 535 536 return (has_client); 537 } 538 539 /* 540 * close one opened console. 541 * This function is passed to vntsd_que_walk to close one console. 542 * The function returns B_FALSE so that vntsd_que_walk will 543 * continue to apply the function to all consoles in the group. 544 */ 545 static boolean_t 546 close_one_vcc_fd(vntsd_cons_t *consp) 547 { 548 (void) mutex_lock(&consp->lock); 549 550 if (consp->vcc_fd != -1) { 551 (void) close(consp->vcc_fd); 552 consp->vcc_fd = -1; 553 } 554 555 (void) mutex_unlock(&consp->lock); 556 557 return (B_FALSE); 558 } 559 560 561 /* clean up client and exit the thread */ 562 static void 563 client_fini(vntsd_group_t *groupp, vntsd_client_t *clientp) 564 { 565 566 assert(groupp); 567 assert(clientp); 568 569 /* disconnct client from tcp port */ 570 assert(clientp->sockfd != -1); 571 (void) close(clientp->sockfd); 572 573 (void) mutex_lock(&groupp->lock); 574 575 /* 576 * close all consoles in the group if the client is the 577 * last one connected to the group 578 */ 579 if (vntsd_que_walk(groupp->conspq, (el_func_t)is_client_que_empty) == 580 VNTSD_SUCCESS) { 581 (void) vntsd_que_walk(groupp->conspq, 582 (el_func_t)close_one_vcc_fd); 583 } 584 585 586 (void) vntsd_que_rm(&groupp->no_cons_clientpq, clientp); 587 588 if ((groupp->no_cons_clientpq == NULL) && 589 (groupp->status & VNTSD_GROUP_SIG_WAIT)) { 590 /* group is waiting to be deleted */ 591 groupp->status &= ~VNTSD_GROUP_SIG_WAIT; 592 (void) cond_signal(&groupp->cvp); 593 } 594 (void) mutex_unlock(&groupp->lock); 595 596 (void) mutex_destroy(&clientp->lock); 597 free(clientp); 598 599 thr_exit(0); 600 } 601 602 /* check client's status. exit if client quits or fatal errors */ 603 static void 604 console_chk_status(vntsd_group_t *groupp, vntsd_client_t *clientp, int status) 605 { 606 char err_msg[VNTSD_LINE_LEN]; 607 608 D1(stderr, "t@%d console_chk_status() status=%d " 609 "client status=%x num consoles=%d \n", 610 thr_self(), status, clientp->status, groupp->num_cons); 611 612 (void) snprintf(err_msg, VNTSD_LINE_LEN, "console_chk_status client%d" 613 " num_cos=%d", clientp->sockfd, groupp->num_cons); 614 615 if (groupp->num_cons == 0) { 616 /* no more console in the group */ 617 client_fini(groupp, clientp); 618 } 619 620 if (status == VNTSD_STATUS_INTR) { 621 /* reason for signal? */ 622 status = vntsd_cons_chk_intr(clientp); 623 } 624 625 switch (status) { 626 627 case VNTSD_STATUS_CLIENT_QUIT: 628 client_fini(groupp, clientp); 629 return; 630 631 case VNTSD_STATUS_RESELECT_CONS: 632 assert(clientp->cons); 633 if ((groupp->num_cons == 1) && 634 (groupp->conspq->handle == clientp->cons)) { 635 /* no other selection available */ 636 client_fini(groupp, clientp); 637 } else { 638 client_init(clientp); 639 } 640 return; 641 642 case VNTSD_STATUS_VCC_IO_ERR: 643 if ((clientp->status & VNTSD_CLIENT_CONS_DELETED) == 0) { 644 /* check if console was deleted */ 645 status = vntsd_vcc_err(clientp->cons); 646 } 647 648 if (status != VNTSD_STATUS_CONTINUE) { 649 /* console was deleted */ 650 if (groupp->num_cons == 1) { 651 client_fini(groupp, clientp); 652 } 653 } 654 655 /* console is ok */ 656 client_init(clientp); 657 return; 658 659 case VNTSD_STATUS_MOV_CONS_FORWARD: 660 case VNTSD_STATUS_MOV_CONS_BACKWARD: 661 if (groupp->num_cons == 1) { 662 /* same console */ 663 return; 664 } 665 666 /* get selected console */ 667 (void) mutex_lock(&(clientp->cons->group->lock)); 668 clientp->cons = vntsd_que_pos(clientp->cons->group->conspq, 669 clientp->cons, 670 (status == VNTSD_STATUS_MOV_CONS_FORWARD)?(1):(-1)); 671 (void) mutex_unlock(&(clientp->cons->group->lock)); 672 return; 673 674 case VNTSD_SUCCESS: 675 case VNTSD_STATUS_CONTINUE: 676 case VNTSD_STATUS_NO_CONS: 677 client_init(clientp); 678 return; 679 680 case VNTSD_ERR_INVALID_INPUT: 681 return; 682 683 default: 684 /* fatal error */ 685 vntsd_log(status, err_msg); 686 client_fini(groupp, clientp); 687 return; 688 } 689 } 690 691 /* console thread */ 692 void * 693 vntsd_console_thread(vntsd_thr_arg_t *argp) 694 { 695 vntsd_group_t *groupp; 696 vntsd_cons_t *consp; 697 vntsd_client_t *clientp; 698 699 char buf[MAXHOSTNAMELEN]; 700 char prompt[72]; 701 char cmd; 702 int rv = VNTSD_SUCCESS; 703 int num_cons; 704 705 706 groupp = (vntsd_group_t *)argp->handle; 707 clientp = (vntsd_client_t *)argp->arg; 708 709 assert(groupp); 710 assert(clientp); 711 712 /* check if group is removed */ 713 714 D1(stderr, "t@%d get_client_sel@%lld:client@%d\n", thr_self(), 715 groupp->tcp_port, clientp->sockfd); 716 717 bzero(buf, MAXHOSTNAMELEN); 718 719 /* host name */ 720 if (gethostname(buf, MAXHOSTNAMELEN)) { 721 vntsd_log(VNTSD_STATUS_NO_HOST_NAME, "vntsd_console_thread()"); 722 (void) snprintf(buf, sizeof (buf), "unkown host"); 723 } 724 725 if (snprintf(prompt, sizeof (prompt), 726 "%s-vnts-%s: h, l, c{id}, n{name}, q:", 727 buf, groupp->group_name) >= sizeof (prompt)) { 728 /* long prompt doesn't fit, use short one */ 729 (void) snprintf(prompt, sizeof (prompt), 730 "vnts: h, l, c{id}, n{name}, q:"); 731 } 732 733 734 for (;;) { 735 cmd = ' '; 736 D1(stderr, "t@%d console_thread()@%lld:client@%d\n", thr_self(), 737 groupp->tcp_port, clientp->sockfd); 738 739 num_cons = vntsd_chk_group_total_cons(groupp); 740 741 if ((num_cons > 1) && (clientp->cons == NULL)) { 742 /* console to connect to */ 743 rv = read_cmd(clientp, prompt, &cmd); 744 /* check error and may exit */ 745 console_chk_status(groupp, clientp, rv); 746 } 747 748 switch (cmd) { 749 750 case 'l': 751 752 /* list domain names */ 753 rv = list_all_domains(groupp, clientp); 754 break; 755 756 757 case 'q': 758 759 rv = VNTSD_STATUS_CLIENT_QUIT; 760 break; 761 762 case ' ': 763 764 if (clientp->cons == NULL) { 765 if (num_cons == 1) { 766 /* by pass selecting console */ 767 consp = (vntsd_cons_t *) 768 (groupp->conspq->handle); 769 } else { 770 continue; 771 } 772 773 } else { 774 consp = clientp->cons; 775 } 776 777 /* connect to console */ 778 rv = connect_cons(consp, clientp); 779 780 break; 781 782 case 'c': 783 case 'n': 784 /* select console */ 785 if (clientp->cons == NULL) { 786 rv = select_cons(groupp, &consp, clientp, cmd); 787 if (rv == VNTSD_ERR_INVALID_INPUT) { 788 rv = display_help(clientp); 789 break; 790 } 791 } else { 792 consp = clientp->cons; 793 } 794 assert(consp); 795 796 /* connect to console */ 797 rv = connect_cons(consp, clientp); 798 D1(stderr, "t@%d console_thread()" 799 "connect_cons returns %d\n", 800 thr_self(), rv); 801 break; 802 803 case 'h': 804 default: 805 rv = display_help(clientp); 806 break; 807 808 } 809 810 /* check error and may exit */ 811 console_chk_status(groupp, clientp, rv); 812 } 813 814 /*NOTREACHED*/ 815 return (NULL); 816 } 817