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