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