xref: /openbmc/ipmitool/lib/ipmi_tsol.c (revision d79b0e05af2d9ae1c3d5493d0941cc00792da74b)
1c18ec02fSPetter Reinholdtsen /*
2c18ec02fSPetter Reinholdtsen  * Copyright (c) 2005 Tyan Computer Corp.  All Rights Reserved.
3c18ec02fSPetter Reinholdtsen  *
4c18ec02fSPetter Reinholdtsen  * Redistribution and use in source and binary forms, with or without
5c18ec02fSPetter Reinholdtsen  * modification, are permitted provided that the following conditions
6c18ec02fSPetter Reinholdtsen  * are met:
7c18ec02fSPetter Reinholdtsen  *
8c18ec02fSPetter Reinholdtsen  * Redistribution of source code must retain the above copyright
9c18ec02fSPetter Reinholdtsen  * notice, this list of conditions and the following disclaimer.
10c18ec02fSPetter Reinholdtsen  *
11c18ec02fSPetter Reinholdtsen  * Redistribution in binary form must reproduce the above copyright
12c18ec02fSPetter Reinholdtsen  * notice, this list of conditions and the following disclaimer in the
13c18ec02fSPetter Reinholdtsen  * documentation and/or other materials provided with the distribution.
14c18ec02fSPetter Reinholdtsen  *
15c18ec02fSPetter Reinholdtsen  * Neither the name of Sun Microsystems, Inc. or the names of
16c18ec02fSPetter Reinholdtsen  * contributors may be used to endorse or promote products derived
17c18ec02fSPetter Reinholdtsen  * from this software without specific prior written permission.
18c18ec02fSPetter Reinholdtsen  *
19c18ec02fSPetter Reinholdtsen  * This software is provided "AS IS," without a warranty of any kind.
20c18ec02fSPetter Reinholdtsen  * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
21c18ec02fSPetter Reinholdtsen  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
22c18ec02fSPetter Reinholdtsen  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
23c18ec02fSPetter Reinholdtsen  * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
24c18ec02fSPetter Reinholdtsen  * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
25c18ec02fSPetter Reinholdtsen  * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
26c18ec02fSPetter Reinholdtsen  * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
27c18ec02fSPetter Reinholdtsen  * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
28c18ec02fSPetter Reinholdtsen  * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
29c18ec02fSPetter Reinholdtsen  * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
30c18ec02fSPetter Reinholdtsen  * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31c18ec02fSPetter Reinholdtsen  */
32c18ec02fSPetter Reinholdtsen 
33c18ec02fSPetter Reinholdtsen #include <sys/types.h>
34c18ec02fSPetter Reinholdtsen #include <sys/stat.h>
35c18ec02fSPetter Reinholdtsen #include <sys/poll.h>
36c18ec02fSPetter Reinholdtsen #include <fcntl.h>
37c18ec02fSPetter Reinholdtsen #include <unistd.h>
38c18ec02fSPetter Reinholdtsen #include <errno.h>
39c18ec02fSPetter Reinholdtsen #include <stdlib.h>
40c18ec02fSPetter Reinholdtsen #include <stdio.h>
41c18ec02fSPetter Reinholdtsen #include <string.h>
42c18ec02fSPetter Reinholdtsen #include <sys/socket.h>
43c18ec02fSPetter Reinholdtsen #include <netinet/in.h>
44c18ec02fSPetter Reinholdtsen #include <arpa/inet.h>
45c18ec02fSPetter Reinholdtsen #include <netdb.h>
46c18ec02fSPetter Reinholdtsen #include <signal.h>
47c18ec02fSPetter Reinholdtsen 
48c18ec02fSPetter Reinholdtsen #include <sys/select.h>
49c18ec02fSPetter Reinholdtsen #include <sys/time.h>
50c18ec02fSPetter Reinholdtsen #include <sys/ioctl.h>
51c18ec02fSPetter Reinholdtsen 
52c18ec02fSPetter Reinholdtsen #if defined(HAVE_CONFIG_H)
53c18ec02fSPetter Reinholdtsen # include <config.h>
54c18ec02fSPetter Reinholdtsen #endif
55c18ec02fSPetter Reinholdtsen 
56c18ec02fSPetter Reinholdtsen #if defined(HAVE_TERMIOS_H)
57c18ec02fSPetter Reinholdtsen # include <termios.h>
58c18ec02fSPetter Reinholdtsen #elif defined (HAVE_SYS_TERMIOS_H)
59c18ec02fSPetter Reinholdtsen # include <sys/termios.h>
60c18ec02fSPetter Reinholdtsen #endif
61c18ec02fSPetter Reinholdtsen 
62c18ec02fSPetter Reinholdtsen #include <ipmitool/log.h>
63c18ec02fSPetter Reinholdtsen #include <ipmitool/helper.h>
64c18ec02fSPetter Reinholdtsen #include <ipmitool/ipmi.h>
65c18ec02fSPetter Reinholdtsen #include <ipmitool/ipmi_intf.h>
66c18ec02fSPetter Reinholdtsen #include <ipmitool/ipmi_tsol.h>
67c18ec02fSPetter Reinholdtsen #include <ipmitool/ipmi_strings.h>
68c18ec02fSPetter Reinholdtsen #include <ipmitool/bswap.h>
69c18ec02fSPetter Reinholdtsen 
70c18ec02fSPetter Reinholdtsen static struct timeval _start_keepalive;
71c18ec02fSPetter Reinholdtsen static struct termios _saved_tio;
72c18ec02fSPetter Reinholdtsen static struct winsize _saved_winsize;
73c18ec02fSPetter Reinholdtsen static int _in_raw_mode = 0;
74c18ec02fSPetter Reinholdtsen static int _altterm = 0;
75c18ec02fSPetter Reinholdtsen 
76c18ec02fSPetter Reinholdtsen extern int verbose;
77c18ec02fSPetter Reinholdtsen 
78c18ec02fSPetter Reinholdtsen static int
79aefd2872SZdenek Styblik ipmi_tsol_command(struct ipmi_intf *intf, char *recvip, int port,
80aefd2872SZdenek Styblik 		unsigned char cmd)
81c18ec02fSPetter Reinholdtsen {
82c18ec02fSPetter Reinholdtsen 	struct ipmi_rs *rsp;
83c18ec02fSPetter Reinholdtsen 	struct ipmi_rq	req;
84c18ec02fSPetter Reinholdtsen 	unsigned char data[6];
85c18ec02fSPetter Reinholdtsen 	unsigned ip1, ip2, ip3, ip4;
86c18ec02fSPetter Reinholdtsen 
87c18ec02fSPetter Reinholdtsen 	if (sscanf(recvip, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) != 4) {
88c18ec02fSPetter Reinholdtsen 		lprintf(LOG_ERR, "Invalid IP address: %s", recvip);
89aefd2872SZdenek Styblik 		return (-1);
90c18ec02fSPetter Reinholdtsen 	}
91c18ec02fSPetter Reinholdtsen 	memset(&req, 0, sizeof(struct ipmi_rq));
92c18ec02fSPetter Reinholdtsen 	req.msg.netfn = IPMI_NETFN_TSOL;
93c18ec02fSPetter Reinholdtsen 	req.msg.cmd = cmd;
94c18ec02fSPetter Reinholdtsen 	req.msg.data_len = 6;
95c18ec02fSPetter Reinholdtsen 	req.msg.data = data;
96c18ec02fSPetter Reinholdtsen 
97c18ec02fSPetter Reinholdtsen 	memset(data, 0, sizeof(data));
98c18ec02fSPetter Reinholdtsen 	data[0] = ip1;
99c18ec02fSPetter Reinholdtsen 	data[1] = ip2;
100c18ec02fSPetter Reinholdtsen 	data[2] = ip3;
101c18ec02fSPetter Reinholdtsen 	data[3] = ip4;
102c18ec02fSPetter Reinholdtsen 	data[4] = (port & 0xff00) >> 8;
103c18ec02fSPetter Reinholdtsen 	data[5] = (port & 0xff);
104c18ec02fSPetter Reinholdtsen 
105c18ec02fSPetter Reinholdtsen 	rsp = intf->sendrecv(intf, &req);
106c18ec02fSPetter Reinholdtsen 	if (rsp == NULL) {
107c18ec02fSPetter Reinholdtsen 		lprintf(LOG_ERR, "Unable to perform TSOL command");
108aefd2872SZdenek Styblik 		return (-1);
109c18ec02fSPetter Reinholdtsen 	}
110c18ec02fSPetter Reinholdtsen 	if (rsp->ccode > 0) {
111c18ec02fSPetter Reinholdtsen 		lprintf(LOG_ERR, "Unable to perform TSOL command: %s",
112c18ec02fSPetter Reinholdtsen 				val2str(rsp->ccode, completion_code_vals));
113aefd2872SZdenek Styblik 		return (-1);
114c18ec02fSPetter Reinholdtsen 	}
115c18ec02fSPetter Reinholdtsen 	return 0;
116c18ec02fSPetter Reinholdtsen }
117c18ec02fSPetter Reinholdtsen 
118c18ec02fSPetter Reinholdtsen static int
119c18ec02fSPetter Reinholdtsen ipmi_tsol_start(struct ipmi_intf *intf, char *recvip, int port)
120c18ec02fSPetter Reinholdtsen {
121c18ec02fSPetter Reinholdtsen 	return ipmi_tsol_command(intf, recvip, port, IPMI_TSOL_CMD_START);
122c18ec02fSPetter Reinholdtsen }
123c18ec02fSPetter Reinholdtsen 
124c18ec02fSPetter Reinholdtsen static int
125c18ec02fSPetter Reinholdtsen ipmi_tsol_stop(struct ipmi_intf *intf, char *recvip, int port)
126c18ec02fSPetter Reinholdtsen {
127c18ec02fSPetter Reinholdtsen 	return ipmi_tsol_command(intf, recvip, port, IPMI_TSOL_CMD_STOP);
128c18ec02fSPetter Reinholdtsen }
129c18ec02fSPetter Reinholdtsen 
130c18ec02fSPetter Reinholdtsen static int
131c18ec02fSPetter Reinholdtsen ipmi_tsol_send_keystroke(struct ipmi_intf *intf, char *buff, int length)
132c18ec02fSPetter Reinholdtsen {
133c18ec02fSPetter Reinholdtsen 	struct ipmi_rs *rsp;
134c18ec02fSPetter Reinholdtsen 	struct ipmi_rq req;
135c18ec02fSPetter Reinholdtsen 	unsigned char data[16];
136c18ec02fSPetter Reinholdtsen 	static unsigned char keyseq = 0;
137c18ec02fSPetter Reinholdtsen 
138c18ec02fSPetter Reinholdtsen 	memset(&req, 0, sizeof(struct ipmi_rq));
139c18ec02fSPetter Reinholdtsen 	req.msg.netfn = IPMI_NETFN_TSOL;
140c18ec02fSPetter Reinholdtsen 	req.msg.cmd = IPMI_TSOL_CMD_SENDKEY;
141c18ec02fSPetter Reinholdtsen 	req.msg.data_len = length + 2;
142c18ec02fSPetter Reinholdtsen 	req.msg.data = data;
143c18ec02fSPetter Reinholdtsen 
144c18ec02fSPetter Reinholdtsen 	memset(data, 0, sizeof(data));
145c18ec02fSPetter Reinholdtsen 	data[0] = length + 1;
146c18ec02fSPetter Reinholdtsen 	memcpy(data + 1, buff, length);
147c18ec02fSPetter Reinholdtsen 	data[length + 1] = keyseq++;
148c18ec02fSPetter Reinholdtsen 
149c18ec02fSPetter Reinholdtsen 	rsp = intf->sendrecv(intf, &req);
150c18ec02fSPetter Reinholdtsen 	if (verbose) {
151c18ec02fSPetter Reinholdtsen 		if (rsp == NULL) {
152c18ec02fSPetter Reinholdtsen 			lprintf(LOG_ERR, "Unable to send keystroke");
153c18ec02fSPetter Reinholdtsen 			return -1;
154c18ec02fSPetter Reinholdtsen 		}
155c18ec02fSPetter Reinholdtsen 		if (rsp->ccode > 0) {
156c18ec02fSPetter Reinholdtsen 			lprintf(LOG_ERR, "Unable to send keystroke: %s",
157c18ec02fSPetter Reinholdtsen 					val2str(rsp->ccode, completion_code_vals));
158c18ec02fSPetter Reinholdtsen 			return -1;
159c18ec02fSPetter Reinholdtsen 		}
160c18ec02fSPetter Reinholdtsen 	}
161c18ec02fSPetter Reinholdtsen 	return length;
162c18ec02fSPetter Reinholdtsen }
163c18ec02fSPetter Reinholdtsen 
164c18ec02fSPetter Reinholdtsen static int
165c18ec02fSPetter Reinholdtsen tsol_keepalive(struct ipmi_intf *intf)
166c18ec02fSPetter Reinholdtsen {
167c18ec02fSPetter Reinholdtsen 	struct timeval end;
168c18ec02fSPetter Reinholdtsen 	gettimeofday(&end, 0);
169aefd2872SZdenek Styblik 	if (end.tv_sec - _start_keepalive.tv_sec <= 30) {
170c18ec02fSPetter Reinholdtsen 		return 0;
171aefd2872SZdenek Styblik 	}
172c18ec02fSPetter Reinholdtsen 	intf->keepalive(intf);
173c18ec02fSPetter Reinholdtsen 	gettimeofday(&_start_keepalive, 0);
174c18ec02fSPetter Reinholdtsen 	return 0;
175c18ec02fSPetter Reinholdtsen }
176c18ec02fSPetter Reinholdtsen 
177c18ec02fSPetter Reinholdtsen static void
178c18ec02fSPetter Reinholdtsen print_escape_seq(struct ipmi_intf *intf)
179c18ec02fSPetter Reinholdtsen {
180c18ec02fSPetter Reinholdtsen 	lprintf(LOG_NOTICE,
181c18ec02fSPetter Reinholdtsen "       %c.  - terminate connection\n"
182c18ec02fSPetter Reinholdtsen "       %c^Z - suspend ipmitool\n"
183c18ec02fSPetter Reinholdtsen "       %c^X - suspend ipmitool, but don't restore tty on restart\n"
184c18ec02fSPetter Reinholdtsen "       %c?  - this message\n"
185c18ec02fSPetter Reinholdtsen "       %c%c  - send the escape character by typing it twice\n"
186c18ec02fSPetter Reinholdtsen "       (Note that escapes are only recognized immediately after newline.)",
187c18ec02fSPetter Reinholdtsen 			intf->session->sol_escape_char,
188c18ec02fSPetter Reinholdtsen 			intf->session->sol_escape_char,
189c18ec02fSPetter Reinholdtsen 			intf->session->sol_escape_char,
190c18ec02fSPetter Reinholdtsen 			intf->session->sol_escape_char,
191c18ec02fSPetter Reinholdtsen 			intf->session->sol_escape_char,
192c18ec02fSPetter Reinholdtsen 			intf->session->sol_escape_char);
193c18ec02fSPetter Reinholdtsen }
194c18ec02fSPetter Reinholdtsen 
195c18ec02fSPetter Reinholdtsen static int
196c18ec02fSPetter Reinholdtsen leave_raw_mode(void)
197c18ec02fSPetter Reinholdtsen {
198aefd2872SZdenek Styblik 	if (!_in_raw_mode) {
199c18ec02fSPetter Reinholdtsen 		return -1;
200aefd2872SZdenek Styblik 	} else if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1) {
201c18ec02fSPetter Reinholdtsen 		lperror(LOG_ERR, "tcsetattr(stdin)");
202aefd2872SZdenek Styblik 	} else if (tcsetattr(fileno(stdout), TCSADRAIN, &_saved_tio) == -1) {
203c18ec02fSPetter Reinholdtsen 		lperror(LOG_ERR, "tcsetattr(stdout)");
204aefd2872SZdenek Styblik 	} else {
205c18ec02fSPetter Reinholdtsen 		_in_raw_mode = 0;
206aefd2872SZdenek Styblik 	}
207c18ec02fSPetter Reinholdtsen 	return 0;
208c18ec02fSPetter Reinholdtsen }
209c18ec02fSPetter Reinholdtsen 
210c18ec02fSPetter Reinholdtsen static int
211c18ec02fSPetter Reinholdtsen enter_raw_mode(void)
212c18ec02fSPetter Reinholdtsen {
213c18ec02fSPetter Reinholdtsen 	struct termios tio;
214c18ec02fSPetter Reinholdtsen 	if (tcgetattr(fileno(stdout), &_saved_tio) < 0) {
215c18ec02fSPetter Reinholdtsen 		lperror(LOG_ERR, "tcgetattr failed");
216c18ec02fSPetter Reinholdtsen 		return -1;
217c18ec02fSPetter Reinholdtsen 	}
218c18ec02fSPetter Reinholdtsen 
219c18ec02fSPetter Reinholdtsen 	tio = _saved_tio;
220c18ec02fSPetter Reinholdtsen 	if (_altterm) {
221c18ec02fSPetter Reinholdtsen 		tio.c_iflag &= (ISTRIP | IGNBRK);
222c18ec02fSPetter Reinholdtsen 		tio.c_cflag &= ~(CSIZE | PARENB | IXON | IXOFF | IXANY);
223c18ec02fSPetter Reinholdtsen 		tio.c_cflag |= (CS8 |CREAD) | (IXON|IXOFF|IXANY);
224c18ec02fSPetter Reinholdtsen 		tio.c_lflag &= 0;
225c18ec02fSPetter Reinholdtsen 		tio.c_cc[VMIN] = 1;
226c18ec02fSPetter Reinholdtsen 		tio.c_cc[VTIME] = 0;
227c18ec02fSPetter Reinholdtsen 	} else {
228c18ec02fSPetter Reinholdtsen 		tio.c_iflag |= IGNPAR;
229c18ec02fSPetter Reinholdtsen 		tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
230c18ec02fSPetter Reinholdtsen 		tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL | IEXTEN);
231c18ec02fSPetter Reinholdtsen 		tio.c_oflag &= ~OPOST;
232c18ec02fSPetter Reinholdtsen 		tio.c_cc[VMIN] = 1;
233c18ec02fSPetter Reinholdtsen 		tio.c_cc[VTIME] = 0;
234c18ec02fSPetter Reinholdtsen 	}
235c18ec02fSPetter Reinholdtsen 
236aefd2872SZdenek Styblik 	if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0) {
237c18ec02fSPetter Reinholdtsen 		lperror(LOG_ERR, "tcsetattr(stdin)");
238aefd2872SZdenek Styblik 	} else if (tcsetattr(fileno(stdout), TCSADRAIN, &tio) < 0) {
239c18ec02fSPetter Reinholdtsen 		lperror(LOG_ERR, "tcsetattr(stdout)");
240aefd2872SZdenek Styblik 	} else {
241c18ec02fSPetter Reinholdtsen 		_in_raw_mode = 1;
242aefd2872SZdenek Styblik 	}
243c18ec02fSPetter Reinholdtsen 	return 0;
244c18ec02fSPetter Reinholdtsen }
245c18ec02fSPetter Reinholdtsen 
246c18ec02fSPetter Reinholdtsen static void
247c18ec02fSPetter Reinholdtsen suspend_self(int restore_tty)
248c18ec02fSPetter Reinholdtsen {
249c18ec02fSPetter Reinholdtsen 	leave_raw_mode();
250c18ec02fSPetter Reinholdtsen 	kill(getpid(), SIGTSTP);
251aefd2872SZdenek Styblik 	if (restore_tty) {
252c18ec02fSPetter Reinholdtsen 		enter_raw_mode();
253c18ec02fSPetter Reinholdtsen 	}
254aefd2872SZdenek Styblik }
255c18ec02fSPetter Reinholdtsen 
256c18ec02fSPetter Reinholdtsen static int
257c18ec02fSPetter Reinholdtsen do_inbuf_actions(struct ipmi_intf *intf, char *in_buff, int len)
258c18ec02fSPetter Reinholdtsen {
259c18ec02fSPetter Reinholdtsen 	static int in_esc = 0;
260c18ec02fSPetter Reinholdtsen 	static int last_was_cr = 1;
261c18ec02fSPetter Reinholdtsen 	int i;
262c18ec02fSPetter Reinholdtsen 
263c18ec02fSPetter Reinholdtsen 	for(i = 0; i < len ;) {
264c18ec02fSPetter Reinholdtsen 		if (!in_esc) {
265c18ec02fSPetter Reinholdtsen 			if (last_was_cr &&
266c18ec02fSPetter Reinholdtsen 					(in_buff[i] == intf->session->sol_escape_char)) {
267c18ec02fSPetter Reinholdtsen 				in_esc = 1;
268c18ec02fSPetter Reinholdtsen 				memmove(in_buff, in_buff + 1, len - i - 1);
269c18ec02fSPetter Reinholdtsen 				len--;
270c18ec02fSPetter Reinholdtsen 				continue;
271c18ec02fSPetter Reinholdtsen 			}
272c18ec02fSPetter Reinholdtsen 		}
273c18ec02fSPetter Reinholdtsen 		if (in_esc) {
274c18ec02fSPetter Reinholdtsen 			if (in_buff[i] == intf->session->sol_escape_char) {
275c18ec02fSPetter Reinholdtsen 				in_esc = 0;
276c18ec02fSPetter Reinholdtsen 				i++;
277c18ec02fSPetter Reinholdtsen 				continue;
278c18ec02fSPetter Reinholdtsen 			}
279c18ec02fSPetter Reinholdtsen 
280c18ec02fSPetter Reinholdtsen 			switch (in_buff[i]) {
281c18ec02fSPetter Reinholdtsen 			case '.':
282c18ec02fSPetter Reinholdtsen 				printf("%c. [terminated ipmitool]\n",
283c18ec02fSPetter Reinholdtsen 						intf->session->sol_escape_char);
284c18ec02fSPetter Reinholdtsen 				return -1;
285c18ec02fSPetter Reinholdtsen 			case 'Z' - 64:
286c18ec02fSPetter Reinholdtsen 				printf("%c^Z [suspend ipmitool]\n",
287c18ec02fSPetter Reinholdtsen 						intf->session->sol_escape_char);
288aefd2872SZdenek Styblik 				/* Restore tty back to raw */
289aefd2872SZdenek Styblik 				suspend_self(1);
290c18ec02fSPetter Reinholdtsen 				break;
291c18ec02fSPetter Reinholdtsen 			case 'X' - 64:
292c18ec02fSPetter Reinholdtsen 				printf("%c^X [suspend ipmitool]\n",
293c18ec02fSPetter Reinholdtsen 						intf->session->sol_escape_char);
294aefd2872SZdenek Styblik 				/* Don't restore to raw mode */
295aefd2872SZdenek Styblik 				suspend_self(0);
296c18ec02fSPetter Reinholdtsen 				break;
297c18ec02fSPetter Reinholdtsen 			case '?':
298c18ec02fSPetter Reinholdtsen 				printf("%c? [ipmitool help]\n",
299c18ec02fSPetter Reinholdtsen 						intf->session->sol_escape_char);
300c18ec02fSPetter Reinholdtsen 				print_escape_seq(intf);
301c18ec02fSPetter Reinholdtsen 				break;
302c18ec02fSPetter Reinholdtsen 			}
303c18ec02fSPetter Reinholdtsen 
304aefd2872SZdenek Styblik 			memmove(in_buff, (in_buff + 1), (len - i - 1));
305c18ec02fSPetter Reinholdtsen 			len--;
306c18ec02fSPetter Reinholdtsen 			in_esc = 0;
307c18ec02fSPetter Reinholdtsen 			continue;
308c18ec02fSPetter Reinholdtsen 		}
309c18ec02fSPetter Reinholdtsen 		last_was_cr = (in_buff[i] == '\r' || in_buff[i] == '\n');
310c18ec02fSPetter Reinholdtsen 		i++;
311c18ec02fSPetter Reinholdtsen 	}
312c18ec02fSPetter Reinholdtsen 	return len;
313c18ec02fSPetter Reinholdtsen }
314c18ec02fSPetter Reinholdtsen 
315c18ec02fSPetter Reinholdtsen 
316c18ec02fSPetter Reinholdtsen static void
317c18ec02fSPetter Reinholdtsen do_terminal_cleanup(void)
318c18ec02fSPetter Reinholdtsen {
319aefd2872SZdenek Styblik 	if (_saved_winsize.ws_row > 0 && _saved_winsize.ws_col > 0) {
320c18ec02fSPetter Reinholdtsen 		ioctl(fileno(stdout), TIOCSWINSZ, &_saved_winsize);
321aefd2872SZdenek Styblik 	}
322c18ec02fSPetter Reinholdtsen 	leave_raw_mode();
323aefd2872SZdenek Styblik 	if (errno) {
324c18ec02fSPetter Reinholdtsen 		lprintf(LOG_ERR, "Exiting due to error %d -> %s",
325c18ec02fSPetter Reinholdtsen 			errno, strerror(errno));
326c18ec02fSPetter Reinholdtsen 	}
327aefd2872SZdenek Styblik }
328c18ec02fSPetter Reinholdtsen 
329c18ec02fSPetter Reinholdtsen static void
330c18ec02fSPetter Reinholdtsen set_terminal_size(int rows, int cols)
331c18ec02fSPetter Reinholdtsen {
332c18ec02fSPetter Reinholdtsen 	struct winsize winsize;
333aefd2872SZdenek Styblik 	if (rows <= 0 || cols <= 0) {
334c18ec02fSPetter Reinholdtsen 		return;
335aefd2872SZdenek Styblik 	}
336c18ec02fSPetter Reinholdtsen 	/* save initial winsize */
337c18ec02fSPetter Reinholdtsen 	ioctl(fileno(stdout), TIOCGWINSZ, &_saved_winsize);
338c18ec02fSPetter Reinholdtsen 	/* set new winsize */
339c18ec02fSPetter Reinholdtsen 	winsize.ws_row = rows;
340c18ec02fSPetter Reinholdtsen 	winsize.ws_col = cols;
341c18ec02fSPetter Reinholdtsen 	ioctl(fileno(stdout), TIOCSWINSZ, &winsize);
342c18ec02fSPetter Reinholdtsen }
343c18ec02fSPetter Reinholdtsen 
344c18ec02fSPetter Reinholdtsen static void
345c18ec02fSPetter Reinholdtsen print_tsol_usage(void)
346c18ec02fSPetter Reinholdtsen {
347c18ec02fSPetter Reinholdtsen 	struct winsize winsize;
348aefd2872SZdenek Styblik 	lprintf(LOG_NOTICE,
349aefd2872SZdenek Styblik "Usage: tsol [recvip] [port=NUM] [ro|rw] [rows=NUM] [cols=NUM] [altterm]");
350aefd2872SZdenek Styblik 	lprintf(LOG_NOTICE,
351aefd2872SZdenek Styblik "       recvip       Receiver IP Address             [default=local]");
352aefd2872SZdenek Styblik 	lprintf(LOG_NOTICE,
353aefd2872SZdenek Styblik "       port=NUM     Receiver UDP Port               [default=%d]",
354c18ec02fSPetter Reinholdtsen 			IPMI_TSOL_DEF_PORT);
355aefd2872SZdenek Styblik 	lprintf(LOG_NOTICE,
356aefd2872SZdenek Styblik "       ro|rw        Set Read-Only or Read-Write     [default=rw]");
357c18ec02fSPetter Reinholdtsen 			ioctl(fileno(stdout), TIOCGWINSZ, &winsize);
358aefd2872SZdenek Styblik 	lprintf(LOG_NOTICE,
359aefd2872SZdenek Styblik "       rows=NUM     Set terminal rows               [default=%d]",
360c18ec02fSPetter Reinholdtsen 			winsize.ws_row);
361aefd2872SZdenek Styblik 	lprintf(LOG_NOTICE,
362aefd2872SZdenek Styblik "       cols=NUM     Set terminal columns            [default=%d]",
363c18ec02fSPetter Reinholdtsen 			winsize.ws_col);
364aefd2872SZdenek Styblik 	lprintf(LOG_NOTICE,
365aefd2872SZdenek Styblik "       altterm      Alternate terminal setup        [default=off]");
366c18ec02fSPetter Reinholdtsen }
367c18ec02fSPetter Reinholdtsen 
368c18ec02fSPetter Reinholdtsen int
369c18ec02fSPetter Reinholdtsen ipmi_tsol_main(struct ipmi_intf *intf, int argc, char **argv)
370c18ec02fSPetter Reinholdtsen {
371c18ec02fSPetter Reinholdtsen 	struct pollfd fds_wait[3], fds_data_wait[3], *fds;
372c18ec02fSPetter Reinholdtsen 	struct sockaddr_in sin, myaddr, *sa_in;
373c18ec02fSPetter Reinholdtsen 	socklen_t mylen;
374c18ec02fSPetter Reinholdtsen 	char *recvip = NULL;
375*d79b0e05SZdenek Styblik 	char in_buff[IPMI_BUF_SIZE];
376*d79b0e05SZdenek Styblik 	char out_buff[IPMI_BUF_SIZE * 8];
377c18ec02fSPetter Reinholdtsen 	char buff[IPMI_BUF_SIZE + 4];
378c18ec02fSPetter Reinholdtsen 	int fd_socket, result, i;
379c18ec02fSPetter Reinholdtsen 	int out_buff_fill, in_buff_fill;
380c18ec02fSPetter Reinholdtsen 	int ip1, ip2, ip3, ip4;
381c18ec02fSPetter Reinholdtsen 	int read_only = 0, rows = 0, cols = 0;
382c18ec02fSPetter Reinholdtsen 	int port = IPMI_TSOL_DEF_PORT;
383c18ec02fSPetter Reinholdtsen 
384c18ec02fSPetter Reinholdtsen 	if (strlen(intf->name) < 3 || strncmp(intf->name, "lan", 3) != 0) {
385c18ec02fSPetter Reinholdtsen 		lprintf(LOG_ERR, "Error: Tyan SOL is only available over lan interface");
386aefd2872SZdenek Styblik 		return (-1);
387c18ec02fSPetter Reinholdtsen 	}
388c18ec02fSPetter Reinholdtsen 
389c18ec02fSPetter Reinholdtsen 	for (i = 0; i<argc; i++) {
390c18ec02fSPetter Reinholdtsen 		if (sscanf(argv[i], "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) == 4) {
391c18ec02fSPetter Reinholdtsen 			/* not free'd ...*/
392c18ec02fSPetter Reinholdtsen 			/* recvip = strdup(argv[i]); */
393c18ec02fSPetter Reinholdtsen 			recvip = argv[i];
394aefd2872SZdenek Styblik 		} else if (sscanf(argv[i], "port=%d", &ip1) == 1) {
395c18ec02fSPetter Reinholdtsen 			port = ip1;
396aefd2872SZdenek Styblik 		} else if (sscanf(argv[i], "rows=%d", &ip1) == 1) {
397c18ec02fSPetter Reinholdtsen 			rows = ip1;
398aefd2872SZdenek Styblik 		} else if (sscanf(argv[i], "cols=%d", &ip1) == 1) {
399c18ec02fSPetter Reinholdtsen 			cols = ip1;
400aefd2872SZdenek Styblik 		} else if (strlen(argv[i]) == 2
401aefd2872SZdenek Styblik 				&& strncmp(argv[i], "ro", 2) == 0) {
402c18ec02fSPetter Reinholdtsen 			read_only = 1;
403aefd2872SZdenek Styblik 		} else if (strlen(argv[i]) == 2
404aefd2872SZdenek Styblik 				&& strncmp(argv[i], "rw", 2) == 0) {
405c18ec02fSPetter Reinholdtsen 			read_only = 0;
406aefd2872SZdenek Styblik 		} else if (strlen(argv[i]) == 7
407aefd2872SZdenek Styblik 				&& strncmp(argv[i], "altterm", 7) == 0) {
408c18ec02fSPetter Reinholdtsen 			_altterm = 1;
409aefd2872SZdenek Styblik 		} else if (strlen(argv[i]) == 4
410aefd2872SZdenek Styblik 				&& strncmp(argv[i], "help", 4) == 0) {
411c18ec02fSPetter Reinholdtsen 			print_tsol_usage();
412c18ec02fSPetter Reinholdtsen 			return 0;
413aefd2872SZdenek Styblik 		} else {
414c18ec02fSPetter Reinholdtsen 			print_tsol_usage();
415c18ec02fSPetter Reinholdtsen 			return 0;
416c18ec02fSPetter Reinholdtsen 		}
417c18ec02fSPetter Reinholdtsen 	}
418c18ec02fSPetter Reinholdtsen 
419c18ec02fSPetter Reinholdtsen 	/* create udp socket to receive the packet */
420c18ec02fSPetter Reinholdtsen 	memset(&sin, 0, sizeof(sin));
421c18ec02fSPetter Reinholdtsen 	sin.sin_family = AF_INET;
422c18ec02fSPetter Reinholdtsen 	sin.sin_port = htons(port);
423c18ec02fSPetter Reinholdtsen 
424c18ec02fSPetter Reinholdtsen 	sa_in = (struct sockaddr_in *)&intf->session->addr;
425c18ec02fSPetter Reinholdtsen 	result = inet_pton(AF_INET, (const char *)intf->session->hostname,
426c18ec02fSPetter Reinholdtsen 			&sa_in->sin_addr);
427c18ec02fSPetter Reinholdtsen 
428c18ec02fSPetter Reinholdtsen 	if (result <= 0) {
429c18ec02fSPetter Reinholdtsen 		struct hostent *host = gethostbyname((const char *)intf->session->hostname);
430c18ec02fSPetter Reinholdtsen 		if (host == NULL ) {
431c18ec02fSPetter Reinholdtsen 			lprintf(LOG_ERR, "Address lookup for %s failed",
432c18ec02fSPetter Reinholdtsen 				intf->session->hostname);
433c18ec02fSPetter Reinholdtsen 			return -1;
434c18ec02fSPetter Reinholdtsen 		}
435c18ec02fSPetter Reinholdtsen 		if (host->h_addrtype != AF_INET) {
436c18ec02fSPetter Reinholdtsen 			lprintf(LOG_ERR,
437c18ec02fSPetter Reinholdtsen 					"Address lookup for %s failed. Got %s, expected IPv4 address.",
438c18ec02fSPetter Reinholdtsen 					intf->session->hostname,
439c18ec02fSPetter Reinholdtsen 					(host->h_addrtype == AF_INET6) ? "IPv6" : "Unknown");
440c18ec02fSPetter Reinholdtsen 			return (-1);
441c18ec02fSPetter Reinholdtsen 		}
442c18ec02fSPetter Reinholdtsen 		sa_in->sin_family = host->h_addrtype;
443c18ec02fSPetter Reinholdtsen 		memcpy(&sa_in->sin_addr, host->h_addr, host->h_length);
444c18ec02fSPetter Reinholdtsen 	}
445c18ec02fSPetter Reinholdtsen 
446c18ec02fSPetter Reinholdtsen 	fd_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
447c18ec02fSPetter Reinholdtsen 	if (fd_socket < 0) {
448c18ec02fSPetter Reinholdtsen 		lprintf(LOG_ERR, "Can't open port %d", port);
449c18ec02fSPetter Reinholdtsen 		return -1;
450c18ec02fSPetter Reinholdtsen 	}
451aefd2872SZdenek Styblik 	if (bind(fd_socket, (struct sockaddr *)&sin, sizeof(sin)) == (-1)) {
452c18ec02fSPetter Reinholdtsen 		lprintf(LOG_ERR, "Failed to bind socket.");
453c18ec02fSPetter Reinholdtsen 		close(fd_socket);
454c18ec02fSPetter Reinholdtsen 		return -1;
455c18ec02fSPetter Reinholdtsen 	}
456c18ec02fSPetter Reinholdtsen 
457c18ec02fSPetter Reinholdtsen 	/*
458c18ec02fSPetter Reinholdtsen 	 * retrieve local IP address if not supplied on command line
459c18ec02fSPetter Reinholdtsen 	 */
460c18ec02fSPetter Reinholdtsen 	if (recvip == NULL) {
461aefd2872SZdenek Styblik 		/* must connect first */
462aefd2872SZdenek Styblik 		result = intf->open(intf);
463c18ec02fSPetter Reinholdtsen 		if (result < 0) {
464c18ec02fSPetter Reinholdtsen 			close(fd_socket);
465c18ec02fSPetter Reinholdtsen 			return -1;
466c18ec02fSPetter Reinholdtsen 		}
467c18ec02fSPetter Reinholdtsen 
468c18ec02fSPetter Reinholdtsen 		mylen = sizeof(myaddr);
469c18ec02fSPetter Reinholdtsen 		if (getsockname(intf->fd, (struct sockaddr *)&myaddr, &mylen) < 0) {
470c18ec02fSPetter Reinholdtsen 			lperror(LOG_ERR, "getsockname failed");
471c18ec02fSPetter Reinholdtsen 			close(fd_socket);
472c18ec02fSPetter Reinholdtsen 			return -1;
473c18ec02fSPetter Reinholdtsen 		}
474c18ec02fSPetter Reinholdtsen 
475c18ec02fSPetter Reinholdtsen 		recvip = inet_ntoa(myaddr.sin_addr);
476c18ec02fSPetter Reinholdtsen 		if (recvip == NULL) {
477c18ec02fSPetter Reinholdtsen 			lprintf(LOG_ERR, "Unable to find local IP address");
478c18ec02fSPetter Reinholdtsen 			close(fd_socket);
479c18ec02fSPetter Reinholdtsen 			return -1;
480c18ec02fSPetter Reinholdtsen 		}
481c18ec02fSPetter Reinholdtsen 	}
482c18ec02fSPetter Reinholdtsen 
483c18ec02fSPetter Reinholdtsen 	printf("[Starting %sSOL with receiving address %s:%d]\n",
484c18ec02fSPetter Reinholdtsen 			read_only ? "Read-only " : "", recvip, port);
485c18ec02fSPetter Reinholdtsen 
486c18ec02fSPetter Reinholdtsen 	set_terminal_size(rows, cols);
487c18ec02fSPetter Reinholdtsen 	enter_raw_mode();
488c18ec02fSPetter Reinholdtsen 
489c18ec02fSPetter Reinholdtsen 	/*
490c18ec02fSPetter Reinholdtsen 	 * talk to smdc to start Console redirect - IP address and port as parameter
491c18ec02fSPetter Reinholdtsen 	 * ipmitool -I lan -H 192.168.168.227 -U Administrator raw 0x30 0x06 0xC0 0xA8 0xA8 0x78 0x1A 0x0A
492c18ec02fSPetter Reinholdtsen 	 */
493c18ec02fSPetter Reinholdtsen 	result = ipmi_tsol_start(intf, recvip, port);
494c18ec02fSPetter Reinholdtsen 	if (result < 0) {
495c18ec02fSPetter Reinholdtsen 		lprintf(LOG_ERR, "Error starting SOL");
496c18ec02fSPetter Reinholdtsen 		close(fd_socket);
497aefd2872SZdenek Styblik 		return (-1);
498c18ec02fSPetter Reinholdtsen 	}
499c18ec02fSPetter Reinholdtsen 
500c18ec02fSPetter Reinholdtsen 	printf("[SOL Session operational.  Use %c? for help]\n",
501c18ec02fSPetter Reinholdtsen 			intf->session->sol_escape_char);
502c18ec02fSPetter Reinholdtsen 
503c18ec02fSPetter Reinholdtsen 	gettimeofday(&_start_keepalive, 0);
504c18ec02fSPetter Reinholdtsen 
505c18ec02fSPetter Reinholdtsen 	fds_wait[0].fd = fd_socket;
506c18ec02fSPetter Reinholdtsen 	fds_wait[0].events = POLLIN;
507c18ec02fSPetter Reinholdtsen 	fds_wait[0].revents = 0;
508c18ec02fSPetter Reinholdtsen 	fds_wait[1].fd = fileno(stdin);
509c18ec02fSPetter Reinholdtsen 	fds_wait[1].events = POLLIN;
510c18ec02fSPetter Reinholdtsen 	fds_wait[1].revents = 0;
511c18ec02fSPetter Reinholdtsen 	fds_wait[2].fd = -1;
512c18ec02fSPetter Reinholdtsen 	fds_wait[2].events = 0;
513c18ec02fSPetter Reinholdtsen 	fds_wait[2].revents = 0;
514c18ec02fSPetter Reinholdtsen 
515c18ec02fSPetter Reinholdtsen 	fds_data_wait[0].fd = fd_socket;
516c18ec02fSPetter Reinholdtsen 	fds_data_wait[0].events = POLLIN | POLLOUT;
517c18ec02fSPetter Reinholdtsen 	fds_data_wait[0].revents = 0;
518c18ec02fSPetter Reinholdtsen 	fds_data_wait[1].fd = fileno(stdin);
519c18ec02fSPetter Reinholdtsen 	fds_data_wait[1].events = POLLIN;
520c18ec02fSPetter Reinholdtsen 	fds_data_wait[1].revents = 0;
521c18ec02fSPetter Reinholdtsen 	fds_data_wait[2].fd = fileno(stdout);
522c18ec02fSPetter Reinholdtsen 	fds_data_wait[2].events = POLLOUT;
523c18ec02fSPetter Reinholdtsen 	fds_data_wait[2].revents = 0;
524c18ec02fSPetter Reinholdtsen 
525c18ec02fSPetter Reinholdtsen 	out_buff_fill = 0;
526c18ec02fSPetter Reinholdtsen 	in_buff_fill = 0;
527c18ec02fSPetter Reinholdtsen 	fds = fds_wait;
528c18ec02fSPetter Reinholdtsen 	for (;;) {
529c18ec02fSPetter Reinholdtsen 		result = poll(fds, 3, 15 * 1000);
530aefd2872SZdenek Styblik 		if (result < 0) {
531c18ec02fSPetter Reinholdtsen 			break;
532aefd2872SZdenek Styblik 		}
533c18ec02fSPetter Reinholdtsen 
534c18ec02fSPetter Reinholdtsen 		/* send keepalive packet */
535c18ec02fSPetter Reinholdtsen 		tsol_keepalive(intf);
536c18ec02fSPetter Reinholdtsen 
537c18ec02fSPetter Reinholdtsen 		if ((fds[0].revents & POLLIN) && (sizeof(out_buff) > out_buff_fill)) {
538c18ec02fSPetter Reinholdtsen 			socklen_t sin_len = sizeof(sin);
539*d79b0e05SZdenek Styblik 			int buff_size = sizeof(buff);
540*d79b0e05SZdenek Styblik 			if ((sizeof(out_buff) - out_buff_fill + 4) < buff_size) {
541*d79b0e05SZdenek Styblik 				buff_size = (sizeof(out_buff) - out_buff_fill) + 4;
542*d79b0e05SZdenek Styblik 				if ((buff_size - 4) <= 0) {
543*d79b0e05SZdenek Styblik 					buff_size = 0;
544*d79b0e05SZdenek Styblik 				}
545*d79b0e05SZdenek Styblik 			}
546aefd2872SZdenek Styblik 			result = recvfrom(fd_socket, buff,
547*d79b0e05SZdenek Styblik 					buff_size, 0,
548c18ec02fSPetter Reinholdtsen 					(struct sockaddr *)&sin, &sin_len);
549aefd2872SZdenek Styblik 			/* read the data from udp socket,
550aefd2872SZdenek Styblik 			 * skip some bytes in the head
551aefd2872SZdenek Styblik 			 */
552c18ec02fSPetter Reinholdtsen 			if ((result - 4) > 0) {
553c18ec02fSPetter Reinholdtsen 				int length = result - 4;
554c18ec02fSPetter Reinholdtsen 				memcpy(out_buff + out_buff_fill, buff + 4, length);
555c18ec02fSPetter Reinholdtsen 				out_buff_fill += length;
556c18ec02fSPetter Reinholdtsen 			}
557c18ec02fSPetter Reinholdtsen 		}
558c18ec02fSPetter Reinholdtsen 		if ((fds[1].revents & POLLIN) && (sizeof(in_buff) > in_buff_fill)) {
559aefd2872SZdenek Styblik 			/* Read from keyboard */
560c18ec02fSPetter Reinholdtsen 			result = read(fileno(stdin), in_buff + in_buff_fill,
561aefd2872SZdenek Styblik 					sizeof(in_buff) - in_buff_fill);
562c18ec02fSPetter Reinholdtsen 			if (result > 0) {
563c18ec02fSPetter Reinholdtsen 				int bytes;
564aefd2872SZdenek Styblik 				bytes = do_inbuf_actions(intf,
565aefd2872SZdenek Styblik 						in_buff + in_buff_fill, result);
566c18ec02fSPetter Reinholdtsen 				if (bytes < 0) {
567c18ec02fSPetter Reinholdtsen 					result = ipmi_tsol_stop(intf, recvip, port);
568c18ec02fSPetter Reinholdtsen 					do_terminal_cleanup();
569c18ec02fSPetter Reinholdtsen 					return result;
570c18ec02fSPetter Reinholdtsen 				}
571aefd2872SZdenek Styblik 				if (read_only) {
572c18ec02fSPetter Reinholdtsen 					bytes = 0;
573aefd2872SZdenek Styblik 				}
574c18ec02fSPetter Reinholdtsen 				in_buff_fill += bytes;
575c18ec02fSPetter Reinholdtsen 			}
576c18ec02fSPetter Reinholdtsen 		}
577c18ec02fSPetter Reinholdtsen 		if ((fds[2].revents & POLLOUT) && out_buff_fill) {
578aefd2872SZdenek Styblik 			/* To screen */
579aefd2872SZdenek Styblik 			result = write(fileno(stdout), out_buff, out_buff_fill);
580c18ec02fSPetter Reinholdtsen 			if (result > 0) {
581c18ec02fSPetter Reinholdtsen 				out_buff_fill -= result;
582c18ec02fSPetter Reinholdtsen 				if (out_buff_fill) {
583c18ec02fSPetter Reinholdtsen 					memmove(out_buff, out_buff + result, out_buff_fill);
584c18ec02fSPetter Reinholdtsen 				}
585c18ec02fSPetter Reinholdtsen 			}
586c18ec02fSPetter Reinholdtsen 		}
587c18ec02fSPetter Reinholdtsen 		if ((fds[0].revents & POLLOUT) && in_buff_fill) {
588c18ec02fSPetter Reinholdtsen 			/*
589c18ec02fSPetter Reinholdtsen 			 * translate key and send that to SMDC using IPMI
590c18ec02fSPetter Reinholdtsen 			 * ipmitool -I lan -H 192.168.168.227 -U Administrator raw 0x30 0x03 0x04 0x1B 0x5B 0x43
591c18ec02fSPetter Reinholdtsen 			 */
592aefd2872SZdenek Styblik 			result = ipmi_tsol_send_keystroke(intf,
593aefd2872SZdenek Styblik 					in_buff, __min(in_buff_fill, 14));
594c18ec02fSPetter Reinholdtsen 			if (result > 0) {
595c18ec02fSPetter Reinholdtsen 				gettimeofday(&_start_keepalive, 0);
596c18ec02fSPetter Reinholdtsen 				in_buff_fill -= result;
597c18ec02fSPetter Reinholdtsen 				if (in_buff_fill) {
598c18ec02fSPetter Reinholdtsen 					memmove(in_buff, in_buff + result, in_buff_fill);
599c18ec02fSPetter Reinholdtsen 				}
600c18ec02fSPetter Reinholdtsen 			}
601c18ec02fSPetter Reinholdtsen 		}
602c18ec02fSPetter Reinholdtsen 		fds = (in_buff_fill || out_buff_fill )?
603c18ec02fSPetter Reinholdtsen 			fds_data_wait : fds_wait;
604c18ec02fSPetter Reinholdtsen 	}
605c18ec02fSPetter Reinholdtsen 	return 0;
606c18ec02fSPetter Reinholdtsen }
607