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