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