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