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