Home | History | Annotate | Download | only in vntsd
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * supporting modules.
     29  */
     30 
     31 #include <stdio.h>
     32 #include <sys/types.h>
     33 #include <sys/ipc.h>
     34 #include <stdlib.h>
     35 #include <string.h>
     36 #include <unistd.h>
     37 #include <sys/socket.h>
     38 #include <sys/ipc.h>
     39 #include <sys/shm.h>
     40 #include <sys/sem.h>
     41 #include <sys/poll.h>
     42 #include <wait.h>
     43 #include <time.h>
     44 #include <netinet/in.h>
     45 #include <thread.h>
     46 #include <signal.h>
     47 #include <ctype.h>
     48 #include <langinfo.h>
     49 #include <libintl.h>
     50 #include <syslog.h>
     51 #include "vntsd.h"
     52 #include "chars.h"
     53 
     54 /*  vntsd_write_line() - write a line to TCP client */
     55 int
     56 vntsd_write_line(vntsd_client_t *clientp, char *line)
     57 {
     58 	int rv;
     59 
     60 	rv = vntsd_write_client(clientp, line, strlen(line));
     61 	if (rv == VNTSD_SUCCESS) {
     62 		rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN);
     63 	}
     64 
     65 	return (rv);
     66 }
     67 
     68 /*  vntsd_write_lines() write one or more lines to client.  */
     69 int
     70 vntsd_write_lines(vntsd_client_t *clientp, char *lines)
     71 {
     72 	char	*buf;
     73 	char	*line;
     74 	char 	*endofline;
     75 
     76 	buf = strdup(lines);
     77 	if (buf == NULL) {
     78 		return (VNTSD_ERR_NO_MEM);
     79 	}
     80 
     81 	line = buf;
     82 
     83 	while ((line != NULL) && (*line != '\0')) {
     84 
     85 		endofline = strchr(line, '\n');
     86 		if (endofline != NULL) {
     87 			*endofline = '\0';
     88 		}
     89 
     90 		(void) vntsd_write_line(clientp, line);
     91 
     92 		if (endofline != NULL)
     93 			line = endofline + 1;
     94 		else
     95 			line = NULL;
     96 	}
     97 
     98 	free(buf);
     99 	return (VNTSD_SUCCESS);
    100 }
    101 
    102 /* vntsd_get_yes_no() -  read in a "y" or "n" */
    103 int
    104 vntsd_get_yes_no(vntsd_client_t *clientp, char *msg, int *yes_no)
    105 {
    106 	char	c;
    107 	char	yesno[8];
    108 	int	rv;
    109 
    110 	/* create [y/n] prompt */
    111 	(void) snprintf(yesno, sizeof (yesno), "[%c/%c] ",
    112 	    *nl_langinfo(YESSTR), *nl_langinfo(NOSTR));
    113 
    114 	for (; ; ) {
    115 		if ((rv = vntsd_write_client(clientp, msg, strlen(msg)))
    116 		    != VNTSD_SUCCESS) {
    117 			return (rv);
    118 		}
    119 
    120 		if ((rv = vntsd_write_client(clientp, yesno, strlen(yesno))) !=
    121 		    VNTSD_SUCCESS) {
    122 			return (rv);
    123 		}
    124 
    125 		if ((rv = vntsd_read_data(clientp, &c))
    126 		    != VNTSD_SUCCESS) {
    127 			return (rv);
    128 		}
    129 
    130 		/* echo */
    131 		if ((rv = vntsd_write_client(clientp, &c, 1)) !=
    132 		    VNTSD_SUCCESS) {
    133 			return (rv);
    134 		}
    135 
    136 		if ((rv = vntsd_write_client(clientp, vntsd_eol,
    137 		    VNTSD_EOL_LEN)) != VNTSD_SUCCESS) {
    138 			return (rv);
    139 		}
    140 
    141 		c = tolower(c);
    142 
    143 		if (c == *nl_langinfo(YESSTR)) {
    144 			*yes_no = B_TRUE;
    145 			return (VNTSD_SUCCESS);
    146 		}
    147 
    148 		if (c == *nl_langinfo(NOSTR)) {
    149 			*yes_no = B_FALSE;
    150 			return (VNTSD_SUCCESS);
    151 		}
    152 
    153 		if ((rv = vntsd_write_line(clientp,
    154 		    gettext("Invalid response. Try again.")))
    155 		    != VNTSD_SUCCESS) {
    156 			return (rv);
    157 		}
    158 	}
    159 
    160 	/*NOTREACHED*/
    161 	return (0);
    162 }
    163 
    164 /* vntsd_open_vcc()  -  open a vcc port */
    165 int
    166 vntsd_open_vcc(char *dev_name, uint_t cons_no)
    167 {
    168 	int	drvfd;
    169 	int	sz;
    170 	char	*path;
    171 	sz = strlen(VCC_DEVICE_PATH) + strlen(dev_name)+1;
    172 
    173 	path = calloc(sz, 1);
    174 
    175 	if (path == NULL) {
    176 		return (-1);
    177 	}
    178 
    179 	(void) snprintf(path, sz-1, VCC_DEVICE_PATH, dev_name);
    180 
    181 	for (; ; ) {
    182 		drvfd = open(path, O_RDWR);
    183 
    184 		if ((drvfd < 0) && (errno == EAGAIN)) {
    185 			if (vntsd_vcc_ioctl(VCC_FORCE_CLOSE, cons_no, &cons_no)
    186 			    != VNTSD_SUCCESS) {
    187 				break;
    188 			}
    189 		} else {
    190 			break;
    191 		}
    192 	}
    193 
    194 
    195 	if (drvfd < 0) {
    196 		D1(stderr, "t@%d open_vcc@%s exit\n", thr_self(), dev_name);
    197 		free(path);
    198 		return (-1);
    199 	}
    200 
    201 	free(path);
    202 	return (drvfd);
    203 }
    204 
    205 /* vntsd_cons_by_consno() - match a console structure to cons no */
    206 boolean_t
    207 vntsd_cons_by_consno(vntsd_cons_t *consp, int *cons_id)
    208 {
    209 	if (consp->status & VNTSD_CONS_DELETED) {
    210 		return (B_FALSE);
    211 	}
    212 	return (consp->cons_no == *cons_id);
    213 }
    214 
    215 /* vntsd_write_client() write to telnet client */
    216 int
    217 vntsd_write_client(vntsd_client_t *client, char *buffer, size_t sz)
    218 {
    219 	int rv;
    220 
    221 
    222 	/* write to client */
    223 	rv = vntsd_write_fd(client->sockfd, buffer, sz);
    224 
    225 	/* client has output, reset timer */
    226 	vntsd_reset_timer(client->cons_tid);
    227 
    228 	return (rv);
    229 }
    230 
    231 /* vntsd_write_fd() write to tcp socket file descriptor  */
    232 int
    233 vntsd_write_fd(int fd, void *buf, size_t sz)
    234 {
    235 	int n;
    236 
    237 	while (sz > 0) {
    238 		n = write(fd, buf, sz);
    239 		if (n < 0) {
    240 			if (errno == EINTR) {
    241 				return (VNTSD_STATUS_INTR);
    242 			}
    243 
    244 			return (VNTSD_STATUS_CLIENT_QUIT);
    245 		}
    246 
    247 		if (n == 0) {
    248 			return (VNTSD_STATUS_CLIENT_QUIT);
    249 		}
    250 
    251 		buf =  (caddr_t)buf + n;
    252 		sz -= n;
    253 	}
    254 	return (VNTSD_SUCCESS);
    255 
    256 }
    257 
    258 /*
    259  * vntsd_read_char() - read a char from TCP Clienti. Returns:
    260  * VNTSD_SUCCESS, VNTSD_STATUS_CLIENT_QUIT or VNTSD_STATUS_INTR
    261  */
    262 int
    263 vntsd_read_char(vntsd_client_t *clientp, char *c)
    264 {
    265 	int		n;
    266 	vntsd_timeout_t tmo;
    267 	int		rv;
    268 
    269 	tmo.tid = thr_self();
    270 	tmo.minutes = 0;
    271 	tmo.clientp = clientp;
    272 
    273 	/* attach to timer */
    274 	if ((rv = vntsd_attach_timer(&tmo)) != VNTSD_SUCCESS) {
    275 		return (rv);
    276 	}
    277 
    278 	n = read(clientp->sockfd, c, 1);
    279 
    280 	/* detach from timer */
    281 	if ((rv = vntsd_detach_timer(&tmo)) != VNTSD_SUCCESS) {
    282 		return (rv);
    283 	}
    284 
    285 	if (n == 1) {
    286 		return (VNTSD_SUCCESS);
    287 	}
    288 
    289 	if (n == 0) {
    290 		return (VNTSD_STATUS_CLIENT_QUIT);
    291 	}
    292 
    293 	/*
    294 	 * read error or wake up by signal, either console is being removed or
    295 	 * timeout occurs.
    296 	 */
    297 	if (errno == EINTR) {
    298 		return (VNTSD_STATUS_INTR);
    299 	}
    300 
    301 	/* any other error, we close client */
    302 	return (VNTSD_STATUS_CLIENT_QUIT);
    303 }
    304 
    305 /*
    306  * vntsd_read_data() -  handle special commands
    307  * such as telnet, daemon and ctrl cmds. Returns:
    308  * from vntsd_read_char:
    309  *	    VNTSD_STATUS_CLIENT_QUIT
    310  *	    VNTSD_STATUS_INTR
    311  * from vnts_process_daemon_cmd:
    312  *	    VNTSD_STATUS_RESELECT_CONS
    313  *	    VNTSD_STATUS_MOV_CONS_FORWARD
    314  *	    VNTSD_STATUS_MOV_CONS_BACKWARD
    315  *	    VNTSD_STATUS_ACQURE_WRITER
    316  *	    VNTSD_STATUS_CONTINUE
    317  * from vntsd_telnet_cmd
    318  *	    VNTSD_STATUS_CONTINUE
    319  */
    320 int
    321 vntsd_read_data(vntsd_client_t *clientp, char *c)
    322 {
    323 	int rv;
    324 
    325 	for (; ; ) {
    326 		if ((rv = vntsd_read_char(clientp, c)) != VNTSD_SUCCESS) {
    327 			return (rv);
    328 		}
    329 
    330 		/* daemon cmd? */
    331 		rv = vntsd_process_daemon_cmd(clientp, *c);
    332 
    333 		if (rv == VNTSD_SUCCESS) {
    334 			/* telnet cmd? */
    335 			rv = vntsd_telnet_cmd(clientp, *c);
    336 		}
    337 
    338 		if (rv == VNTSD_STATUS_CONTINUE) {
    339 			/*
    340 			 * either a daemon cmd or a telnet cmd
    341 			 * was processed.
    342 			 */
    343 			clientp->prev_char = 0;
    344 			continue;
    345 		}
    346 
    347 		return (rv);
    348 	}
    349 
    350 	/*NOTREACHED*/
    351 	return (0);
    352 }
    353 /* vntsd_read_line() -  read a line from TCP client */
    354 int
    355 vntsd_read_line(vntsd_client_t *clientp, char *buf, int *in_sz)
    356 {
    357 	char	c;
    358 	int	rv;
    359 	int	out_sz = 0;
    360 
    361 
    362 	for (; ; ) {
    363 
    364 		if ((rv =  vntsd_read_data(clientp, &c)) !=  VNTSD_SUCCESS) {
    365 			return (rv);
    366 		}
    367 
    368 		if (c == BS) {
    369 			/* back */
    370 			if ((rv = vntsd_write_client(clientp, &c, 1)) !=
    371 			    VNTSD_SUCCESS) {
    372 				return (rv);
    373 			}
    374 
    375 			c = ' ';
    376 			if ((rv = vntsd_write_client(clientp, &c, 1)) !=
    377 			    VNTSD_SUCCESS) {
    378 				return (rv);
    379 			}
    380 
    381 			buf--;
    382 			out_sz--;
    383 			continue;
    384 		}
    385 		/* echo */
    386 		if ((rv = vntsd_write_client(clientp, &c, 1)) !=
    387 		    VNTSD_SUCCESS) {
    388 			return (rv);
    389 		}
    390 
    391 		*buf++ = c;
    392 		out_sz++;
    393 
    394 		if (c == CR) {
    395 			/* end of line */
    396 			*in_sz = out_sz;
    397 			return (VNTSD_SUCCESS);
    398 		}
    399 
    400 		if (out_sz == *in_sz) {
    401 			return (VNTSD_SUCCESS);
    402 		}
    403 	}
    404 
    405 	/*NOTREACHED*/
    406 	return (0);
    407 }
    408 
    409 /* free a client */
    410 void
    411 vntsd_free_client(vntsd_client_t *clientp)
    412 {
    413 
    414 	if (clientp->sockfd != -1) {
    415 		(void) close(clientp->sockfd);
    416 	}
    417 
    418 	(void) mutex_destroy(&clientp->lock);
    419 
    420 	free(clientp);
    421 }
    422 
    423 
    424 /* check if a vcc console port still ok */
    425 boolean_t
    426 vntsd_vcc_cons_alive(vntsd_cons_t *consp)
    427 {
    428 	vcc_console_t	vcc_cons;
    429 	int		rv;
    430 
    431 	assert(consp);
    432 	assert(consp->group);
    433 
    434 	/* construct current configuration */
    435 	(void) strncpy(vcc_cons.domain_name, consp->domain_name, MAXPATHLEN);
    436 	(void) strncpy(vcc_cons.group_name, consp->group->group_name,
    437 	    MAXPATHLEN);
    438 	vcc_cons.tcp_port = consp->group->tcp_port;
    439 	vcc_cons.cons_no   = consp->cons_no;
    440 
    441 	/* call vcc to verify */
    442 	rv = vntsd_vcc_ioctl(VCC_CONS_STATUS, consp->cons_no, &vcc_cons);
    443 	if (rv != VNTSD_SUCCESS) {
    444 		return (B_FALSE);
    445 	}
    446 
    447 	if (vcc_cons.cons_no == -1) {
    448 		/* port is gone */
    449 		return (B_FALSE);
    450 	}
    451 
    452 	/* port is ok */
    453 	return (B_TRUE);
    454 
    455 }
    456 
    457 /* add to total if a console is alive  */
    458 static boolean_t
    459 total_cons(vntsd_cons_t *consp, int *num_cons)
    460 {
    461 	int rv;
    462 
    463 	assert(consp->group);
    464 	rv = vntsd_vcc_err(consp);
    465 	if (rv == VNTSD_STATUS_CONTINUE) {
    466 		(*num_cons)++;
    467 	}
    468 	return (B_FALSE);
    469 }
    470 
    471 
    472 /* total alive consoles in a group  */
    473 int
    474 vntsd_chk_group_total_cons(vntsd_group_t *groupp)
    475 {
    476 	uint_t num_cons = 0;
    477 
    478 	(void) vntsd_que_find(groupp->conspq, (compare_func_t)total_cons,
    479 	    &num_cons);
    480 	return (num_cons);
    481 }
    482 
    483 /* vntsd_log() log function for errors */
    484 void
    485 vntsd_log(vntsd_status_t status, char *msg)
    486 {
    487 	char	*status_msg = NULL;
    488 	int	critical = 0;
    489 
    490 	switch (status) {
    491 
    492 	case VNTSD_SUCCESS:
    493 		status_msg = "STATUS_OK";
    494 		break;
    495 
    496 	case VNTSD_STATUS_CONTINUE:
    497 		status_msg = "CONTINUE";
    498 		break;
    499 
    500 	case VNTSD_STATUS_EXIT_SIG:
    501 		critical = 1;
    502 		status_msg = "KILL SIGNAL RECV";
    503 		break;
    504 
    505 	case VNTSD_STATUS_SIG:
    506 		status_msg = "SIG RECV";
    507 		break;
    508 
    509 	case VNTSD_STATUS_NO_HOST_NAME:
    510 		status_msg = "Warining NO HOST NAME";
    511 		break;
    512 
    513 	case VNTSD_STATUS_CLIENT_QUIT:
    514 		status_msg = "CLIENT CLOSED  GROUP CONNECTION";
    515 		break;
    516 
    517 	case VNTSD_STATUS_RESELECT_CONS:
    518 		status_msg = "CLIENT RESELECTS CONSOLE";
    519 		break;
    520 
    521 	case VNTSD_STATUS_VCC_IO_ERR:
    522 		status_msg = "CONSOLE WAS DELETED";
    523 		break;
    524 
    525 	case VNTSD_STATUS_MOV_CONS_FORWARD:
    526 		status_msg = "MOVE CONSOLE FORWARD";
    527 		break;
    528 
    529 	case VNTSD_STATUS_MOV_CONS_BACKWARD:
    530 		status_msg = "MOVE CONSOLE BACKWARD";
    531 		break;
    532 
    533 	case VNTSD_STATUS_ACQUIRE_WRITER:
    534 		status_msg = "FORCE CONSOLE WRITE";
    535 		break;
    536 
    537 	case VNTSD_STATUS_INTR:
    538 		status_msg = "RECV SIGNAL";
    539 		break;
    540 
    541 	case VNTSD_STATUS_DISCONN_CONS:
    542 		status_msg = "DELETING CONSOLE";
    543 		break;
    544 
    545 	case VNTSD_STATUS_NO_CONS:
    546 		status_msg = "All console(s) in the group have been deleted.";
    547 		break;
    548 
    549 	case VNTSD_STATUS_AUTH_ENABLED:
    550 		critical = 1;
    551 		status_msg = "VNTSD_STATUS_AUTH_ENABLED";
    552 		break;
    553 
    554 	case VNTSD_ERR_NO_MEM:
    555 		critical = 1;
    556 		status_msg = "NO MEMORY";
    557 		break;
    558 
    559 	case VNTSD_ERR_NO_DRV:
    560 		critical = 1;
    561 		status_msg = "NO VCC DRIVER";
    562 		break;
    563 
    564 	case VNTSD_ERR_WRITE_CLIENT:
    565 		status_msg  =  "WRITE CLIENT ERR";
    566 		break;
    567 
    568 	case VNTSD_ERR_EL_NOT_FOUND:
    569 		critical = 1;
    570 		status_msg = "ELEMENT_NOT_FOUND";
    571 		break;
    572 
    573 	case VNTSD_ERR_VCC_CTRL_DATA:
    574 		critical = 1;
    575 		status_msg = "VCC CTRL DATA  ERROR";
    576 		break;
    577 
    578 	case VNTSD_ERR_VCC_POLL:
    579 		critical = 1;
    580 		status_msg = "VCC POLL ERROR";
    581 		break;
    582 
    583 	case VNTSD_ERR_VCC_IOCTL:
    584 		critical = 1;
    585 		status_msg = "VCC IOCTL ERROR";
    586 		break;
    587 
    588 	case VNTSD_ERR_VCC_GRP_NAME:
    589 		critical = 1;
    590 		status_msg = "VCC GROUP NAME ERROR";
    591 		break;
    592 
    593 	case VNTSD_ERR_CREATE_LISTEN_THR:
    594 		critical = 1;
    595 		status_msg = "FAIL TO CREATE LISTEN THREAD";
    596 		break;
    597 
    598 	case VNTSD_ERR_CREATE_WR_THR:
    599 		critical = 1;
    600 		status_msg = "FAIL TO CREATE WRITE THREAD";
    601 		break;
    602 
    603 	case VNTSD_ERR_ADD_CONS_FAILED:
    604 		critical = 1;
    605 		status_msg = "FAIL TO ADD A CONSOLE";
    606 		break;
    607 
    608 	case VNTSD_ERR_LISTEN_SOCKET:
    609 		critical = 1;
    610 		status_msg = "LISTEN SOCKET ERROR";
    611 		break;
    612 
    613 	case VNTSD_ERR_LISTEN_OPTS:
    614 		critical = 1;
    615 		status_msg = "SET SOCKET OPTIONS ERROR";
    616 		break;
    617 
    618 	case VNTSD_ERR_LISTEN_BIND:
    619 		critical = 1;
    620 		status_msg = "BIND SOCKET ERROR";
    621 		break;
    622 
    623 	case VNTSD_STATUS_ACCEPT_ERR:
    624 		critical = 1;
    625 		status_msg = "LISTEN ACCEPT ERROR";
    626 		break;
    627 
    628 	case VNTSD_ERR_CREATE_CONS_THR:
    629 		critical = 1;
    630 		status_msg = "CREATE CONSOLE THREAD ERROR ";
    631 		break;
    632 
    633 	case VNTSD_ERR_SIG:
    634 		critical = 1;
    635 		status_msg = "RECV UNKNOWN SIG";
    636 		break;
    637 
    638 	case VNTSD_ERR_UNKNOWN_CMD:
    639 		critical = 1;
    640 		status_msg = "RECV UNKNOWN COMMAND";
    641 		break;
    642 
    643 	case VNTSD_ERR_CLIENT_TIMEOUT:
    644 		status_msg  =  "CLOSE CLIENT BECAUSE TIMEOUT";
    645 		break;
    646 	default:
    647 		status_msg = "Unknown status recv";
    648 		break;
    649 	}
    650 
    651 
    652 	if (critical) {
    653 		syslog(LOG_ERR, "%s: thread[%d] %s\n", status_msg,
    654 		    thr_self(), msg);
    655 	}
    656 #ifdef DEBUG
    657 	DERR(stderr, "%s: thread[%d] %s\n", status_msg, thr_self(), msg);
    658 	syslog(LOG_ERR, "%s: thread[%d] %s\n", status_msg, thr_self(), msg);
    659 #endif
    660 }
    661