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