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 /* 591 * group is waiting to be deleted. - signal the group's 592 * listen thread - the VNTSD_GROUP_SIG_WAIT state will 593 * be cleared when the listen thread exits. 594 */ 595 (void) cond_signal(&groupp->cvp); 596 } 597 (void) mutex_unlock(&groupp->lock); 598 599 (void) mutex_destroy(&clientp->lock); 600 free(clientp); 601 602 thr_exit(0); 603 } 604 605 /* check client's status. exit if client quits or fatal errors */ 606 static void 607 console_chk_status(vntsd_group_t *groupp, vntsd_client_t *clientp, int status) 608 { 609 char err_msg[VNTSD_LINE_LEN]; 610 611 D1(stderr, "t@%d console_chk_status() status=%d " 612 "client status=%x num consoles=%d \n", 613 thr_self(), status, clientp->status, groupp->num_cons); 614 615 (void) snprintf(err_msg, VNTSD_LINE_LEN, "console_chk_status client%d" 616 " num_cos=%d", clientp->sockfd, groupp->num_cons); 617 618 /* 619 * obtain group lock to protect groupp->num_cons. 620 * When groupp->num_cons == 0, close client and exit the tread. 621 */ 622 (void) mutex_lock(&groupp->lock); 623 624 if (groupp->num_cons == 0) { 625 /* no more console in the group */ 626 (void) mutex_unlock(&groupp->lock); 627 client_fini(groupp, clientp); 628 return; 629 } 630 631 if (status == VNTSD_STATUS_INTR) { 632 /* reason for signal? */ 633 status = vntsd_cons_chk_intr(clientp); 634 } 635 636 switch (status) { 637 638 case VNTSD_STATUS_CLIENT_QUIT: 639 (void) mutex_unlock(&groupp->lock); 640 client_fini(groupp, clientp); 641 return; 642 643 case VNTSD_STATUS_RESELECT_CONS: 644 645 if (clientp->cons == NULL) { 646 /* 647 * domain was deleted before client connects to it 648 * connect to other console in the same group 649 */ 650 (void) mutex_unlock(&groupp->lock); 651 client_init(clientp); 652 return; 653 } 654 655 if ((groupp->num_cons == 1) && 656 ((clientp->status & VNTSD_CLIENT_CONS_DELETED) || 657 (groupp->conspq->handle == clientp->cons))) { 658 /* no other selection available */ 659 (void) mutex_unlock(&groupp->lock); 660 client_fini(groupp, clientp); 661 } else { 662 (void) mutex_unlock(&groupp->lock); 663 client_init(clientp); 664 } 665 666 return; 667 668 case VNTSD_STATUS_VCC_IO_ERR: 669 if ((clientp->status & VNTSD_CLIENT_CONS_DELETED) == 0) { 670 /* check if console was deleted */ 671 (void) mutex_unlock(&groupp->lock); 672 status = vntsd_vcc_err(clientp->cons); 673 (void) mutex_lock(&groupp->lock); 674 } 675 676 if (status != VNTSD_STATUS_CONTINUE) { 677 /* console was deleted */ 678 if (groupp->num_cons <= 1) { 679 (void) mutex_unlock(&groupp->lock); 680 client_fini(groupp, clientp); 681 return; 682 } 683 } 684 685 (void) mutex_unlock(&groupp->lock); 686 /* console is ok */ 687 client_init(clientp); 688 return; 689 690 case VNTSD_STATUS_MOV_CONS_FORWARD: 691 case VNTSD_STATUS_MOV_CONS_BACKWARD: 692 if (groupp->num_cons == 1) { 693 /* same console */ 694 (void) mutex_unlock(&groupp->lock); 695 return; 696 } 697 698 /* get selected console */ 699 clientp->cons = vntsd_que_pos(groupp->conspq, 700 clientp->cons, 701 (status == VNTSD_STATUS_MOV_CONS_FORWARD)?(1):(-1)); 702 (void) mutex_unlock(&groupp->lock); 703 return; 704 705 case VNTSD_SUCCESS: 706 case VNTSD_STATUS_CONTINUE: 707 case VNTSD_STATUS_NO_CONS: 708 (void) mutex_unlock(&groupp->lock); 709 client_init(clientp); 710 return; 711 712 case VNTSD_ERR_INVALID_INPUT: 713 (void) mutex_unlock(&groupp->lock); 714 return; 715 716 default: 717 /* fatal error */ 718 (void) mutex_unlock(&groupp->lock); 719 vntsd_log(status, err_msg); 720 client_fini(groupp, clientp); 721 return; 722 } 723 } 724 725 /* console thread */ 726 void * 727 vntsd_console_thread(vntsd_thr_arg_t *argp) 728 { 729 vntsd_group_t *groupp; 730 vntsd_cons_t *consp; 731 vntsd_client_t *clientp; 732 733 char buf[MAXHOSTNAMELEN]; 734 char prompt[72]; 735 char cmd; 736 int rv = VNTSD_SUCCESS; 737 int num_cons; 738 739 740 groupp = (vntsd_group_t *)argp->handle; 741 clientp = (vntsd_client_t *)argp->arg; 742 743 assert(groupp); 744 assert(clientp); 745 746 /* check if group is removed */ 747 748 D1(stderr, "t@%d get_client_sel@%lld:client@%d\n", thr_self(), 749 groupp->tcp_port, clientp->sockfd); 750 751 bzero(buf, MAXHOSTNAMELEN); 752 753 /* host name */ 754 if (gethostname(buf, MAXHOSTNAMELEN)) { 755 vntsd_log(VNTSD_STATUS_NO_HOST_NAME, "vntsd_console_thread()"); 756 (void) snprintf(buf, sizeof (buf), "unkown host"); 757 } 758 759 if (snprintf(prompt, sizeof (prompt), 760 "%s-vnts-%s: h, l, c{id}, n{name}, q:", 761 buf, groupp->group_name) >= sizeof (prompt)) { 762 /* long prompt doesn't fit, use short one */ 763 (void) snprintf(prompt, sizeof (prompt), 764 "vnts: h, l, c{id}, n{name}, q:"); 765 } 766 767 768 for (;;) { 769 cmd = ' '; 770 D1(stderr, "t@%d console_thread()@%lld:client@%d\n", thr_self(), 771 groupp->tcp_port, clientp->sockfd); 772 773 num_cons = vntsd_chk_group_total_cons(groupp); 774 775 if ((num_cons > 1) && (clientp->cons == NULL)) { 776 /* console to connect to */ 777 rv = read_cmd(clientp, prompt, &cmd); 778 /* check error and may exit */ 779 console_chk_status(groupp, clientp, rv); 780 781 /* any console is removed from group? */ 782 num_cons = vntsd_chk_group_total_cons(groupp); 783 if (num_cons <= 1) { 784 cmd = ' '; 785 } 786 } 787 788 switch (cmd) { 789 790 case 'l': 791 792 /* list domain names */ 793 rv = list_all_domains(groupp, clientp); 794 break; 795 796 797 case 'q': 798 799 rv = VNTSD_STATUS_CLIENT_QUIT; 800 break; 801 802 case ' ': 803 804 if (num_cons == 0) 805 /* no console in the group */ 806 break; 807 808 if (clientp->cons == NULL) { 809 if (num_cons == 1) { 810 /* by pass selecting console */ 811 consp = (vntsd_cons_t *) 812 (groupp->conspq->handle); 813 } else { 814 continue; 815 } 816 817 } else { 818 consp = clientp->cons; 819 } 820 821 /* connect to console */ 822 rv = connect_cons(consp, clientp); 823 824 break; 825 826 case 'c': 827 case 'n': 828 /* select console */ 829 if (clientp->cons == NULL) { 830 rv = select_cons(groupp, &consp, clientp, cmd); 831 if (rv == VNTSD_ERR_INVALID_INPUT) { 832 rv = display_help(clientp); 833 break; 834 } 835 } else { 836 consp = clientp->cons; 837 } 838 assert(consp); 839 840 /* connect to console */ 841 rv = connect_cons(consp, clientp); 842 D1(stderr, "t@%d console_thread()" 843 "connect_cons returns %d\n", 844 thr_self(), rv); 845 break; 846 847 case 'h': 848 default: 849 rv = display_help(clientp); 850 break; 851 852 } 853 854 /* check error and may exit */ 855 console_chk_status(groupp, clientp, rv); 856 } 857 858 /*NOTREACHED*/ 859 return (NULL); 860 } 861