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