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 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * VNTSD main 31 * 32 * VNTSD takes the following options: 33 * -i <device instance> 34 * VCC device instance to use, e.g. virtual-console-concentrator@0. 35 * Required option. 36 * -p <ip address> 37 * IP address VNTSD listens to. 38 * -d 39 * Do not daemonize. This is only available in a DEBUG build. 40 * -t timeout for inactivity 0 = indefinite 41 */ 42 43 #include <stdio.h> 44 #include <stdio_ext.h> 45 #include <sys/types.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <sys/socket.h> 50 #include <arpa/inet.h> 51 #include <time.h> 52 #include <netinet/in.h> 53 #include <thread.h> 54 #include <signal.h> 55 #include <fcntl.h> 56 #include <ctype.h> 57 #include <libintl.h> 58 #include <locale.h> 59 #include <syslog.h> 60 #include <sys/socket.h> 61 #include <netdb.h> 62 #include "vntsd.h" 63 #include "chars.h" 64 65 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 66 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't. */ 67 #endif 68 69 /* global variables */ 70 71 #ifdef DEBUG 72 int vntsddbg = 0x8; 73 #endif 74 75 #define MINUTE 60 76 77 #define VNTSD_INVALID_LISTEN_ADDR ((in_addr_t)-1) 78 79 #define LOCALHOST_IPv4 "127.0.0.1" 80 #define LOCALHOST_IPv6 "::1" 81 82 static vntsd_t *vntsdp; 83 84 85 static void vntsd_exit(void); 86 /* Signal handler for SIGINT, SIGKILL and SIGHUP */ 87 static void 88 exit_sig_handler(int sig) 89 { 90 91 D1(stderr, "t@%d exit_sig_handler%d \n", thr_self(), sig); 92 93 if (thr_self() != vntsdp->tid) { 94 /* not main thread, pass to main thread */ 95 (void) thr_kill(vntsdp->tid, sig); 96 } else { 97 exit(0); 98 } 99 } 100 101 /* 102 * Before a thread reads in client's input, it attaches to vntsd timer so that 103 * it can be waken up if a client does not access the connection for 104 * VNTSD_INPUT_TIMEOUT(10) minutes. 105 */ 106 107 /* attach a thread to timer */ 108 int 109 vntsd_attach_timer(vntsd_timeout_t *tmop) 110 { 111 int rv; 112 113 if (vntsdp->timeout == 0) { 114 return (VNTSD_SUCCESS); 115 } 116 117 (void) mutex_lock(&vntsdp->tmo_lock); 118 rv = vntsd_que_append(&vntsdp->tmoq, (void *)tmop); 119 (void) mutex_unlock(&vntsdp->tmo_lock); 120 return (rv); 121 } 122 123 /* detach a thread from timer */ 124 int 125 vntsd_detach_timer(vntsd_timeout_t *tmop) 126 { 127 int rv; 128 129 if (vntsdp->timeout == 0) { 130 return (VNTSD_SUCCESS); 131 } 132 133 (void) mutex_lock(&vntsdp->tmo_lock); 134 rv = vntsd_que_rm(&vntsdp->tmoq, (void *)tmop); 135 (void) mutex_unlock(&vntsdp->tmo_lock); 136 137 return (rv); 138 } 139 140 /* check threadd's timeout */ 141 static boolean_t 142 chk_timeout(vntsd_timeout_t *tmop) 143 { 144 tmop->minutes++; 145 146 if (tmop->minutes == vntsdp->timeout) { 147 /* wake up the thread */ 148 tmop->clientp->status |= VNTSD_CLIENT_TIMEOUT; 149 (void) thr_kill(tmop->tid, SIGALRM); 150 } 151 152 /* return false to walk the queue */ 153 return (B_FALSE); 154 } 155 156 /* reset timer */ 157 static boolean_t 158 reset_timeout(vntsd_timeout_t *tmop, thread_t tid) 159 { 160 if (tmop->tid == tid) { 161 tmop->minutes = 0; 162 } 163 /* return false to walk the queue */ 164 return (B_FALSE); 165 } 166 167 void 168 vntsd_reset_timer(thread_t tid) 169 { 170 if (vntsdp->timeout == 0) { 171 return; 172 } 173 174 (void) mutex_lock(&vntsdp->tmo_lock); 175 (void) vntsd_que_find(vntsdp->tmoq, (compare_func_t)reset_timeout, 176 (void*)tid); 177 (void) mutex_unlock(&vntsdp->tmo_lock); 178 } 179 180 /* 181 * When alarm goes off, wake up timeout threads. Alarm is set off every 182 * minutes. 183 */ 184 static void 185 vntsd_alarm_sig_handler(int sig) 186 { 187 static thread_t main_thread = 0; 188 189 D1(stderr, "t@%d alarm signal %d\n", thr_self(), sig); 190 if (vntsdp->timeout == 0) { 191 DERR(stderr, "t@%d alarm signal should not recv %d\n", 192 thr_self(), sig); 193 return; 194 } 195 196 197 if (main_thread == 0) { 198 /* initialize thread id */ 199 main_thread = thr_self(); 200 } else if (main_thread != thr_self()) { 201 /* get signal because thread is timeout */ 202 return; 203 } 204 205 /* in main thread */ 206 (void) mutex_lock(&vntsdp->tmo_lock); 207 208 /* wake up timeout threads */ 209 (void) vntsd_que_walk(vntsdp->tmoq, (el_func_t)chk_timeout); 210 (void) mutex_unlock(&vntsdp->tmo_lock); 211 212 /* reset alarm */ 213 (void) alarm(MINUTE); 214 } 215 216 /* got a SIGUSER1 siginal */ 217 static void 218 vntsd_sig_handler(int sig) 219 { 220 char err_msg[VNTSD_LINE_LEN]; 221 222 (void) snprintf(err_msg, sizeof (err_msg), "sig_handler() sig=%d", 223 sig); 224 225 if (sig != SIGUSR1) { 226 vntsd_log(VNTSD_STATUS_SIG, err_msg); 227 } 228 } 229 230 /* vntsd exits */ 231 static void 232 vntsd_exit(void) 233 { 234 D1(stderr, "t@%d vntsd_exit\n", thr_self()); 235 236 (void) mutex_lock(&vntsdp->lock); 237 238 if (vntsdp->timeout > 0) { 239 /* cancel the timer */ 240 (void) alarm(0); 241 } 242 /* delete all groups */ 243 vntsd_free_que(&vntsdp->grouppq, (clean_func_t)vntsd_clean_group); 244 245 /* close control port */ 246 (void) close(vntsdp->ctrl_fd); 247 248 assert(vntsdp->tmoq == NULL); 249 (void) mutex_unlock(&vntsdp->lock); 250 251 /* clean up vntsdp */ 252 (void) mutex_destroy(&vntsdp->tmo_lock); 253 (void) mutex_destroy(&vntsdp->lock); 254 free(vntsdp); 255 closelog(); 256 } 257 258 /* 259 * vntsd_help() 260 * print out valid command line options 261 */ 262 static void 263 vntsd_help(void) 264 { 265 (void) fprintf(stderr, gettext("Usage: vntsd -i <VCC device instance> " 266 "[-p <listen address>] [-t <timeout in minutes>]\n")); 267 } 268 269 /* 270 * get_listen_ip_addr() 271 * check for a valid control domain ip address in format of xxx.xxx.xxx.xxx. 272 * if ip address is valid and is assigned to this host, return ip address 273 * or else return VNTSD_INVALID_LISTEN_ADDR. 274 */ 275 static in_addr_t 276 get_listen_ip_addr(char *listen_addr) 277 { 278 char host_name[MAXPATHLEN]; 279 in_addr_t addr; 280 struct addrinfo hints; 281 struct addrinfo *res, *infop; 282 int err; 283 struct sockaddr_in *sa; 284 285 if (gethostname(host_name, MAXPATHLEN) != 0) { 286 syslog(LOG_ERR, "Can not get host name!"); 287 return (VNTSD_INVALID_LISTEN_ADDR); 288 } 289 290 if ((int)(addr = inet_addr(listen_addr)) == -1) 291 /* bad IP address format */ 292 return (VNTSD_INVALID_LISTEN_ADDR); 293 294 bzero(&hints, sizeof (hints)); 295 hints.ai_family = PF_INET; 296 hints.ai_socktype = SOCK_STREAM; 297 298 err = getaddrinfo(host_name, NULL, &hints, &res); 299 if (err != 0) { 300 syslog(LOG_ERR, "getaddrinfo failed: %s", gai_strerror(err)); 301 return (VNTSD_INVALID_LISTEN_ADDR); 302 } 303 304 infop = res; 305 while (infop != NULL) { 306 /* LINTED E_BAD_PTR_CAST_ALIGN */ 307 sa = (struct sockaddr_in *)infop->ai_addr; 308 if (sa->sin_addr.s_addr == addr) { 309 /* ip address found */ 310 freeaddrinfo(res); 311 return (addr); 312 } 313 infop = infop->ai_next; 314 } 315 316 /* ip address not found */ 317 freeaddrinfo(res); 318 return (VNTSD_INVALID_LISTEN_ADDR); 319 } 320 321 #ifdef DEBUG 322 #define DEBUG_OPTIONS "d" 323 #else 324 #define DEBUG_OPTIONS "" 325 #endif 326 327 int 328 main(int argc, char ** argv) 329 { 330 char *path; 331 struct pollfd poll_drv[1]; 332 struct sigaction act; 333 struct rlimit rlim; 334 char *listen_addr = NULL; 335 pid_t pid; 336 int i; 337 int option; 338 int sz; 339 int fd; 340 int n; 341 342 /* internationalization */ 343 (void) setlocale(LC_MESSAGES, ""); 344 (void) textdomain(TEXT_DOMAIN); 345 vntsd_init_esctable_msgs(); 346 347 /* initialization */ 348 bzero(&act, sizeof (act)); 349 350 /* 351 * ensure that we can obtain sufficient file descriptors for all 352 * the accept() calls when a machine contains many domains. 353 */ 354 (void) getrlimit(RLIMIT_NOFILE, &rlim); 355 if (rlim.rlim_cur < rlim.rlim_max) 356 rlim.rlim_cur = rlim.rlim_max; 357 if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) 358 vntsd_log(VNTSD_STATUS_CONTINUE, "Unable to increase file " 359 "descriptor limit."); 360 (void) enable_extended_FILE_stdio(-1, -1); 361 362 vntsdp = calloc(sizeof (vntsd_t), 1); 363 if (vntsdp == NULL) { 364 vntsd_log(VNTSD_ERR_NO_MEM, "main:vntsdp"); 365 exit(1); 366 } 367 368 vntsdp->ctrl_fd = -1; 369 vntsdp->devinst = NULL; 370 371 (void) mutex_init(&vntsdp->lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL); 372 (void) mutex_init(&vntsdp->tmo_lock, USYNC_THREAD|LOCK_ERRORCHECK, 373 NULL); 374 375 /* get CLI options */ 376 while ((option = getopt(argc, argv, "i:t:p:"DEBUG_OPTIONS)) != EOF) { 377 switch (option) { 378 #ifdef DEBUG 379 case 'd': 380 vntsdp->options |= VNTSD_OPT_DAEMON_OFF; 381 break; 382 #endif 383 case 'i': 384 vntsdp->devinst = optarg; 385 break; 386 case 'p': 387 listen_addr = optarg; 388 break; 389 390 case 't': 391 n = sscanf(optarg, "%d", &(vntsdp->timeout)); 392 if (n != 1) { 393 vntsdp->timeout = -1; 394 } 395 break; 396 397 default: 398 vntsd_help(); 399 exit(1); 400 } 401 } 402 403 if ((vntsdp->devinst == NULL) || (vntsdp->timeout == -1)) { 404 vntsd_help(); 405 exit(1); 406 } 407 408 if (listen_addr == NULL || strcmp(listen_addr, "localhost") == 0 || 409 strcmp(listen_addr, LOCALHOST_IPv4) == 0 || 410 strcmp(listen_addr, LOCALHOST_IPv6) == 0) { 411 /* by default listen on loopback interface */ 412 vntsdp->ip_addr.s_addr = htonl(INADDR_LOOPBACK); 413 } else if (strcmp(listen_addr, "any") == 0) { 414 vntsdp->ip_addr.s_addr = htonl(INADDR_ANY); 415 } else { 416 vntsdp->ip_addr.s_addr = get_listen_ip_addr(listen_addr); 417 if (vntsdp->ip_addr.s_addr == VNTSD_INVALID_LISTEN_ADDR) { 418 syslog(LOG_ERR, 419 "Invalid listen address '%s'\n", 420 listen_addr); 421 exit(2); 422 } 423 } 424 425 D3(stderr, "options = %llx, instance = %s, listen = %s\n", 426 vntsdp->options, vntsdp->devinst, 427 listen_addr ? listen_addr : "<null>"); 428 429 /* open VCC driver control port */ 430 sz = strlen(VCC_DEVICE_CTL_PATH) + strlen(vntsdp->devinst) + 1; 431 path = calloc(sz, 1); 432 if (path == NULL) { 433 vntsd_log(VNTSD_ERR_NO_MEM, "main(): alloc dev path"); 434 exit(1); 435 } 436 (void) snprintf(path, sz-1, VCC_DEVICE_CTL_PATH, vntsdp->devinst, 437 sizeof (vntsdp->devinst)); 438 vntsdp->ctrl_fd = open(path, O_RDWR); 439 440 if (vntsdp->ctrl_fd == -1) { 441 /* print error if device is not present */ 442 syslog(LOG_ERR, 443 "Error opening VCC device control port: %s", 444 path); 445 /* tell SMF no retry */ 446 exit(2); 447 } 448 449 free(path); 450 451 if ((vntsdp->options & VNTSD_OPT_DAEMON_OFF) == 0) { 452 /* daemonize it */ 453 pid = fork(); 454 if (pid < 0) { 455 perror("fork"); 456 exit(1); 457 } 458 if (pid > 0) { 459 /* parent */ 460 exit(0); 461 } 462 463 /* 464 * child process (daemon) 465 * 466 * Close all file descriptors other than 2 and the ctrl fd. 467 */ 468 (void) close(0); 469 (void) close(1); 470 for (i = 3; i < vntsdp->ctrl_fd; i++) { 471 (void) close(i); 472 } 473 closefrom(vntsdp->ctrl_fd + 1); 474 475 /* obtain a new process group */ 476 (void) setsid(); 477 fd = open("/dev/null", O_RDWR); 478 if (fd < 0) { 479 syslog(LOG_ERR, "Can not open /dev/null"); 480 exit(1); 481 } 482 /* handle standard I/O */ 483 if (dup2(fd, 0) < 0) { 484 syslog(LOG_ERR, "Failed dup2()"); 485 exit(1); 486 } 487 488 if (dup2(fd, 1) < 0) { 489 syslog(LOG_ERR, "Failed dup2()"); 490 exit(1); 491 } 492 493 /* ignore terminal signals */ 494 (void) signal(SIGTSTP, SIG_IGN); 495 (void) signal(SIGTTOU, SIG_IGN); 496 (void) signal(SIGTTIN, SIG_IGN); 497 } 498 499 500 /* set up signal handlers */ 501 502 /* exit signals */ 503 act.sa_handler = exit_sig_handler; 504 505 (void) sigemptyset(&act.sa_mask); 506 (void) sigaction(SIGINT, &act, NULL); 507 (void) sigaction(SIGTERM, &act, NULL); 508 (void) sigaction(SIGHUP, &act, NULL); 509 510 /* vntsd internal signals */ 511 act.sa_handler = vntsd_sig_handler; 512 (void) sigemptyset(&act.sa_mask); 513 (void) sigaction(SIGUSR1, &act, NULL); 514 515 516 act.sa_handler = vntsd_alarm_sig_handler; 517 (void) sigemptyset(&act.sa_mask); 518 (void) sigaction(SIGALRM, &act, NULL); 519 520 521 /* setup exit */ 522 (void) atexit(vntsd_exit); 523 524 525 526 /* initialization */ 527 openlog("vntsd", LOG_CONS, LOG_DAEMON); 528 529 530 /* set alarm */ 531 if (vntsdp->timeout > 0) { 532 (void) alarm(MINUTE); 533 } 534 535 vntsdp->tid = thr_self(); 536 537 /* get exiting consoles from vcc */ 538 vntsd_get_config(vntsdp); 539 540 for (; ; ) { 541 /* poll vcc for configuration change */ 542 bzero(poll_drv, sizeof (poll_drv)); 543 544 poll_drv[0].fd = vntsdp->ctrl_fd; 545 poll_drv[0].events = POLLIN; 546 547 if (poll(poll_drv, 1, -1) == -1) { 548 if (errno == EINTR) { 549 /* wake up because a consle was deleted */ 550 vntsd_delete_cons(vntsdp); 551 continue; 552 } 553 vntsd_log(VNTSD_ERR_VCC_POLL, 554 "vcc control poll err! aborting.."); 555 exit(1); 556 } 557 558 D1(stderr, "t@%d driver event %x\n", thr_self(), 559 poll_drv[0].revents); 560 561 vntsd_daemon_wakeup(vntsdp); 562 /* 563 * Main thread may miss a console-delete signal when it is 564 * not polling vcc. check if any console is deleted. 565 */ 566 vntsd_delete_cons(vntsdp); 567 568 } 569 570 /*NOTREACHED*/ 571 return (0); 572 } 573 574 /* export ip_addr */ 575 struct in_addr 576 vntsd_ip_addr(void) 577 { 578 return (vntsdp->ip_addr); 579 } 580 581 /* 582 * ioctl to vcc control port 583 * Supported ioctls interface are: 584 * ioctl code parameters return data 585 * VCC_NUM_CONSOLE none uint_t no consoles 586 * VCC_CONS_TBL none array of vcc_cons_t 587 * VCC_INQUIRY none vcc_response_t response 588 * VCC_CONS_INFO uint_t portno vcc_cons_t 589 * VCC_CONS_STATUS uint_t portno 590 * VCC_FORCE_CLOSE uint_t portno 591 */ 592 int 593 vntsd_vcc_ioctl(int ioctl_code, uint_t portno, void *buf) 594 { 595 D1(stderr, "t@%d vcc_ioctl@%d code=%x\n", thr_self(), portno, 596 ioctl_code); 597 598 if ((ioctl_code == (VCC_CONS_INFO)) || 599 (ioctl_code == (VCC_FORCE_CLOSE))) { 600 /* construct vcc in buf */ 601 *((uint_t *)buf) = portno; 602 } 603 604 if (ioctl(vntsdp->ctrl_fd, ioctl_code, (caddr_t)buf)) { 605 /* ioctl request error */ 606 return (VNTSD_STATUS_VCC_IO_ERR); 607 } 608 609 return (VNTSD_SUCCESS); 610 } 611 612 /* 613 * check if a vcc i/o error is caused by removal of a console. If so 614 * wake up main thread to cleanup the console. 615 */ 616 int 617 vntsd_vcc_err(vntsd_cons_t *consp) 618 { 619 vntsd_group_t *groupp; 620 621 assert(consp); 622 groupp = consp->group; 623 assert(groupp); 624 625 if (consp->status & VNTSD_CONS_DELETED) { 626 /* console was deleted */ 627 return (VNTSD_STATUS_VCC_IO_ERR); 628 } 629 630 if (vntsd_vcc_cons_alive(consp)) { 631 /* console is ok */ 632 return (VNTSD_STATUS_CONTINUE); 633 } 634 635 /* console needs to be deleted */ 636 (void) mutex_lock(&consp->lock); 637 consp->status |= VNTSD_CONS_DELETED; 638 639 /* 640 * main thread will close all clients after receiving console 641 * delete signal. 642 */ 643 (void) mutex_unlock(&consp->lock); 644 645 /* mark the group */ 646 (void) mutex_lock(&groupp->lock); 647 groupp->status |= VNTSD_GROUP_CLEAN_CONS; 648 (void) mutex_unlock(&groupp->lock); 649 650 /* signal main thread to deleted console */ 651 (void) thr_kill(vntsdp->tid, SIGUSR1); 652 653 return (VNTSD_STATUS_VCC_IO_ERR); 654 } 655