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 * Configuration and setup interface to vcc driver. 28 * At intialization time, vntsd opens vcc ctrl port and read initial 29 * configuratioa. It manages console groups, creates the listen thread, 30 * dynamically adds and removes virtual console within a group. 31 */ 32 33 34 #include <syslog.h> 35 #include <stdio.h> 36 #include <sys/types.h> 37 #include <sys/ipc.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 #include <sys/socket.h> 42 #include <sys/ipc.h> 43 #include <sys/shm.h> 44 #include <sys/sem.h> 45 #include <wait.h> 46 #include <time.h> 47 #include <synch.h> 48 #include <netinet/in.h> 49 #include <thread.h> 50 #include <signal.h> 51 #include "vntsd.h" 52 53 /* signal all clients that console has been deleted */ 54 boolean_t 55 vntsd_notify_client_cons_del(vntsd_client_t *clientp) 56 { 57 (void) mutex_lock(&clientp->lock); 58 clientp->status |= VNTSD_CLIENT_CONS_DELETED; 59 (void) thr_kill(clientp->cons_tid, SIGUSR1); 60 (void) mutex_unlock(&clientp->lock); 61 return (B_FALSE); 62 } 63 64 /* free console structure */ 65 static void 66 free_cons(vntsd_cons_t *consp) 67 { 68 assert(consp); 69 (void) mutex_destroy(&consp->lock); 70 (void) cond_destroy(&consp->cvp); 71 if (consp->vcc_fd != -1) 72 (void) close(consp->vcc_fd); 73 free(consp); 74 } 75 76 /* free group structure */ 77 static void 78 free_group(vntsd_group_t *groupp) 79 { 80 assert(groupp); 81 (void) mutex_destroy(&groupp->lock); 82 (void) cond_destroy(&groupp->cvp); 83 if (groupp->sockfd != -1) 84 (void) close(groupp->sockfd); 85 free(groupp); 86 } 87 88 /* 89 * all clients connected to a console must disconnect before 90 * removing a console. 91 */ 92 static void 93 cleanup_cons(vntsd_cons_t *consp) 94 { 95 vntsd_group_t *groupp; 96 timestruc_t to; 97 98 assert(consp); 99 D1(stderr, "t@%d vntsd_disconn_clients@%d\n", thr_self(), 100 consp->cons_no); 101 102 groupp = consp->group; 103 assert(groupp); 104 105 106 (void) mutex_lock(&consp->lock); 107 108 /* wait for all clients disconnect from the console */ 109 while (consp->clientpq != NULL) { 110 consp->status |= VNTSD_CONS_SIG_WAIT; 111 112 /* signal client to disconnect the console */ 113 (void) vntsd_que_walk(consp->clientpq, 114 (el_func_t)vntsd_notify_client_cons_del); 115 116 (void) thr_kill(consp->wr_tid, SIGUSR1); 117 to.tv_sec = VNTSD_CV_WAIT_DELTIME; 118 to.tv_nsec = 0; 119 120 /* wait for clients to disconnect */ 121 (void) cond_reltimedwait(&consp->cvp, &consp->lock, &to); 122 } 123 124 /* reduce console count in the group */ 125 (void) mutex_lock(&groupp->lock); 126 assert(groupp->num_cons > 0); 127 groupp->num_cons--; 128 (void) mutex_unlock(&groupp->lock); 129 130 (void) mutex_unlock(&consp->lock); 131 132 free_cons(consp); 133 } 134 135 /* search for a group whose console is being deleted */ 136 static boolean_t 137 find_clean_cons_group(vntsd_group_t *groupp) 138 { 139 if (groupp->status & VNTSD_GROUP_CLEAN_CONS) { 140 return (B_TRUE); 141 } else { 142 return (B_FALSE); 143 } 144 } 145 146 /* search for a console that is being deleted */ 147 static boolean_t 148 find_clean_cons(vntsd_cons_t *consp) 149 { 150 if (consp->status & VNTSD_CONS_DELETED) { 151 return (B_TRUE); 152 } else { 153 return (B_FALSE); 154 } 155 } 156 157 /* delete a console */ 158 void 159 vntsd_delete_cons(vntsd_t *vntsdp) 160 { 161 vntsd_group_t *groupp; 162 vntsd_cons_t *consp; 163 164 for (; ; ) { 165 /* get the group contains deleted console */ 166 (void) mutex_lock(&vntsdp->lock); 167 groupp = vntsd_que_walk(vntsdp->grouppq, 168 (el_func_t)find_clean_cons_group); 169 if (groupp == NULL) { 170 /* no more group has console deleted */ 171 (void) mutex_unlock(&vntsdp->lock); 172 return; 173 } 174 (void) mutex_lock(&groupp->lock); 175 groupp->status &= ~VNTSD_GROUP_CLEAN_CONS; 176 (void) mutex_unlock(&groupp->lock); 177 (void) mutex_unlock(&vntsdp->lock); 178 179 for (; ; ) { 180 /* get the console to be deleted */ 181 (void) mutex_lock(&groupp->lock); 182 183 /* clean up any deleted console in the group */ 184 if (groupp->conspq != NULL) { 185 consp = vntsd_que_walk(groupp->conspq, 186 (el_func_t)find_clean_cons); 187 if (consp == NULL) { 188 /* no more cons to delete */ 189 (void) mutex_unlock(&groupp->lock); 190 break; 191 } 192 193 /* remove console from the group */ 194 (void) vntsd_que_rm(&groupp->conspq, consp); 195 (void) mutex_unlock(&groupp->lock); 196 197 /* clean up the console */ 198 cleanup_cons(consp); 199 } 200 201 /* delete group? */ 202 if (groupp->conspq == NULL) { 203 /* no more console in the group delete group */ 204 assert(groupp->vntsd); 205 206 (void) mutex_lock(&groupp->vntsd->lock); 207 (void) vntsd_que_rm(&groupp->vntsd->grouppq, 208 groupp); 209 (void) mutex_unlock(&groupp->vntsd->lock); 210 211 /* clean up the group */ 212 vntsd_clean_group(groupp); 213 break; 214 } 215 } 216 } 217 } 218 219 /* clean up a group */ 220 void 221 vntsd_clean_group(vntsd_group_t *groupp) 222 { 223 224 timestruc_t to; 225 226 D1(stderr, "t@%d clean_group() group=%s tcp=%lld\n", thr_self(), 227 groupp->group_name, groupp->tcp_port); 228 229 (void) mutex_lock(&groupp->lock); 230 231 /* prevent from reentry */ 232 if (groupp->status & VNTSD_GROUP_IN_CLEANUP) { 233 (void) mutex_unlock(&groupp->lock); 234 return; 235 } 236 groupp->status |= VNTSD_GROUP_IN_CLEANUP; 237 238 /* mark group waiting for listen thread to exits */ 239 groupp->status |= VNTSD_GROUP_SIG_WAIT; 240 (void) mutex_unlock(&groupp->lock); 241 242 vntsd_free_que(&groupp->conspq, (clean_func_t)cleanup_cons); 243 244 (void) mutex_lock(&groupp->lock); 245 /* walk through no cons client queue */ 246 while (groupp->no_cons_clientpq != NULL) { 247 (void) vntsd_que_walk(groupp->no_cons_clientpq, 248 (el_func_t)vntsd_notify_client_cons_del); 249 to.tv_sec = VNTSD_CV_WAIT_DELTIME; 250 to.tv_nsec = 0; 251 (void) cond_reltimedwait(&groupp->cvp, &groupp->lock, &to); 252 } 253 254 /* waiting for listen thread to exit */ 255 while (groupp->status & VNTSD_GROUP_SIG_WAIT) { 256 /* signal listen thread to exit */ 257 (void) thr_kill(groupp->listen_tid, SIGUSR1); 258 to.tv_sec = VNTSD_CV_WAIT_DELTIME; 259 to.tv_nsec = 0; 260 /* wait listen thread to exit */ 261 (void) cond_reltimedwait(&groupp->cvp, &groupp->lock, &to); 262 } 263 264 (void) mutex_unlock(&groupp->lock); 265 (void) thr_join(groupp->listen_tid, NULL, NULL); 266 /* free group */ 267 free_group(groupp); 268 } 269 270 /* allocate and initialize console structure */ 271 static vntsd_cons_t * 272 alloc_cons(vntsd_group_t *groupp, vcc_console_t *consolep) 273 { 274 vntsd_cons_t *consp; 275 int rv; 276 277 /* allocate console */ 278 consp = (vntsd_cons_t *)malloc(sizeof (vntsd_cons_t)); 279 if (consp == NULL) { 280 vntsd_log(VNTSD_ERR_NO_MEM, "alloc_cons"); 281 return (NULL); 282 } 283 284 /* intialize console */ 285 bzero(consp, sizeof (vntsd_cons_t)); 286 287 (void) mutex_init(&consp->lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL); 288 (void) cond_init(&consp->cvp, USYNC_THREAD, NULL); 289 290 consp->cons_no = consolep->cons_no; 291 (void) strlcpy(consp->domain_name, consolep->domain_name, MAXPATHLEN); 292 (void) strlcpy(consp->dev_name, consolep->dev_name, MAXPATHLEN); 293 consp->wr_tid = (thread_t)-1; 294 consp->vcc_fd = -1; 295 296 /* join the group */ 297 (void) mutex_lock(&groupp->lock); 298 299 if ((rv = vntsd_que_append(&groupp->conspq, consp)) != 300 VNTSD_SUCCESS) { 301 (void) mutex_unlock(&groupp->lock); 302 vntsd_log(rv, "alloc_cons"); 303 free_cons(consp); 304 return (NULL); 305 } 306 groupp->num_cons++; 307 consp->group = groupp; 308 309 (void) mutex_unlock(&groupp->lock); 310 311 D1(stderr, "t@%d alloc_cons@%d %s %s\n", thr_self(), 312 consp->cons_no, consp->domain_name, consp->dev_name); 313 314 return (consp); 315 } 316 317 /* compare tcp with group->tcp */ 318 static boolean_t 319 grp_by_tcp(vntsd_group_t *groupp, uint64_t *tcp_port) 320 { 321 assert(groupp); 322 assert(tcp_port); 323 return (groupp->tcp_port == *tcp_port); 324 } 325 326 /* allocate and initialize group */ 327 static vntsd_group_t * 328 alloc_group(vntsd_t *vntsdp, char *group_name, uint64_t tcp_port) 329 { 330 vntsd_group_t *groupp; 331 332 /* allocate group */ 333 groupp = (vntsd_group_t *)malloc(sizeof (vntsd_group_t)); 334 if (groupp == NULL) { 335 vntsd_log(VNTSD_ERR_NO_MEM, "alloc_group"); 336 return (NULL); 337 } 338 339 /* initialize group */ 340 bzero(groupp, sizeof (vntsd_group_t)); 341 342 (void) mutex_init(&groupp->lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL); 343 (void) cond_init(&groupp->cvp, USYNC_THREAD, NULL); 344 345 if (group_name != NULL) { 346 (void) memcpy(groupp->group_name, group_name, MAXPATHLEN); 347 } 348 349 groupp->tcp_port = tcp_port; 350 groupp->listen_tid = (thread_t)-1; 351 groupp->sockfd = -1; 352 groupp->vntsd = vntsdp; 353 354 D1(stderr, "t@%d alloc_group@%lld:%s\n", thr_self(), groupp->tcp_port, 355 groupp->group_name); 356 357 return (groupp); 358 } 359 360 /* mark a deleted console */ 361 boolean_t 362 vntsd_mark_deleted_cons(vntsd_cons_t *consp) 363 { 364 (void) mutex_lock(&consp->lock); 365 consp->status |= VNTSD_CONS_DELETED; 366 (void) mutex_unlock(&consp->lock); 367 return (B_FALSE); 368 } 369 370 /* 371 * Initialize a console, if console is associated with with a 372 * new group, intialize the group. 373 */ 374 static int 375 alloc_cons_with_group(vntsd_t *vntsdp, vcc_console_t *consp, 376 vntsd_group_t **new_groupp) 377 { 378 vntsd_group_t *groupp = NULL; 379 int rv; 380 381 *new_groupp = NULL; 382 383 /* match group by tcp port */ 384 385 386 (void) mutex_lock(&vntsdp->lock); 387 groupp = vntsd_que_find(vntsdp->grouppq, 388 (compare_func_t)grp_by_tcp, (void *)&(consp->tcp_port)); 389 if (groupp != NULL) 390 (void) mutex_lock(&groupp->lock); 391 392 (void) mutex_unlock(&vntsdp->lock); 393 394 if (groupp != NULL) { 395 /* 396 * group with same tcp port found. 397 * if there is no console in the group, the 398 * group should be removed and the tcp port can 399 * be used for tne new group. 400 * This is possible, when there is tight loop of 401 * creating/deleting domains. When a vcc port is 402 * removed, a read thread will have an I/O error because 403 * vcc has closed the port. The read thread then marks 404 * the console is removed and notify main thread to 405 * remove the console. 406 * Meanwhile, the same port and its group (with same 407 * tcp port and group name) is created. Vcc notify 408 * vntsd that new console is added. 409 * Main thread now have two events. If main thread polls 410 * out vcc notification first, it will find that there is 411 * a group has no console. 412 */ 413 414 if (vntsd_chk_group_total_cons(groupp) == 0) { 415 416 /* all consoles in the group have been removed */ 417 (void) vntsd_que_walk(groupp->conspq, 418 (el_func_t)vntsd_mark_deleted_cons); 419 groupp->status |= VNTSD_GROUP_CLEAN_CONS; 420 (void) mutex_unlock(&groupp->lock); 421 groupp = NULL; 422 423 } else if (strcmp(groupp->group_name, consp->group_name)) { 424 /* conflict group name */ 425 vntsd_log(VNTSD_ERR_VCC_GRP_NAME, 426 "group name is different from existing group"); 427 (void) mutex_unlock(&groupp->lock); 428 return (VNTSD_ERR_VCC_CTRL_DATA); 429 430 } else { 431 /* group already existed */ 432 (void) mutex_unlock(&groupp->lock); 433 } 434 435 } 436 437 if (groupp == NULL) { 438 /* new group */ 439 groupp = alloc_group(vntsdp, consp->group_name, 440 consp->tcp_port); 441 if (groupp == NULL) { 442 return (VNTSD_ERR_NO_MEM); 443 } 444 445 assert(groupp->conspq == NULL); 446 /* queue group to vntsdp */ 447 (void) mutex_lock(&vntsdp->lock); 448 rv = vntsd_que_append(&vntsdp->grouppq, groupp); 449 (void) mutex_unlock(&vntsdp->lock); 450 451 if (rv != VNTSD_SUCCESS) { 452 return (rv); 453 } 454 455 *new_groupp = groupp; 456 } 457 458 /* intialize console */ 459 if (alloc_cons(groupp, consp) == NULL) { 460 /* no memory */ 461 if (*new_groupp != NULL) { 462 /* clean up new group */ 463 free_group(groupp); 464 } 465 466 return (VNTSD_ERR_NO_MEM); 467 } 468 469 return (VNTSD_SUCCESS); 470 471 } 472 473 474 /* create listen thread */ 475 static boolean_t 476 create_listen_thread(vntsd_group_t *groupp) 477 { 478 479 char err_msg[VNTSD_LINE_LEN]; 480 int rv; 481 482 assert(groupp); 483 484 (void) mutex_lock(&groupp->lock); 485 assert(groupp->num_cons); 486 487 D1(stderr, "t@%d create_listen:%lld\n", thr_self(), groupp->tcp_port); 488 489 if ((rv = thr_create(NULL, 0, (thr_func_t)vntsd_listen_thread, 490 (void *)groupp, THR_DETACHED, &groupp->listen_tid)) != 0) { 491 (void) (void) snprintf(err_msg, sizeof (err_msg), 492 "Can not create listen thread for" 493 "group %s tcp %llx\n", groupp->group_name, 494 groupp->tcp_port); 495 vntsd_log(VNTSD_ERR_CREATE_LISTEN_THR, err_msg); 496 497 /* clean up group queue */ 498 vntsd_free_que(&groupp->conspq, (clean_func_t)free_cons); 499 groupp->listen_tid = (thread_t)-1; 500 } 501 502 (void) mutex_unlock(&groupp->lock); 503 504 return (rv != 0); 505 } 506 507 /* find deleted console by console no */ 508 static boolean_t 509 deleted_cons_by_consno(vntsd_cons_t *consp, int *cons_no) 510 { 511 vntsd_client_t *clientp; 512 513 assert(consp); 514 515 if (consp->cons_no != *cons_no) 516 return (B_FALSE); 517 518 /* has console marked as deleted? */ 519 if ((consp->status & VNTSD_CONS_DELETED) == 0) 520 return (B_TRUE); 521 522 if (consp->clientpq == NULL) 523 /* there is no client for this console */ 524 return (B_TRUE); 525 526 /* need to notify clients of console ? */ 527 clientp = (vntsd_client_t *)consp->clientpq->handle; 528 529 if (clientp->status & VNTSD_CLIENT_CONS_DELETED) 530 /* clients of console have notified */ 531 return (B_FALSE); 532 533 return (B_TRUE); 534 } 535 536 /* find group structure from console no */ 537 static boolean_t 538 find_cons_group_by_cons_no(vntsd_group_t *groupp, uint_t *cons_no) 539 { 540 vntsd_cons_t *consp; 541 542 consp = vntsd_que_find(groupp->conspq, 543 (compare_func_t)deleted_cons_by_consno, cons_no); 544 return (consp != NULL); 545 546 } 547 548 /* delete a console if the console exists in the vntsd */ 549 static void 550 delete_cons_before_add(vntsd_t *vntsdp, uint_t cons_no) 551 { 552 vntsd_group_t *groupp; 553 vntsd_cons_t *consp; 554 555 /* group exists? */ 556 (void) mutex_lock(&vntsdp->lock); 557 groupp = vntsd_que_find(vntsdp->grouppq, 558 (compare_func_t)find_cons_group_by_cons_no, 559 &cons_no); 560 (void) mutex_unlock(&vntsdp->lock); 561 562 if (groupp == NULL) { 563 /* no such group */ 564 return; 565 } 566 567 /* group exists, if console exists? */ 568 (void) mutex_lock(&groupp->lock); 569 consp = vntsd_que_find(groupp->conspq, 570 (compare_func_t)deleted_cons_by_consno, &cons_no); 571 572 if (consp == NULL) { 573 /* no such console */ 574 (void) mutex_unlock(&groupp->lock); 575 return; 576 } 577 578 /* console exists - mark console for main thread to delete it */ 579 (void) mutex_lock(&consp->lock); 580 581 if (consp->status & VNTSD_CONS_DELETED) { 582 /* already marked */ 583 (void) mutex_unlock(&consp->lock); 584 (void) mutex_unlock(&groupp->lock); 585 return; 586 } 587 588 consp->status |= VNTSD_CONS_DELETED; 589 groupp->status |= VNTSD_GROUP_CLEAN_CONS; 590 591 (void) mutex_unlock(&consp->lock); 592 (void) mutex_unlock(&groupp->lock); 593 594 } 595 596 /* add a console */ 597 static void 598 do_add_cons(vntsd_t *vntsdp, int cons_no) 599 { 600 vcc_console_t console; 601 vntsd_group_t *groupp; 602 int rv; 603 char err_msg[VNTSD_LINE_LEN]; 604 605 606 (void) snprintf(err_msg, sizeof (err_msg), 607 "do_add_cons():Can not add console=%d", cons_no); 608 609 /* get console configuration from vcc */ 610 611 if ((rv = vntsd_vcc_ioctl(VCC_CONS_INFO, cons_no, (void *)&console)) 612 != VNTSD_SUCCESS) { 613 vntsd_log(rv, err_msg); 614 return; 615 } 616 617 /* clean up the console if console was deleted and added again */ 618 delete_cons_before_add(vntsdp, console.cons_no); 619 620 /* initialize console */ 621 622 if ((rv = alloc_cons_with_group(vntsdp, &console, &groupp)) != 623 VNTSD_SUCCESS) { 624 /* no memory to add this new console */ 625 vntsd_log(rv, err_msg); 626 return; 627 } 628 629 if (groupp != NULL) { 630 /* new group */ 631 /* create listen thread for this console */ 632 if (create_listen_thread(groupp)) { 633 vntsd_log(VNTSD_ERR_CREATE_LISTEN_THR, err_msg); 634 free_group(groupp); 635 } 636 637 } 638 } 639 640 /* daemon wake up */ 641 void 642 vntsd_daemon_wakeup(vntsd_t *vntsdp) 643 { 644 645 vcc_response_t inq_data; 646 647 /* reason to wake up */ 648 if (vntsd_vcc_ioctl(VCC_INQUIRY, 0, (void *)&inq_data) != 649 VNTSD_SUCCESS) { 650 vntsd_log(VNTSD_ERR_VCC_IOCTL, "vntsd_daemon_wakeup()"); 651 return; 652 } 653 654 D1(stderr, "t@%d vntsd_daemon_wakup:msg %d port %x\n", thr_self(), 655 inq_data.reason, inq_data.cons_no); 656 657 switch (inq_data.reason) { 658 659 case VCC_CONS_ADDED: 660 do_add_cons(vntsdp, inq_data.cons_no); 661 break; 662 663 case VCC_CONS_MISS_ADDED: 664 /* an added port was deleted before vntsd can process it */ 665 return; 666 667 default: 668 DERR(stderr, "t@%d daemon_wakeup:ioctl_unknown %d\n", 669 thr_self(), inq_data.reason); 670 vntsd_log(VNTSD_ERR_UNKNOWN_CMD, "from vcc\n"); 671 break; 672 } 673 } 674 675 /* initial console configuration */ 676 void 677 vntsd_get_config(vntsd_t *vntsdp) 678 { 679 680 int i; 681 int num_cons; 682 vcc_console_t *consp; 683 vntsd_group_t *groupp; 684 685 /* num of consoles */ 686 num_cons = 0; 687 688 if (vntsd_vcc_ioctl(VCC_NUM_CONSOLE, 0, (void *)&num_cons) != 689 VNTSD_SUCCESS) { 690 vntsd_log(VNTSD_ERR_VCC_IOCTL, "VCC_NUM_CONSOLE failed\n"); 691 return; 692 } 693 694 D3(stderr, "get_config:num_cons=%d", num_cons); 695 696 if (num_cons == 0) { 697 return; 698 } 699 700 /* allocate memory for all consoles */ 701 consp = malloc(num_cons*sizeof (vcc_console_t)); 702 703 if (consp == NULL) { 704 vntsd_log(VNTSD_ERR_NO_MEM, "for console table."); 705 return; 706 } 707 708 /* get console table */ 709 if (vntsd_vcc_ioctl(VCC_CONS_TBL, 0, (void *)consp) != VNTSD_SUCCESS) { 710 vntsd_log(VNTSD_ERR_VCC_IOCTL, " VCC_CONS_TBL " 711 "for console table\n"); 712 return; 713 } 714 715 /* intialize groups and consoles */ 716 for (i = 0; i < num_cons; i++) { 717 if (alloc_cons_with_group(vntsdp, &consp[i], &groupp) 718 != VNTSD_SUCCESS) { 719 vntsd_log(VNTSD_ERR_ADD_CONS_FAILED, "get_config"); 720 } 721 } 722 723 /* create listen thread for each group */ 724 (void) mutex_lock(&vntsdp->lock); 725 726 for (; ; ) { 727 groupp = vntsd_que_walk(vntsdp->grouppq, 728 (el_func_t)create_listen_thread); 729 if (groupp == NULL) { 730 break; 731 } 732 vntsd_log(VNTSD_ERR_CREATE_LISTEN_THR, "get config()"); 733 } 734 735 (void) mutex_unlock(&vntsdp->lock); 736 } 737