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[c ]]{id} -- connect to console of domain {id}"); 211 212 if ((rv = vntsd_write_line(clientp, bufp)) != VNTSD_SUCCESS) { 213 return (rv); 214 } 215 216 return (VNTSD_SUCCESS); 217 } 218 219 /* select a console to connect */ 220 static int 221 select_cons(vntsd_group_t *groupp, int num_cons, vntsd_cons_t **consp, 222 vntsd_client_t *clientp, char c) 223 { 224 int cons_no = -2; 225 int n; 226 int i; 227 char buf[VNTSD_LINE_LEN]; 228 int rv; 229 230 231 232 (void) mutex_lock(&groupp->lock); 233 if (groupp->num_cons == 0) { 234 (void) mutex_unlock(&groupp->lock); 235 /* no console in this group */ 236 return (VNTSD_STATUS_NO_CONS); 237 } 238 (void) mutex_unlock(&groupp->lock); 239 240 if (num_cons == 1) { 241 /* by pass selecting console */ 242 *consp = (vntsd_cons_t *)(groupp->conspq->handle); 243 return (VNTSD_SUCCESS); 244 } 245 246 247 if (isdigit(c)) { 248 /* {id} input */ 249 cons_no = c - '0'; 250 } else if (c == 'c') { 251 /* c{id} or c {id} input */ 252 cons_no = -1; 253 } else if (!isspace(c)) { 254 return (VNTSD_ERR_INVALID_INPUT); 255 } 256 257 /* get client selections */ 258 n = VNTSD_LINE_LEN; 259 260 if ((rv = vntsd_read_line(clientp, buf, &n)) != VNTSD_SUCCESS) { 261 return (rv); 262 } 263 264 /* parse command */ 265 for (i = 0; i < n; i++) { 266 if (cons_no == -1) { 267 /* c{id} */ 268 cons_no = atoi(buf + i); 269 break; 270 } 271 272 if (isspace(buf[i]) && cons_no == -2) { 273 /* skip space */ 274 continue; 275 } 276 277 if (buf[i] == 'c') { 278 /* c{id} or c {id} */ 279 cons_no = -1; 280 } else if (buf[i] == CR) { 281 break; 282 } else { 283 return (VNTSD_ERR_INVALID_INPUT); 284 } 285 } 286 287 if (cons_no < 0) { 288 return (VNTSD_ERR_INVALID_INPUT); 289 } 290 291 /* get selected console */ 292 (void) mutex_lock(&groupp->lock); 293 294 *consp = (vntsd_cons_t *)vntsd_que_find(groupp->conspq, 295 (compare_func_t)vntsd_cons_by_consno, &cons_no); 296 297 if (*consp == NULL) { 298 /* during console selection, the console has been deleted */ 299 (void) mutex_unlock(&groupp->lock); 300 301 return (VNTSD_ERR_INVALID_INPUT); 302 } 303 if ((*consp)->status & VNTSD_CONS_DELETED) { 304 return (VNTSD_ERR_INVALID_INPUT); 305 } 306 307 (void) mutex_unlock(&groupp->lock); 308 309 return (VNTSD_SUCCESS); 310 } 311 312 /* compare if there is a match console in the gorup */ 313 static boolean_t 314 find_cons_in_group(vntsd_cons_t *consp_in_group, vntsd_cons_t *consp) 315 { 316 if (consp_in_group == consp) { 317 return (B_TRUE); 318 } else { 319 return (B_FALSE); 320 } 321 } 322 323 /* connect a client to a console */ 324 static int 325 connect_cons(vntsd_cons_t *consp, vntsd_client_t *clientp) 326 { 327 int rv, rv1; 328 vntsd_group_t *groupp; 329 330 assert(consp); 331 groupp = consp->group; 332 assert(groupp); 333 assert(clientp); 334 335 (void) mutex_lock(&groupp->lock); 336 337 /* check if console is valid */ 338 consp = vntsd_que_find(groupp->conspq, 339 (compare_func_t)find_cons_in_group, consp); 340 341 if (consp == NULL) { 342 (void) mutex_unlock(&groupp->lock); 343 return (VNTSD_STATUS_NO_CONS); 344 } 345 if (consp->status & VNTSD_CONS_DELETED) { 346 (void) mutex_unlock(&groupp->lock); 347 return (VNTSD_STATUS_NO_CONS); 348 } 349 350 (void) mutex_lock(&consp->lock); 351 (void) mutex_lock(&clientp->lock); 352 353 354 clientp->cons = consp; 355 356 /* enable daemon cmd */ 357 clientp->status &= ~VNTSD_CLIENT_DISABLE_DAEMON_CMD; 358 359 if (consp->clientpq == NULL) { 360 /* first connect to console - a writer */ 361 assert(consp->vcc_fd == -1); 362 /* open vcc */ 363 consp->vcc_fd = vntsd_open_vcc(consp->dev_name, consp->cons_no); 364 if (consp->vcc_fd < 0) { 365 (void) mutex_unlock(&clientp->lock); 366 (void) mutex_unlock(&consp->lock); 367 (void) mutex_unlock(&groupp->lock); 368 assert(consp->group); 369 return (vntsd_vcc_err(consp)); 370 } 371 } 372 373 (void) mutex_unlock(&clientp->lock); 374 375 /* 376 * move the client from group's no console selected queue 377 * to cons queue 378 */ 379 380 rv = vntsd_que_rm(&groupp->no_cons_clientpq, clientp); 381 assert(rv == VNTSD_SUCCESS); 382 383 rv = vntsd_que_append(&consp->clientpq, clientp); 384 (void) mutex_unlock(&groupp->lock); 385 386 if (rv != VNTSD_SUCCESS) { 387 if (consp->clientpq->handle == clientp) { 388 /* writer */ 389 (void) close(consp->vcc_fd); 390 consp->vcc_fd = -1; 391 } 392 393 (void) mutex_unlock(&consp->lock); 394 return (rv); 395 } 396 397 (void) mutex_unlock(&consp->lock); 398 399 if (consp->clientpq->handle == clientp) { 400 /* create a write thread */ 401 rv = create_write_thread(consp); 402 if (rv != VNTSD_SUCCESS) { 403 return (rv); 404 } 405 } 406 407 /* write connecting message */ 408 if ((rv = write_connect_msg(clientp, consp->group->group_name, 409 consp->domain_name)) != VNTSD_SUCCESS) { 410 return (rv); 411 } 412 413 /* process input from client */ 414 rv = vntsd_read(clientp); 415 416 /* client disconnected from the console */ 417 (void) mutex_lock(&groupp->lock); 418 419 /* remove client from console queue */ 420 (void) mutex_lock(&consp->lock); 421 rv1 = vntsd_que_rm(&consp->clientpq, clientp); 422 assert(rv1 == VNTSD_SUCCESS); 423 424 /* append client to group's no console selected queue */ 425 rv1 = vntsd_que_append(&groupp->no_cons_clientpq, clientp); 426 (void) mutex_unlock(&groupp->lock); 427 428 if (consp->clientpq == NULL) { 429 /* clean up console since there is no client connected to it */ 430 assert(consp->vcc_fd != -1); 431 432 /* close vcc port */ 433 (void) close(consp->vcc_fd); 434 consp->vcc_fd = -1; 435 436 /* force write thread to exit */ 437 assert(consp->wr_tid != (thread_t)-1); 438 (void) thr_kill(consp->wr_tid, SIGUSR1); 439 (void) mutex_unlock(&consp->lock); 440 (void) thr_join(consp->wr_tid, NULL, NULL); 441 (void) mutex_lock(&consp->lock); 442 } 443 444 if (consp->status & VNTSD_CONS_SIG_WAIT) { 445 /* console is waiting for client to disconnect */ 446 (void) cond_signal(&consp->cvp); 447 } 448 449 (void) mutex_unlock(&consp->lock); 450 451 return (rv1 == VNTSD_SUCCESS ? rv : rv1); 452 453 } 454 455 /* read command line input */ 456 static int 457 read_cmd(vntsd_client_t *clientp, char *prompt, char *cmd) 458 { 459 int rv; 460 461 /* disable daemon special command */ 462 (void) mutex_lock(&clientp->lock); 463 clientp->status |= VNTSD_CLIENT_DISABLE_DAEMON_CMD; 464 (void) mutex_unlock(&clientp->lock); 465 466 if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) 467 != VNTSD_SUCCESS) { 468 return (rv); 469 } 470 471 if ((rv = vntsd_write_client(clientp, prompt, strlen(prompt))) 472 != VNTSD_SUCCESS) { 473 return (rv); 474 } 475 476 if ((rv = vntsd_read_data(clientp, cmd)) != VNTSD_SUCCESS) { 477 return (rv); 478 } 479 if (*cmd == BS) { 480 return (VNTSD_SUCCESS); 481 } 482 483 rv = vntsd_write_client(clientp, cmd, 1); 484 485 *cmd = tolower(*cmd); 486 487 return (rv); 488 } 489 490 /* reset client for selecting a console in the group */ 491 static void 492 client_init(vntsd_client_t *clientp) 493 { 494 (void) mutex_lock(&clientp->lock); 495 clientp->cons = NULL; 496 clientp->status = 0; 497 (void) mutex_unlock(&clientp->lock); 498 } 499 500 /* clean up client and exit the thread */ 501 static void 502 client_fini(vntsd_group_t *groupp, vntsd_client_t *clientp) 503 { 504 505 assert(groupp); 506 assert(clientp); 507 508 /* disconnct client from tcp port */ 509 assert(clientp->sockfd != -1); 510 (void) close(clientp->sockfd); 511 512 (void) mutex_lock(&groupp->lock); 513 (void) vntsd_que_rm(&groupp->no_cons_clientpq, clientp); 514 515 if ((groupp->no_cons_clientpq == NULL) && 516 (groupp->status & VNTSD_GROUP_SIG_WAIT)) { 517 /* group is waiting to be deleted */ 518 groupp->status &= ~VNTSD_GROUP_SIG_WAIT; 519 (void) cond_signal(&groupp->cvp); 520 } 521 (void) mutex_unlock(&groupp->lock); 522 523 (void) mutex_destroy(&clientp->lock); 524 free(clientp); 525 526 thr_exit(0); 527 } 528 529 /* check client's status. exit if client quits or fatal errors */ 530 static void 531 console_chk_status(vntsd_group_t *groupp, vntsd_client_t *clientp, int status) 532 { 533 char err_msg[VNTSD_LINE_LEN]; 534 535 D1(stderr, "t@%d console_chk_status() status=%d " 536 "client status=%x num consoles=%d \n", 537 thr_self(), status, clientp->status, groupp->num_cons); 538 539 (void) snprintf(err_msg, VNTSD_LINE_LEN, "console_chk_status client%d" 540 " num_cos=%d", clientp->sockfd, groupp->num_cons); 541 542 if (groupp->num_cons == 0) { 543 /* no more console in the group */ 544 client_fini(groupp, clientp); 545 } 546 547 if (status == VNTSD_STATUS_INTR) { 548 /* reason for signal? */ 549 status = vntsd_cons_chk_intr(clientp); 550 } 551 552 switch (status) { 553 554 case VNTSD_STATUS_CLIENT_QUIT: 555 client_fini(groupp, clientp); 556 return; 557 558 case VNTSD_STATUS_RESELECT_CONS: 559 assert(clientp->cons); 560 if ((groupp->num_cons == 1) && 561 (groupp->conspq->handle == clientp->cons)) { 562 /* no other selection available */ 563 client_fini(groupp, clientp); 564 } else { 565 client_init(clientp); 566 } 567 return; 568 569 case VNTSD_STATUS_VCC_IO_ERR: 570 if ((clientp->status & VNTSD_CLIENT_CONS_DELETED) == 0) { 571 /* check if console was deleted */ 572 status = vntsd_vcc_err(clientp->cons); 573 } 574 575 if (status != VNTSD_STATUS_CONTINUE) { 576 /* console was deleted */ 577 if (groupp->num_cons == 1) { 578 client_fini(groupp, clientp); 579 } 580 } 581 582 /* console is ok */ 583 client_init(clientp); 584 return; 585 586 case VNTSD_STATUS_MOV_CONS_FORWARD: 587 case VNTSD_STATUS_MOV_CONS_BACKWARD: 588 if (groupp->num_cons == 1) { 589 /* same console */ 590 return; 591 } 592 593 /* get selected console */ 594 (void) mutex_lock(&(clientp->cons->group->lock)); 595 clientp->cons = vntsd_que_pos(clientp->cons->group->conspq, 596 clientp->cons, 597 (status == VNTSD_STATUS_MOV_CONS_FORWARD)?(1):(-1)); 598 (void) mutex_unlock(&(clientp->cons->group->lock)); 599 return; 600 601 case VNTSD_SUCCESS: 602 case VNTSD_STATUS_CONTINUE: 603 case VNTSD_STATUS_NO_CONS: 604 client_init(clientp); 605 return; 606 607 case VNTSD_ERR_INVALID_INPUT: 608 return; 609 610 default: 611 /* fatal error */ 612 vntsd_log(status, err_msg); 613 client_fini(groupp, clientp); 614 return; 615 } 616 } 617 618 /* console thread */ 619 void * 620 vntsd_console_thread(vntsd_thr_arg_t *argp) 621 { 622 vntsd_group_t *groupp; 623 vntsd_cons_t *consp; 624 vntsd_client_t *clientp; 625 626 char buf[MAXHOSTNAMELEN]; 627 char prompt[72]; 628 char cmd; 629 int rv = VNTSD_SUCCESS; 630 int num_cons; 631 632 633 groupp = (vntsd_group_t *)argp->handle; 634 clientp = (vntsd_client_t *)argp->arg; 635 636 assert(groupp); 637 assert(clientp); 638 639 /* check if group is removed */ 640 641 D1(stderr, "t@%d get_client_sel@%lld:client@%d\n", thr_self(), 642 groupp->tcp_port, clientp->sockfd); 643 644 bzero(buf, MAXHOSTNAMELEN); 645 646 /* host name */ 647 if (gethostname(buf, MAXHOSTNAMELEN)) { 648 vntsd_log(VNTSD_STATUS_NO_HOST_NAME, "vntsd_console_thread()"); 649 (void) snprintf(buf, sizeof (buf), "unkown host"); 650 } 651 652 if (snprintf(prompt, sizeof (prompt), 653 "%s-vnts-%s: h,l,{id},c{id},c {id},q:", 654 buf, groupp->group_name) >= sizeof (prompt)) { 655 /* long prompt doesn't fit, use short one */ 656 (void) snprintf(prompt, sizeof (prompt), 657 "vnts: h,l,{id},c{id},c {id}, q:"); 658 } 659 660 661 for (;;) { 662 cmd = ' '; 663 D1(stderr, "t@%d console_thread()@%lld:client@%d\n", thr_self(), 664 groupp->tcp_port, clientp->sockfd); 665 666 num_cons = vntsd_chk_group_total_cons(groupp); 667 668 if ((num_cons > 1) && (clientp->cons == NULL)) { 669 /* console to connect to */ 670 rv = read_cmd(clientp, prompt, &cmd); 671 /* check error and may exit */ 672 console_chk_status(groupp, clientp, rv); 673 } 674 675 switch (cmd) { 676 677 case 'l': 678 679 /* list domain names */ 680 rv = list_all_domains(groupp, clientp); 681 break; 682 683 684 case 'q': 685 686 rv = VNTSD_STATUS_CLIENT_QUIT; 687 break; 688 689 case 'h': 690 rv = display_help(clientp); 691 break; 692 693 default: 694 /* select console */ 695 if (clientp->cons == NULL) { 696 rv = select_cons(groupp, num_cons, 697 &consp, clientp, cmd); 698 if (rv == VNTSD_ERR_INVALID_INPUT) { 699 rv = display_help(clientp); 700 break; 701 } 702 } else { 703 consp = clientp->cons; 704 } 705 assert(consp); 706 707 /* connect to console */ 708 rv = connect_cons(consp, clientp); 709 D1(stderr, "t@%d console_thread()" 710 "connect_cons returns %d\n", 711 thr_self(), rv); 712 break; 713 714 } 715 /* check error and may exit */ 716 console_chk_status(groupp, clientp, rv); 717 } 718 719 /*NOTREACHED*/ 720 return (NULL); 721 } 722