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