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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
30 * VNTSD takes the following options:
31 * -i <device instance>
32 * VCC device instance to use, e.g. virtual-console-concentrator@0.
35 * IP address VNTSD listens to.
37 * Do not daemonize. This is only available in a DEBUG build.
38 * -t timeout for inactivity 0 = indefinite
39 * -A enable Authorization checking. Mutually exclusive with -p.
43 #include <stdio_ext.h>
44 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <arpa/inet.h>
51 #include <netinet/in.h>
59 #include <sys/socket.h>
64 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
65 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't. */
68 /* global variables */
76 #define VNTSD_INVALID_LISTEN_ADDR ((in_addr_t)-1)
78 #define LOCALHOST_IPv4 "127.0.0.1"
79 #define LOCALHOST_IPv6 "::1"
81 static vntsd_t
*vntsdp
;
84 static void vntsd_exit(void);
85 /* Signal handler for SIGINT, SIGKILL and SIGHUP */
87 exit_sig_handler(int sig
)
90 D1(stderr
, "t@%d exit_sig_handler%d \n", thr_self(), sig
);
92 if (thr_self() != vntsdp
->tid
) {
93 /* not main thread, pass to main thread */
94 (void) thr_kill(vntsdp
->tid
, sig
);
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.
106 /* attach a thread to timer */
108 vntsd_attach_timer(vntsd_timeout_t
*tmop
)
112 if (vntsdp
->timeout
== 0) {
113 return (VNTSD_SUCCESS
);
116 (void) mutex_lock(&vntsdp
->tmo_lock
);
117 rv
= vntsd_que_append(&vntsdp
->tmoq
, (void *)tmop
);
118 (void) mutex_unlock(&vntsdp
->tmo_lock
);
122 /* detach a thread from timer */
124 vntsd_detach_timer(vntsd_timeout_t
*tmop
)
128 if (vntsdp
->timeout
== 0) {
129 return (VNTSD_SUCCESS
);
132 (void) mutex_lock(&vntsdp
->tmo_lock
);
133 rv
= vntsd_que_rm(&vntsdp
->tmoq
, (void *)tmop
);
134 (void) mutex_unlock(&vntsdp
->tmo_lock
);
139 /* check threadd's timeout */
141 chk_timeout(vntsd_timeout_t
*tmop
)
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
);
151 /* return false to walk the queue */
157 reset_timeout(vntsd_timeout_t
*tmop
, thread_t tid
)
159 if (tmop
->tid
== tid
) {
162 /* return false to walk the queue */
167 vntsd_reset_timer(thread_t tid
)
169 if (vntsdp
->timeout
== 0) {
173 (void) mutex_lock(&vntsdp
->tmo_lock
);
174 (void) vntsd_que_find(vntsdp
->tmoq
, (compare_func_t
)reset_timeout
,
176 (void) mutex_unlock(&vntsdp
->tmo_lock
);
180 * When alarm goes off, wake up timeout threads. Alarm is set off every
184 vntsd_alarm_sig_handler(int sig
)
186 static thread_t main_thread
= 0;
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",
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 */
205 (void) mutex_lock(&vntsdp
->tmo_lock
);
207 /* wake up timeout threads */
208 (void) vntsd_que_walk(vntsdp
->tmoq
, (el_func_t
)chk_timeout
);
209 (void) mutex_unlock(&vntsdp
->tmo_lock
);
212 (void) alarm(MINUTE
);
215 /* got a SIGUSER1 siginal */
217 vntsd_sig_handler(int sig
)
219 char err_msg
[VNTSD_LINE_LEN
];
221 (void) snprintf(err_msg
, sizeof (err_msg
), "sig_handler() sig=%d",
224 if (sig
!= SIGUSR1
) {
225 vntsd_log(VNTSD_STATUS_SIG
, err_msg
);
233 D1(stderr
, "t@%d vntsd_exit\n", thr_self());
235 (void) mutex_lock(&vntsdp
->lock
);
237 if (vntsdp
->timeout
> 0) {
238 /* cancel the timer */
241 /* delete all groups */
242 vntsd_free_que(&vntsdp
->grouppq
, (clean_func_t
)vntsd_clean_group
);
244 /* close control port */
245 (void) close(vntsdp
->ctrl_fd
);
247 assert(vntsdp
->tmoq
== NULL
);
248 (void) mutex_unlock(&vntsdp
->lock
);
250 /* clean up vntsdp */
251 (void) mutex_destroy(&vntsdp
->tmo_lock
);
252 (void) mutex_destroy(&vntsdp
->lock
);
259 * print out valid command line options
264 (void) fprintf(stderr
, gettext("Usage: vntsd -i <VCC device instance> "
265 "[-p <listen address>] [-t <timeout in minutes>] [-A]\n"));
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.
275 get_listen_ip_addr(char *listen_addr
)
277 char host_name
[MAXPATHLEN
];
279 struct addrinfo hints
;
280 struct addrinfo
*res
, *infop
;
282 struct sockaddr_in
*sa
;
284 if (gethostname(host_name
, MAXPATHLEN
) != 0) {
285 syslog(LOG_ERR
, "Can not get host name!");
286 return (VNTSD_INVALID_LISTEN_ADDR
);
289 if ((int)(addr
= inet_addr(listen_addr
)) == -1)
290 /* bad IP address format */
291 return (VNTSD_INVALID_LISTEN_ADDR
);
293 bzero(&hints
, sizeof (hints
));
294 hints
.ai_family
= PF_INET
;
295 hints
.ai_socktype
= SOCK_STREAM
;
297 err
= getaddrinfo(host_name
, NULL
, &hints
, &res
);
299 syslog(LOG_ERR
, "getaddrinfo failed: %s", gai_strerror(err
));
300 return (VNTSD_INVALID_LISTEN_ADDR
);
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 */
312 infop
= infop
->ai_next
;
315 /* ip address not found */
317 return (VNTSD_INVALID_LISTEN_ADDR
);
321 #define DEBUG_OPTIONS "d"
323 #define DEBUG_OPTIONS ""
327 main(int argc
, char ** argv
)
330 struct pollfd poll_drv
[1];
331 struct sigaction act
;
333 char *listen_addr
= NULL
;
341 /* internationalization */
342 (void) setlocale(LC_MESSAGES
, "");
343 (void) textdomain(TEXT_DOMAIN
);
344 vntsd_init_esctable_msgs();
347 bzero(&act
, sizeof (act
));
350 * ensure that we can obtain sufficient file descriptors for all
351 * the accept() calls when a machine contains many domains.
353 (void) getrlimit(RLIMIT_NOFILE
, &rlim
);
354 if (rlim
.rlim_cur
< rlim
.rlim_max
)
355 rlim
.rlim_cur
= rlim
.rlim_max
;
356 if (setrlimit(RLIMIT_NOFILE
, &rlim
) < 0)
357 vntsd_log(VNTSD_STATUS_CONTINUE
, "Unable to increase file "
358 "descriptor limit.");
359 (void) enable_extended_FILE_stdio(-1, -1);
361 vntsdp
= calloc(sizeof (vntsd_t
), 1);
362 if (vntsdp
== NULL
) {
363 vntsd_log(VNTSD_ERR_NO_MEM
, "main:vntsdp");
367 vntsdp
->ctrl_fd
= -1;
368 vntsdp
->devinst
= NULL
;
370 (void) mutex_init(&vntsdp
->lock
, USYNC_THREAD
|LOCK_ERRORCHECK
, NULL
);
371 (void) mutex_init(&vntsdp
->tmo_lock
, USYNC_THREAD
|LOCK_ERRORCHECK
,
374 /* get CLI options */
375 while ((option
= getopt(argc
, argv
, "i:t:p:A"DEBUG_OPTIONS
)) != EOF
) {
379 vntsdp
->options
|= VNTSD_OPT_DAEMON_OFF
;
383 vntsdp
->devinst
= optarg
;
386 listen_addr
= optarg
;
390 n
= sscanf(optarg
, "%d", &(vntsdp
->timeout
));
392 vntsdp
->timeout
= -1;
398 * This option enables authorization checking of the
399 * user for the console(s) being accessed. As the
400 * authorization checking can be done only for a local
401 * client process, it requires that vntsd listen only
402 * on the loopback address. It means while this option
403 * is enabled, vntsd cannot listen on either INADDR_ANY
404 * or a specific ip address and thus the telnet client
405 * must also run on the local machine in order to
406 * connect to vntsd. The '-p' option if specified while
407 * this option is enabled, will be ignored and the auth
408 * checking takes precedence forcing vntsd to listen on
409 * the loopback interface.
411 vntsdp
->options
|= VNTSD_OPT_AUTH_CHECK
;
420 if ((vntsdp
->devinst
== NULL
) || (vntsdp
->timeout
== -1)) {
425 if (listen_addr
== NULL
|| strcmp(listen_addr
, "localhost") == 0 ||
426 strcmp(listen_addr
, LOCALHOST_IPv4
) == 0 ||
427 strcmp(listen_addr
, LOCALHOST_IPv6
) == 0) {
428 /* by default listen on loopback interface */
429 vntsdp
->ip_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
430 } else if ((vntsdp
->options
& VNTSD_OPT_AUTH_CHECK
) != 0) {
431 vntsd_log(VNTSD_STATUS_AUTH_ENABLED
,
432 "Listen address ignored as authorization checking "
434 vntsdp
->ip_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
435 } else if (strcmp(listen_addr
, "any") == 0) {
436 vntsdp
->ip_addr
.s_addr
= htonl(INADDR_ANY
);
438 vntsdp
->ip_addr
.s_addr
= get_listen_ip_addr(listen_addr
);
439 if (vntsdp
->ip_addr
.s_addr
== VNTSD_INVALID_LISTEN_ADDR
) {
441 "Invalid listen address '%s'\n",
447 D3(stderr
, "options = %llx, instance = %s, listen = %s\n",
448 vntsdp
->options
, vntsdp
->devinst
,
449 listen_addr
? listen_addr
: "<null>");
451 /* open VCC driver control port */
452 sz
= strlen(VCC_DEVICE_CTL_PATH
) + strlen(vntsdp
->devinst
) + 1;
453 path
= calloc(sz
, 1);
455 vntsd_log(VNTSD_ERR_NO_MEM
, "main(): alloc dev path");
458 (void) snprintf(path
, sz
-1, VCC_DEVICE_CTL_PATH
, vntsdp
->devinst
,
459 sizeof (vntsdp
->devinst
));
460 vntsdp
->ctrl_fd
= open(path
, O_RDWR
);
462 if (vntsdp
->ctrl_fd
== -1) {
463 /* print error if device is not present */
465 "Error opening VCC device control port: %s",
467 /* tell SMF no retry */
473 if ((vntsdp
->options
& VNTSD_OPT_DAEMON_OFF
) == 0) {
486 * child process (daemon)
488 * Close all file descriptors other than 2 and the ctrl fd.
492 for (i
= 3; i
< vntsdp
->ctrl_fd
; i
++) {
495 closefrom(vntsdp
->ctrl_fd
+ 1);
497 /* obtain a new process group */
499 fd
= open("/dev/null", O_RDWR
);
501 syslog(LOG_ERR
, "Can not open /dev/null");
504 /* handle standard I/O */
505 if (dup2(fd
, 0) < 0) {
506 syslog(LOG_ERR
, "Failed dup2()");
510 if (dup2(fd
, 1) < 0) {
511 syslog(LOG_ERR
, "Failed dup2()");
515 /* ignore terminal signals */
516 (void) signal(SIGTSTP
, SIG_IGN
);
517 (void) signal(SIGTTOU
, SIG_IGN
);
518 (void) signal(SIGTTIN
, SIG_IGN
);
522 /* set up signal handlers */
525 act
.sa_handler
= exit_sig_handler
;
527 (void) sigemptyset(&act
.sa_mask
);
528 (void) sigaction(SIGINT
, &act
, NULL
);
529 (void) sigaction(SIGTERM
, &act
, NULL
);
530 (void) sigaction(SIGHUP
, &act
, NULL
);
532 /* vntsd internal signals */
533 act
.sa_handler
= vntsd_sig_handler
;
534 (void) sigemptyset(&act
.sa_mask
);
535 (void) sigaction(SIGUSR1
, &act
, NULL
);
538 act
.sa_handler
= vntsd_alarm_sig_handler
;
539 (void) sigemptyset(&act
.sa_mask
);
540 (void) sigaction(SIGALRM
, &act
, NULL
);
544 (void) atexit(vntsd_exit
);
549 openlog("vntsd", LOG_CONS
, LOG_DAEMON
);
553 if (vntsdp
->timeout
> 0) {
554 (void) alarm(MINUTE
);
557 vntsdp
->tid
= thr_self();
559 /* get exiting consoles from vcc */
560 vntsd_get_config(vntsdp
);
563 /* poll vcc for configuration change */
564 bzero(poll_drv
, sizeof (poll_drv
));
566 poll_drv
[0].fd
= vntsdp
->ctrl_fd
;
567 poll_drv
[0].events
= POLLIN
;
569 if (poll(poll_drv
, 1, -1) == -1) {
570 if (errno
== EINTR
) {
571 /* wake up because a consle was deleted */
572 vntsd_delete_cons(vntsdp
);
575 vntsd_log(VNTSD_ERR_VCC_POLL
,
576 "vcc control poll err! aborting..");
580 D1(stderr
, "t@%d driver event %x\n", thr_self(),
581 poll_drv
[0].revents
);
583 vntsd_daemon_wakeup(vntsdp
);
585 * Main thread may miss a console-delete signal when it is
586 * not polling vcc. check if any console is deleted.
588 vntsd_delete_cons(vntsdp
);
600 return (vntsdp
->ip_addr
);
604 * ioctl to vcc control port
605 * Supported ioctls interface are:
606 * ioctl code parameters return data
607 * VCC_NUM_CONSOLE none uint_t no consoles
608 * VCC_CONS_TBL none array of vcc_cons_t
609 * VCC_INQUIRY none vcc_response_t response
610 * VCC_CONS_INFO uint_t portno vcc_cons_t
611 * VCC_CONS_STATUS uint_t portno
612 * VCC_FORCE_CLOSE uint_t portno
615 vntsd_vcc_ioctl(int ioctl_code
, uint_t portno
, void *buf
)
617 D1(stderr
, "t@%d vcc_ioctl@%d code=%x\n", thr_self(), portno
,
620 if ((ioctl_code
== (VCC_CONS_INFO
)) ||
621 (ioctl_code
== (VCC_FORCE_CLOSE
))) {
622 /* construct vcc in buf */
623 *((uint_t
*)buf
) = portno
;
626 if (ioctl(vntsdp
->ctrl_fd
, ioctl_code
, (caddr_t
)buf
)) {
627 /* ioctl request error */
628 return (VNTSD_STATUS_VCC_IO_ERR
);
631 return (VNTSD_SUCCESS
);
635 * check if a vcc i/o error is caused by removal of a console. If so
636 * wake up main thread to cleanup the console.
639 vntsd_vcc_err(vntsd_cons_t
*consp
)
641 vntsd_group_t
*groupp
;
644 groupp
= consp
->group
;
647 if (consp
->status
& VNTSD_CONS_DELETED
) {
648 /* console was deleted */
649 return (VNTSD_STATUS_VCC_IO_ERR
);
652 if (vntsd_vcc_cons_alive(consp
)) {
654 return (VNTSD_STATUS_CONTINUE
);
657 /* console needs to be deleted */
658 (void) mutex_lock(&consp
->lock
);
659 consp
->status
|= VNTSD_CONS_DELETED
;
662 * main thread will close all clients after receiving console
665 (void) mutex_unlock(&consp
->lock
);
668 (void) mutex_lock(&groupp
->lock
);
669 groupp
->status
|= VNTSD_GROUP_CLEAN_CONS
;
670 (void) mutex_unlock(&groupp
->lock
);
672 /* signal main thread to deleted console */
673 (void) thr_kill(vntsdp
->tid
, SIGUSR1
);
675 return (VNTSD_STATUS_VCC_IO_ERR
);