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