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