diff options
| author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-07-23 15:03:01 +0200 | 
|---|---|---|
| committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-07-23 15:03:01 +0200 | 
| commit | 777af8a8761d05c30588abec7444b143fe7393f0 (patch) | |
| tree | 5a135c37eaa9ac94772819a28ce5beedd18e5c4a /lib/ipmi_tsol.c | |
| parent | c3445516ecd58e97de483cf4b7fafcc1104890d7 (diff) | |
| parent | b32d92e890caac903491116e9d817aa780c0323b (diff) | |
Merge tag 'upstream/1.8.14'
Upstream version 1.8.14
Diffstat (limited to 'lib/ipmi_tsol.c')
| -rw-r--r-- | lib/ipmi_tsol.c | 608 | 
1 files changed, 608 insertions, 0 deletions
| diff --git a/lib/ipmi_tsol.c b/lib/ipmi_tsol.c new file mode 100644 index 0000000..94ea284 --- /dev/null +++ b/lib/ipmi_tsol.c @@ -0,0 +1,608 @@ +/* + * Copyright (c) 2005 Tyan Computer Corp.  All Rights Reserved. + *  + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + *  + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + *  + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + *  + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + *  + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/poll.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <signal.h> + +#include <sys/select.h> +#include <sys/time.h> +#include <sys/ioctl.h> + +#if defined(HAVE_CONFIG_H) +# include <config.h> +#endif + +#if defined(HAVE_TERMIOS_H) +# include <termios.h> +#elif defined (HAVE_SYS_TERMIOS_H) +# include <sys/termios.h> +#endif + +#include <ipmitool/log.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_tsol.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/bswap.h> + +static struct timeval _start_keepalive; +static struct termios _saved_tio; +static struct winsize _saved_winsize; +static int _in_raw_mode = 0; +static int _altterm = 0; + +extern int verbose; + +static int +ipmi_tsol_command(struct ipmi_intf * intf, char *recvip, int port, unsigned char cmd) +{ +        struct ipmi_rs *rsp; +        struct ipmi_rq	req; +        unsigned char	data[6]; +	unsigned	ip1, ip2, ip3, ip4; + +        if (sscanf(recvip, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) != 4) { +                lprintf(LOG_ERR, "Invalid IP address: %s", recvip); +                return -1; +        } + +	memset(&req, 0, sizeof(struct ipmi_rq)); +        req.msg.netfn    = IPMI_NETFN_TSOL; +        req.msg.cmd      = cmd; +        req.msg.data_len = 6; +        req.msg.data     = data; + +	memset(data, 0, sizeof(data)); +        data[0] = ip1; +        data[1] = ip2; +        data[2] = ip3; +        data[3] = ip4; +        data[4] = (port & 0xff00) >> 8; +        data[5] = (port & 0xff); + +        rsp = intf->sendrecv(intf, &req); +        if (rsp == NULL) { +		lprintf(LOG_ERR, "Unable to perform TSOL command"); +                return -1; +	} +	if (rsp->ccode > 0) { +		lprintf(LOG_ERR, "Unable to perform TSOL command: %s", +			val2str(rsp->ccode, completion_code_vals)); +		return -1; +        } + +        return 0; +} + +static int +ipmi_tsol_start(struct ipmi_intf * intf, char *recvip, int port) +{ +	return ipmi_tsol_command(intf, recvip, port, IPMI_TSOL_CMD_START); +} + +static int +ipmi_tsol_stop(struct ipmi_intf * intf, char *recvip, int port) +{ +        return ipmi_tsol_command(intf, recvip, port, IPMI_TSOL_CMD_STOP); +} + +static int +ipmi_tsol_send_keystroke(struct ipmi_intf * intf, char *buff, int length) +{ +        struct ipmi_rs * rsp; +        struct ipmi_rq   req; +        unsigned char    data[16]; +	static unsigned char keyseq = 0; + +	memset(&req, 0, sizeof(struct ipmi_rq)); +        req.msg.netfn    = IPMI_NETFN_TSOL; +        req.msg.cmd      = IPMI_TSOL_CMD_SENDKEY; +        req.msg.data_len = length + 2; +        req.msg.data     = data; + +	memset(data, 0, sizeof(data)); +        data[0] = length + 1; +	memcpy(data + 1, buff, length); +	data[length + 1] = keyseq++; + +        rsp = intf->sendrecv(intf, &req); +	if (verbose) { +		if (rsp == NULL) { +			lprintf(LOG_ERR, "Unable to send keystroke"); +			return -1; +		} +		if (rsp->ccode > 0) { +			lprintf(LOG_ERR, "Unable to send keystroke: %s", +				val2str(rsp->ccode, completion_code_vals)); +			return -1; +		} +	} + +        return length; +} + +static int +tsol_keepalive(struct ipmi_intf * intf) +{ +        struct timeval end; + +        gettimeofday(&end, 0); + +        if (end.tv_sec - _start_keepalive.tv_sec <= 30) +                return 0; + +        intf->keepalive(intf); + +	gettimeofday(&_start_keepalive, 0); + +        return 0; +} + +static void +print_escape_seq(struct ipmi_intf *intf) +{ +	lprintf(LOG_NOTICE, +		"       %c.  - terminate connection\n" +		"       %c^Z - suspend ipmitool\n" +		"       %c^X - suspend ipmitool, but don't restore tty on restart\n" +		"       %c?  - this message\n" +		"       %c%c  - send the escape character by typing it twice\n" +		"       (Note that escapes are only recognized immediately after newline.)", +		intf->session->sol_escape_char, +		intf->session->sol_escape_char, +		intf->session->sol_escape_char, +		intf->session->sol_escape_char, +		intf->session->sol_escape_char, +		intf->session->sol_escape_char); +} + +static int +leave_raw_mode(void) +{ +	if (!_in_raw_mode) +		return -1; +	else if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1) +		lperror(LOG_ERR, "tcsetattr(stdin)"); +	else if (tcsetattr(fileno(stdout), TCSADRAIN, &_saved_tio) == -1) +		lperror(LOG_ERR, "tcsetattr(stdout)"); +	else +		_in_raw_mode = 0; + +	return 0; +} + +static int +enter_raw_mode(void) +{ +	struct termios tio; + +	if (tcgetattr(fileno(stdout), &_saved_tio) < 0) { +		lperror(LOG_ERR, "tcgetattr failed"); +		return -1; +	} + +	tio = _saved_tio; + +	if (_altterm) { +		tio.c_iflag &= (ISTRIP | IGNBRK ); +		tio.c_cflag &= ~(CSIZE | PARENB | IXON | IXOFF | IXANY); +		tio.c_cflag |= (CS8 |CREAD) | (IXON|IXOFF|IXANY); +		tio.c_lflag &= 0; +		tio.c_cc[VMIN] = 1; +		tio.c_cc[VTIME] = 0; +	} else { +		tio.c_iflag |= IGNPAR; +		tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); +		tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL | IEXTEN); +		tio.c_oflag &= ~OPOST; +		tio.c_cc[VMIN] = 1; +		tio.c_cc[VTIME] = 0; +	} + +	if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0) +		lperror(LOG_ERR, "tcsetattr(stdin)"); +	else if (tcsetattr(fileno(stdout), TCSADRAIN, &tio) < 0) +		lperror(LOG_ERR, "tcsetattr(stdout)"); +	else +		_in_raw_mode = 1; + +	return 0; +} + +static void +suspend_self(int restore_tty) +{ +	leave_raw_mode(); + +	kill(getpid(), SIGTSTP); + +	if (restore_tty) +		enter_raw_mode(); +} + +static int +do_inbuf_actions(struct ipmi_intf *intf, char *in_buff, int len) +{ +	static int in_esc = 0; +	static int last_was_cr = 1; +	int i; + +	for(i = 0; i < len ;) { +		if (!in_esc) { +			if (last_was_cr && +			    (in_buff[i] == intf->session->sol_escape_char)) { +				in_esc = 1; +				memmove(in_buff, in_buff + 1, len - i - 1); +				len--; +				continue; +			} +		} +		if (in_esc) { +			if (in_buff[i] == intf->session->sol_escape_char) { +				in_esc = 0; +				i++; +				continue; +			} + +			switch (in_buff[i]) { +			case '.': +				printf("%c. [terminated ipmitool]\n", +				       intf->session->sol_escape_char); +				return -1; + +			case 'Z' - 64: +				printf("%c^Z [suspend ipmitool]\n", +				       intf->session->sol_escape_char); +				suspend_self(1); /* Restore tty back to raw */ +				break; + +			case 'X' - 64: +				printf("%c^X [suspend ipmitool]\n", +				       intf->session->sol_escape_char); +				suspend_self(0); /* Don't restore to raw mode */ +				break; + +			case '?': +				printf("%c? [ipmitool help]\n", +				       intf->session->sol_escape_char); +				print_escape_seq(intf); +				break; +			} + +			memmove(in_buff, in_buff + 1, len - i - 1); +			len--; +			in_esc = 0; + +			continue; +		} + +		last_was_cr = (in_buff[i] == '\r' || in_buff[i] == '\n'); + +		i++; +	} + +	return len; +} + + +static void +do_terminal_cleanup(void) +{ +	if (_saved_winsize.ws_row > 0 && _saved_winsize.ws_col > 0) +		ioctl(fileno(stdout), TIOCSWINSZ, &_saved_winsize); + +	leave_raw_mode(); + +	if (errno) +		lprintf(LOG_ERR, "Exiting due to error %d -> %s", +			errno, strerror(errno)); +} + +static void +set_terminal_size(int rows, int cols) +{ +	struct winsize winsize; + +	if (rows <= 0 || cols <= 0) +		return; + +	/* save initial winsize */ +	ioctl(fileno(stdout), TIOCGWINSZ, &_saved_winsize); + +	/* set new winsize */ +	winsize.ws_row = rows; +	winsize.ws_col = cols; +	ioctl(fileno(stdout), TIOCSWINSZ, &winsize); +} + +static void +print_tsol_usage(void) +{ +	struct winsize winsize; + +	lprintf(LOG_NOTICE, "Usage: tsol [recvip] [port=NUM] [ro|rw] [rows=NUM] [cols=NUM] [altterm]"); +	lprintf(LOG_NOTICE, "       recvip       Receiver IP Address             [default=local]"); +	lprintf(LOG_NOTICE, "       port=NUM     Receiver UDP Port               [default=%d]", +		IPMI_TSOL_DEF_PORT); +	lprintf(LOG_NOTICE, "       ro|rw        Set Read-Only or Read-Write     [default=rw]"); + +	ioctl(fileno(stdout), TIOCGWINSZ, &winsize); +	lprintf(LOG_NOTICE, "       rows=NUM     Set terminal rows               [default=%d]", +		winsize.ws_row); +	lprintf(LOG_NOTICE, "       cols=NUM     Set terminal columns            [default=%d]", +		winsize.ws_col); + +	lprintf(LOG_NOTICE, "       altterm      Alternate terminal setup        [default=off]"); +} + +int +ipmi_tsol_main(struct ipmi_intf * intf, int argc, char ** argv) +{ +	struct pollfd fds_wait[3], fds_data_wait[3], *fds; +	struct sockaddr_in sin, myaddr, *sa_in; +	socklen_t mylen; +	char *recvip = NULL; +	char out_buff[IPMI_BUF_SIZE * 8], in_buff[IPMI_BUF_SIZE]; +	char buff[IPMI_BUF_SIZE + 4]; +	int fd_socket, result, i; +	int out_buff_fill, in_buff_fill; +	int ip1, ip2, ip3, ip4; +	int read_only = 0, rows = 0, cols = 0; +	int port = IPMI_TSOL_DEF_PORT; + +	if (strlen(intf->name) < 3 || strncmp(intf->name, "lan", 3) != 0) { +		lprintf(LOG_ERR, "Error: Tyan SOL is only available over lan interface"); +		return -1; +	} + +	for (i = 0; i<argc; i++) { +		if (sscanf(argv[i], "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) == 4) { +			/* not free'd ...*/ +			/* recvip = strdup(argv[i]); */ +			recvip = argv[i]; +		}  +		else if (sscanf(argv[i], "port=%d", &ip1) == 1) +			port = ip1; +		else if (sscanf(argv[i], "rows=%d", &ip1) == 1) +			rows = ip1; +		else if (sscanf(argv[i], "cols=%d", &ip1) == 1) +			cols = ip1; +		else if (strlen(argv[i]) == 2 && strncmp(argv[i], "ro", 2) == 0) +			read_only = 1; +		else if (strlen(argv[i]) == 2 && strncmp(argv[i], "rw", 2) == 0) +			read_only = 0; +		else if (strlen(argv[i]) == 7 && strncmp(argv[i], "altterm", 7) == 0) +			_altterm = 1; +		else if (strlen(argv[i]) == 4 && strncmp(argv[i], "help", 4) == 0) { +			print_tsol_usage(); +			return 0; +		} +		else { +			print_tsol_usage(); +			return 0; +		} +	} + +	/* create udp socket to receive the packet */ +	memset(&sin, 0, sizeof(sin)); +	sin.sin_family = AF_INET; +	sin.sin_port = htons(port); + +	sa_in = (struct sockaddr_in *)&intf->session->addr; +	result = inet_pton(AF_INET, (const char *)intf->session->hostname, +			   &sa_in->sin_addr); + +	if (result <= 0) { +		struct hostent *host = gethostbyname((const char *)intf->session->hostname); +		if (host == NULL ) { +			lprintf(LOG_ERR, "Address lookup for %s failed", +				intf->session->hostname); +			return -1; +		} +		if (host->h_addrtype != AF_INET) { +			lprintf(LOG_ERR, +					"Address lookup for %s failed. Got %s, expected IPv4 address.", +					intf->session->hostname, +					(host->h_addrtype == AF_INET6) ? "IPv6" : "Unknown"); +			return (-1); +		} +		sa_in->sin_family = host->h_addrtype; +		memcpy(&sa_in->sin_addr, host->h_addr, host->h_length); +	} + +	fd_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); +	if (fd_socket < 0) { +		lprintf(LOG_ERR, "Can't open port %d", port); +		return -1; +	} +	if (-1 == bind(fd_socket, (struct sockaddr *)&sin, sizeof(sin))) { +		lprintf(LOG_ERR, "Failed to bind socket."); +		close(fd_socket); +		return -1; +	} + +	/* +	 * retrieve local IP address if not supplied on command line +	 */ +	if (recvip == NULL) { +		result = intf->open(intf);	/* must connect first */ +		if (result < 0) { +			close(fd_socket); +			return -1; +		} + +		mylen = sizeof(myaddr); +		if (getsockname(intf->fd, (struct sockaddr *)&myaddr, &mylen) < 0) { +			lperror(LOG_ERR, "getsockname failed"); +			close(fd_socket); +			return -1; +		} + +		recvip = inet_ntoa(myaddr.sin_addr); +		if (recvip == NULL) { +			lprintf(LOG_ERR, "Unable to find local IP address"); +			close(fd_socket); +			return -1; +		} +	} + +	printf("[Starting %sSOL with receiving address %s:%d]\n", +	       read_only ? "Read-only " : "", recvip, port); + +	set_terminal_size(rows, cols); +	enter_raw_mode(); + +	/* +	 * talk to smdc to start Console redirect - IP address and port as parameter +	 * ipmitool -I lan -H 192.168.168.227 -U Administrator raw 0x30 0x06 0xC0 0xA8 0xA8 0x78 0x1A 0x0A +	 */ +	result = ipmi_tsol_start(intf, recvip, port); +        if (result < 0) { +		lprintf(LOG_ERR, "Error starting SOL"); +		close(fd_socket); +                return -1; +        } + +	printf("[SOL Session operational.  Use %c? for help]\n", +	       intf->session->sol_escape_char); + +	gettimeofday(&_start_keepalive, 0); + +	fds_wait[0].fd = fd_socket; +	fds_wait[0].events = POLLIN; +	fds_wait[0].revents = 0; +	fds_wait[1].fd = fileno(stdin); +	fds_wait[1].events = POLLIN; +	fds_wait[1].revents = 0; +	fds_wait[2].fd = -1; +	fds_wait[2].events = 0; +	fds_wait[2].revents = 0; + +	fds_data_wait[0].fd = fd_socket; +	fds_data_wait[0].events = POLLIN | POLLOUT; +	fds_data_wait[0].revents = 0; +	fds_data_wait[1].fd = fileno(stdin); +	fds_data_wait[1].events = POLLIN; +	fds_data_wait[1].revents = 0; +	fds_data_wait[2].fd = fileno(stdout); +	fds_data_wait[2].events = POLLOUT; +	fds_data_wait[2].revents = 0; + +	out_buff_fill = 0; +	in_buff_fill = 0; +	fds = fds_wait; + +	for (;;) { +		result = poll(fds, 3, 15*1000); +		if (result < 0) +			break; + +		/* send keepalive packet */ +		tsol_keepalive(intf); + +		if ((fds[0].revents & POLLIN) && (sizeof(out_buff) > out_buff_fill)){ +			socklen_t sin_len = sizeof(sin); +			result = recvfrom(fd_socket, buff, sizeof(out_buff) - out_buff_fill + 4, 0, +					  (struct sockaddr *)&sin, &sin_len); + +			/* read the data from udp socket, skip some bytes in the head */ +			if((result - 4) > 0 ){ +				int length = result - 4; +#if 1 +		 		length = (unsigned char)buff[2] & 0xff; +			       	length *= 256; +				length += ((unsigned char)buff[3] & 0xff); +				if ((length <= 0) || (length > (result - 4))) +			              length = result - 4; +#endif +				memcpy(out_buff + out_buff_fill, buff + 4, length); +				out_buff_fill += length; +			} +		} +		if ((fds[1].revents & POLLIN) && (sizeof(in_buff) > in_buff_fill)) { +			result = read(fileno(stdin), in_buff + in_buff_fill, +				      sizeof(in_buff) - in_buff_fill); // read from keyboard +			if (result > 0) { +				int bytes; +				bytes = do_inbuf_actions(intf, in_buff + in_buff_fill, result); +				if(bytes < 0) { +					result = ipmi_tsol_stop(intf, recvip, port); +					do_terminal_cleanup(); +					return result; +				} +				if (read_only) +					bytes = 0; +				in_buff_fill += bytes; +			} +		} +		if ((fds[2].revents & POLLOUT) && out_buff_fill) { +			result = write(fileno(stdout), out_buff, out_buff_fill); // to screen +			if (result > 0) { +				out_buff_fill -= result; +				if (out_buff_fill) { +					memmove(out_buff, out_buff + result, out_buff_fill); +				} +			} +		} +		if ((fds[0].revents & POLLOUT) && in_buff_fill) { +			/* +			 * translate key and send that to SMDC using IPMI +			 * ipmitool -I lan -H 192.168.168.227 -U Administrator raw 0x30 0x03 0x04 0x1B 0x5B 0x43 +			 */ +			result = ipmi_tsol_send_keystroke(intf, in_buff, __min(in_buff_fill,14)); +			if (result > 0) { +				gettimeofday(&_start_keepalive, 0); +				in_buff_fill -= result; +				if (in_buff_fill) { +					memmove(in_buff, in_buff + result, in_buff_fill); +				} +			} +		} +		fds = (in_buff_fill || out_buff_fill )? +			fds_data_wait : fds_wait; +	} + +	return 0; +} | 
