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) { 386 /* first connect to console - a writer */ 387 assert(consp->vcc_fd == -1); 388 /* open vcc */ 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 /* close vcc port */ 459 (void) close(consp->vcc_fd); 460 consp->vcc_fd = -1; 461 462 /* force write thread to exit */ 463 assert(consp->wr_tid != (thread_t)-1); 464 (void) thr_kill(consp->wr_tid, SIGUSR1); 465 (void) mutex_unlock(&consp->lock); 466 (void) thr_join(consp->wr_tid, NULL, NULL); 467 (void) mutex_lock(&consp->lock); 468 } 469 470 if (consp->status & VNTSD_CONS_SIG_WAIT) { 471 /* console is waiting for client to disconnect */ 472 (void) cond_signal(&consp->cvp); 473 } 474 475 (void) mutex_unlock(&consp->lock); 476 477 return (rv1 == VNTSD_SUCCESS ? rv : rv1); 478 479 } 480 481 /* read command line input */ 482 static int 483 read_cmd(vntsd_client_t *clientp, char *prompt, char *cmd) 484 { 485 int rv; 486 487 /* disable daemon special command */ 488 (void) mutex_lock(&clientp->lock); 489 clientp->status |= VNTSD_CLIENT_DISABLE_DAEMON_CMD; 490 (void) mutex_unlock(&clientp->lock); 491 492 if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) 493 != VNTSD_SUCCESS) { 494 return (rv); 495 } 496 497 if ((rv = vntsd_write_client(clientp, prompt, strlen(prompt))) 498 != VNTSD_SUCCESS) { 499 return (rv); 500 } 501 502 if ((rv = vntsd_read_data(clientp, cmd)) != VNTSD_SUCCESS) { 503 return (rv); 504 } 505 if (*cmd == BS) { 506 return (VNTSD_SUCCESS); 507 } 508 509 rv = vntsd_write_client(clientp, cmd, 1); 510 511 *cmd = tolower(*cmd); 512 513 return (rv); 514 } 515 516 /* reset client for selecting a console in the group */ 517 static void 518 client_init(vntsd_client_t *clientp) 519 { 520 (void) mutex_lock(&clientp->lock); 521 clientp->cons = NULL; 522 clientp->status = 0; 523 (void) mutex_unlock(&clientp->lock); 524 } 525 526 /* clean up client and exit the thread */ 527 static void 528 client_fini(vntsd_group_t *groupp, vntsd_client_t *clientp) 529 { 530 531 assert(groupp); 532 assert(clientp); 533 534 /* disconnct client from tcp port */ 535 assert(clientp->sockfd != -1); 536 (void) close(clientp->sockfd); 537 538 (void) mutex_lock(&groupp->lock); 539 (void) vntsd_que_rm(&groupp->no_cons_clientpq, clientp); 540 541 if ((groupp->no_cons_clientpq == NULL) && 542 (groupp->status & VNTSD_GROUP_SIG_WAIT)) { 543 /* group is waiting to be deleted */ 544 groupp->status &= ~VNTSD_GROUP_SIG_WAIT; 545 (void) cond_signal(&groupp->cvp); 546 } 547 (void) mutex_unlock(&groupp->lock); 548 549 (void) mutex_destroy(&clientp->lock); 550 free(clientp); 551 552 thr_exit(0); 553 } 554 555 /* check client's status. exit if client quits or fatal errors */ 556 static void 557 console_chk_status(vntsd_group_t *groupp, vntsd_client_t *clientp, int status) 558 { 559 char err_msg[VNTSD_LINE_LEN]; 560 561 D1(stderr, "t@%d console_chk_status() status=%d " 562 "client status=%x num consoles=%d \n", 563 thr_self(), status, clientp->status, groupp->num_cons); 564 565 (void) snprintf(err_msg, VNTSD_LINE_LEN, "console_chk_status client%d" 566 " num_cos=%d", clientp->sockfd, groupp->num_cons); 567 568 if (groupp->num_cons == 0) { 569 /* no more console in the group */ 570 client_fini(groupp, clientp); 571 } 572 573 if (status == VNTSD_STATUS_INTR) { 574 /* reason for signal? */ 575 status = vntsd_cons_chk_intr(clientp); 576 } 577 578 switch (status) { 579 580 case VNTSD_STATUS_CLIENT_QUIT: 581 client_fini(groupp, clientp); 582 return; 583 584 case VNTSD_STATUS_RESELECT_CONS: 585 assert(clientp->cons); 586 if ((groupp->num_cons == 1) && 587 (groupp->conspq->handle == clientp->cons)) { 588 /* no other selection available */ 589 client_fini(groupp, clientp); 590 } else { 591 client_init(clientp); 592 } 593 return; 594 595 case VNTSD_STATUS_VCC_IO_ERR: 596 if ((clientp->status & VNTSD_CLIENT_CONS_DELETED) == 0) { 597 /* check if console was deleted */ 598 status = vntsd_vcc_err(clientp->cons); 599 } 600 601 if (status != VNTSD_STATUS_CONTINUE) { 602 /* console was deleted */ 603 if (groupp->num_cons == 1) { 604 client_fini(groupp, clientp); 605 } 606 } 607 608 /* console is ok */ 609 client_init(clientp); 610 return; 611 612 case VNTSD_STATUS_MOV_CONS_FORWARD: 613 case VNTSD_STATUS_MOV_CONS_BACKWARD: 614 if (groupp->num_cons == 1) { 615 /* same console */ 616 return; 617 } 618 619 /* get selected console */ 620 (void) mutex_lock(&(clientp->cons->group->lock)); 621 clientp->cons = vntsd_que_pos(clientp->cons->group->conspq, 622 clientp->cons, 623 (status == VNTSD_STATUS_MOV_CONS_FORWARD)?(1):(-1)); 624 (void) mutex_unlock(&(clientp->cons->group->lock)); 625 return; 626 627 case VNTSD_SUCCESS: 628 case VNTSD_STATUS_CONTINUE: 629 case VNTSD_STATUS_NO_CONS: 630 client_init(clientp); 631 return; 632 633 case VNTSD_ERR_INVALID_INPUT: 634 return; 635 636 default: 637 /* fatal error */ 638 vntsd_log(status, err_msg); 639 client_fini(groupp, clientp); 640 return; 641 } 642 } 643 644 /* console thread */ 645 void * 646 vntsd_console_thread(vntsd_thr_arg_t *argp) 647 { 648 vntsd_group_t *groupp; 649 vntsd_cons_t *consp; 650 vntsd_client_t *clientp; 651 652 char buf[MAXHOSTNAMELEN]; 653 char prompt[72]; 654 char cmd; 655 int rv = VNTSD_SUCCESS; 656 int num_cons; 657 658 659 groupp = (vntsd_group_t *)argp->handle; 660 clientp = (vntsd_client_t *)argp->arg; 661 662 assert(groupp); 663 assert(clientp); 664 665 /* check if group is removed */ 666 667 D1(stderr, "t@%d get_client_sel@%lld:client@%d\n", thr_self(), 668 groupp->tcp_port, clientp->sockfd); 669 670 bzero(buf, MAXHOSTNAMELEN); 671 672 /* host name */ 673 if (gethostname(buf, MAXHOSTNAMELEN)) { 674 vntsd_log(VNTSD_STATUS_NO_HOST_NAME, "vntsd_console_thread()"); 675 (void) snprintf(buf, sizeof (buf), "unkown host"); 676 } 677 678 if (snprintf(prompt, sizeof (prompt), 679 "%s-vnts-%s: h, l, c{id}, n{name}, q:", 680 buf, groupp->group_name) >= sizeof (prompt)) { 681 /* long prompt doesn't fit, use short one */ 682 (void) snprintf(prompt, sizeof (prompt), 683 "vnts: h, l, c{id}, n{name}, q:"); 684 } 685 686 687 for (;;) { 688 cmd = ' '; 689 D1(stderr, "t@%d console_thread()@%lld:client@%d\n", thr_self(), 690 groupp->tcp_port, clientp->sockfd); 691 692 num_cons = vntsd_chk_group_total_cons(groupp); 693 694 if ((num_cons > 1) && (clientp->cons == NULL)) { 695 /* console to connect to */ 696 rv = read_cmd(clientp, prompt, &cmd); 697 /* check error and may exit */ 698 console_chk_status(groupp, clientp, rv); 699 } 700 701 switch (cmd) { 702 703 case 'l': 704 705 /* list domain names */ 706 rv = list_all_domains(groupp, clientp); 707 break; 708 709 710 case 'q': 711 712 rv = VNTSD_STATUS_CLIENT_QUIT; 713 break; 714 715 case ' ': 716 717 if (clientp->cons == NULL) { 718 if (num_cons == 1) { 719 /* by pass selecting console */ 720 consp = (vntsd_cons_t *) 721 (groupp->conspq->handle); 722 } else { 723 continue; 724 } 725 726 } else { 727 consp = clientp->cons; 728 } 729 730 /* connect to console */ 731 rv = connect_cons(consp, clientp); 732 733 break; 734 735 case 'c': 736 case 'n': 737 /* select console */ 738 if (clientp->cons == NULL) { 739 rv = select_cons(groupp, &consp, clientp, cmd); 740 if (rv == VNTSD_ERR_INVALID_INPUT) { 741 rv = display_help(clientp); 742 break; 743 } 744 } else { 745 consp = clientp->cons; 746 } 747 assert(consp); 748 749 /* connect to console */ 750 rv = connect_cons(consp, clientp); 751 D1(stderr, "t@%d console_thread()" 752 "connect_cons returns %d\n", 753 thr_self(), rv); 754 break; 755 756 case 'h': 757 default: 758 rv = display_help(clientp); 759 break; 760 761 } 762 763 /* check error and may exit */ 764 console_chk_status(groupp, clientp, rv); 765 } 766 767 /*NOTREACHED*/ 768 return (NULL); 769 } 770