trace_tcp.c 5.03 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
	trace tcp connection handling

	Copyright Jeroen Vreeken (jeroen@vreeken.net), 2013

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

21
#include <trace/trace_def.h>
22
23
#include <trace/trace.h>
#include <log/log.h>
24
#include <utils/tcp_connect.h>
25
26

#include <unistd.h>
27
#include <fcntl.h>
28
29
30
31
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
32
#include <stdio.h>
33
34
35

#define TRACE_BUF_BLOCK	4096

Jeroen Vreeken's avatar
Jeroen Vreeken committed
36
37
38
39
40
struct trace *trace_alloc(void)
{
	return calloc(sizeof(struct trace), 1);
}

41
42
43
44
45
46
47
48
49
50
struct trace *trace_open(char *host, int port)
{
	struct trace *trace;
	
	trace = calloc(sizeof(struct trace), 1);
	if (!trace)
		return NULL;

	trace->host = strdup(host);
	trace->port = port;
51
	trace->fd = -1;
52
	
53
54
	trace_connect(trace);

55
56
57
	return trace;
}

58
void trace_initialize_fd(struct trace *trace, int fd)
59
60
{
	trace->fd = fd;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
61
	trace->rx_len = 0;
62
	
63
	if (trace->fd >= 0) {
64
65
66
67
68
69
70
71
72
73
74
		int flags = fcntl(trace->fd, F_GETFL, 0);
		fcntl(trace->fd, F_SETFL, flags | O_NONBLOCK);
	}
}

int trace_connect(struct trace *trace)
{
	int fd = tcp_connect(trace->host, trace->port);
	if (fd >= 0) {
		trace_initialize_fd(trace, fd);
		return 0;
75
	}
76
	return -1;
77
78
79
80
}

void trace_close(struct trace *trace)
{
Jeroen Vreeken's avatar
Jeroen Vreeken committed
81
	log_send(LOG_T_DEBUG, "Close trace on request");
82
83
	if (trace->fd >= 0)
		close(trace->fd);
84
	trace->fd = -1;	
Jeroen Vreeken's avatar
Jeroen Vreeken committed
85
	trace->recover = false;
86
87
88
89
90
91
}

void trace_free(struct trace *trace)
{
	trace_close(trace);

Jeroen Vreeken's avatar
Jeroen Vreeken committed
92
	free(trace->name);
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
	free(trace->rx_buffer);
	free(trace->host);
	free(trace);
}

int trace_fd_get(struct trace *trace)
{
	return trace->fd;
}

bool trace_fd_read(struct trace *trace)
{
	ssize_t ret;
	int i;
	bool rx_ready = false;

Jeroen Vreeken's avatar
Jeroen Vreeken committed
109
	if (trace->rx_buf_size - trace->rx_len < TRACE_BUF_BLOCK) {
110
		unsigned char *newbuf;
111
112
113
114
		
		newbuf = realloc(trace->rx_buffer, trace->rx_buf_size +	TRACE_BUF_BLOCK);
		if (newbuf) {
			trace->rx_buffer = newbuf;
115
			trace->rx_buf_size += TRACE_BUF_BLOCK;
116
117
118
119
120
121
122
123
124
125
126
127
		} else {
			log_send(LOG_T_WARNING, 
			    "Could not enlarge buffer for receiving traces");
			return false;
		}
	}
	
	ret = read(trace->fd, trace->rx_buffer + trace->rx_len, TRACE_BUF_BLOCK);

	if (ret > 0) {
		trace->rx_len += ret;
	}
128

129
130
131
132
133
	for (i = 0; i < trace->rx_len; i++) {
		if (trace->rx_buffer[i] == TRACE_END)
			rx_ready = true;
	}
	
Jeroen Vreeken's avatar
Jeroen Vreeken committed
134
135
	if (((ret < 0 && errno != EAGAIN) || ret == 0) && !rx_ready) {
		log_send(LOG_T_DEBUG, "error during read, closing");
136
137
		close(trace->fd);
		trace->fd = -1;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
138
139
140
		
		if (trace->handler_close)
			trace->handler_close(trace);
141
142
	}
	
143
144
145
146
147
148
	return rx_ready;
}

struct trace_pkt *trace_packet_get(struct trace *trace)
{
	int i;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
149
	size_t len = 0, buflen;
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
	struct trace_pkt *pkt;
	
	while(trace->rx_len && trace->rx_buffer[0] == TRACE_END) {
		memmove(trace->rx_buffer, trace->rx_buffer+1, trace->rx_len-1);
		trace->rx_len--;
	}

	for (i = 0; i < trace->rx_len; i++) {
		if (trace->rx_buffer[i] == TRACE_END) {
			len = i;
			break;
		}
	}
	
	if (!len) {
		return NULL;
	}
	
Jeroen Vreeken's avatar
Jeroen Vreeken committed
168
169
170
	buflen = len;
	
	pkt = trace_packet_new();
171
172
	if (!pkt)
		goto err_pkt;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
173
	if (trace_packet_resize(pkt, len))
174
175
176
177
178
179
180
181
182
		goto err_pkt_data;

	memcpy(pkt->data, trace->rx_buffer, len);
	pkt->type = pkt->data[0];

	for (i = 0; i < len - 1; i++) {
		if (pkt->data[i] == TRACE_ESC) {
			if (pkt->data[i+1] == TRACE_ESC_END)
				pkt->data[i] = TRACE_END;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
183
			memmove(&pkt->data[i+1], &pkt->data[i+2], len - i - 2);
184
185
186
187
188
			len--;
		}
	}
	pkt->len = len;
	
Jeroen Vreeken's avatar
Jeroen Vreeken committed
189
190
	trace->rx_len -= buflen + 1;
	memmove(trace->rx_buffer, trace->rx_buffer + buflen + 1, trace->rx_len);
191
192
193
194
195
196
197
198
199
200
201
202
203
	
	return pkt;

err_pkt_data:
	free(pkt);
err_pkt:
	return NULL;
}

int trace_packet_write(struct trace *trace, struct trace_pkt *pkt)
{
	int i;
	int start = 0;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
204
	ssize_t r;
205
	
206
207
208
	if (!pkt->len)
		return 0;
	
209
210
	while (start < pkt->len) for (i = start; i < pkt->len; i++) {
		if (pkt->data[i] == TRACE_ESC || pkt->data[i] == TRACE_END) {
211
			unsigned char *seq;
212
			
Jeroen Vreeken's avatar
Jeroen Vreeken committed
213
			r = write(trace->fd, pkt->data + start, i - start);
214
			if (r != i - start)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
215
216
				goto err_write;
			
217
218
			start = i + 1;
			if (pkt->data[i] == TRACE_ESC)
219
				seq = (unsigned char[2]){ TRACE_ESC, TRACE_ESC_ESC };
220
			else
221
				seq = (unsigned char[2]){ TRACE_ESC, TRACE_ESC_END };
Jeroen Vreeken's avatar
Jeroen Vreeken committed
222
			r = write(trace->fd, seq, 2);
223
			if (r != 2)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
224
				goto err_write;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
225
		} else if (i == pkt->len -1) {
Jeroen Vreeken's avatar
Jeroen Vreeken committed
226
			r = write(trace->fd, pkt->data + start, i - start + 1);
227
			if (r != i - start + 1)
Jeroen Vreeken's avatar
Jeroen Vreeken committed
228
				goto err_write;
229
230
			start = i + 1;
		}
231
	}
Jeroen Vreeken's avatar
Jeroen Vreeken committed
232
233
234
	r = write(trace->fd, &(char[1]){ TRACE_END }, 1);
	if (r < 0)
		goto err_write;
235
236
	
	return 0;
Jeroen Vreeken's avatar
Jeroen Vreeken committed
237
238
239
240
241
242
243
244
245
246

err_write:
	log_send(LOG_T_DEBUG, "Error during write, closing %d", trace->fd);
	if (trace->fd >= 0)
		close(trace->fd);
	trace->fd = -1;
		
	if (trace->handler_close)
		trace->handler_close(trace);
	return -1;
247
}