virtagent: Makefile fixups
[qemu/mdroth.git] / virtagent-common.c
blob8c4dcd4b78ac07f95ab8e8ed467529ac9e85b906
1 /*
2 * virt-agent - common host/guest RPC functions
4 * Copyright IBM Corp. 2010
6 * Authors:
7 * Adam Litke <aglitke@linux.vnet.ibm.com>
8 * Michael Roth <mdroth@linux.vnet.ibm.com>
10 * This work is licensed under the terms of the GNU GPL, version 2 or later.
11 * See the COPYING file in the top-level directory.
15 #include "virtagent-common.h"
17 #define VA_READ true
18 #define VA_SEND false
20 enum va_rpc_type {
21 VA_RPC_REQUEST,
22 VA_RPC_RESPONSE,
25 typedef struct VARPCState {
26 char hdr[VA_HDR_LEN_MAX];
27 int fd;
28 size_t hdr_len;
29 size_t hdr_pos;
30 enum {
31 VA_READ_START,
32 VA_READ_HDR,
33 VA_READ_BODY,
34 VA_SEND_START,
35 VA_SEND_HDR,
36 VA_SEND_BODY,
37 } state;
38 enum va_rpc_type rpc_type;
39 char *content;
40 size_t content_len;
41 size_t content_pos;
42 VARPCData *data;
43 } VARPCState;
45 static void va_rpc_read_handler(void *opaque);
46 static void va_rpc_send_handler(void *opaque);
48 static int end_of_header(char *buf, int end_pos)
50 return !strncmp(buf+(end_pos-2), "\n\r\n", 3);
53 static void va_rpc_hdr_init(VARPCState *s) {
54 const char *preamble;
56 TRACE("called");
57 /* essentially ignored in the context of virtagent, but might as well */
58 if (s->rpc_type == VA_RPC_REQUEST) {
59 preamble = "POST /RPC2 HTTP/1.1";
60 } else if (s->rpc_type == VA_RPC_RESPONSE) {
61 preamble = "HTTP/1.1 200 OK";
62 } else {
63 s->hdr_len = 0;
64 return;
67 s->hdr_len = sprintf(s->hdr,
68 "%s" EOL
69 "Content-Type: text/xml" EOL
70 "Content-Length: %u" EOL EOL,
71 preamble,
72 (uint32_t)s->content_len);
75 static void va_rpc_parse_hdr(VARPCState *s)
77 int i, line_pos = 0;
78 char line_buf[4096];
80 for (i = 0; i < VA_HDR_LEN_MAX; ++i) {
81 if (s->hdr[i] != '\n') {
82 /* read line */
83 line_buf[line_pos++] = s->hdr[i];
84 } else {
85 /* process line */
86 if (strncmp(line_buf, "Content-Length: ", 16) == 0) {
87 s->content_len = atoi(&line_buf[16]);
88 return;
90 line_pos = 0;
95 static VARPCState *va_rpc_state_new(VARPCData *data, int fd,
96 enum va_rpc_type rpc_type, bool read)
98 VARPCState *s = qemu_mallocz(sizeof(VARPCState));
100 s->rpc_type = rpc_type;
101 s->fd = fd;
102 s->data = data;
103 if (s->data == NULL) {
104 goto EXIT_BAD;
107 if (read) {
108 s->state = VA_READ_START;
109 s->content = NULL;
110 } else {
111 s->state = VA_SEND_START;
112 if (rpc_type == VA_RPC_REQUEST) {
113 s->content = XMLRPC_MEMBLOCK_CONTENTS(char, s->data->send_req_xml);
114 s->content_len = XMLRPC_MEMBLOCK_SIZE(char, s->data->send_req_xml);
115 } else if (rpc_type == VA_RPC_RESPONSE) {
116 s->content = XMLRPC_MEMBLOCK_CONTENTS(char, s->data->send_resp_xml);
117 s->content_len = XMLRPC_MEMBLOCK_SIZE(char, s->data->send_resp_xml);
118 } else {
119 LOG("unknown rcp type");
120 goto EXIT_BAD;
122 va_rpc_hdr_init(s);
123 if (s->hdr_len == 0) {
124 LOG("failed to initialize http header");
125 goto EXIT_BAD;
129 return s;
130 EXIT_BAD:
131 qemu_free(s);
132 return NULL;
135 /* called by va_rpc_read_handler after reading requests */
136 static int va_rpc_send_response(VARPCData *data, int fd)
138 VARPCState *s = va_rpc_state_new(data, fd, VA_RPC_RESPONSE, VA_SEND);
140 TRACE("called");
141 if (s == NULL) {
142 LOG("failed to set up RPC state");
143 return -1;
145 TRACE("setting up send handler for RPC request");
146 vp_set_fd_handler(fd, NULL, va_rpc_send_handler, s);
148 return 0;
151 static void va_rpc_read_handler_completion(VARPCState *s) {
152 int ret;
154 if (s->rpc_type == VA_RPC_REQUEST) {
155 /* server read request, call it's cb function then set up
156 * a send handler for the rpc response if there weren't any
157 * communication errors
159 if (s->data->cb) {
160 s->data->cb(s->data);
162 if (s->data->status == VA_RPC_STATUS_OK) {
163 ret = va_rpc_send_response(s->data, s->fd);
164 if (ret != 0) {
165 LOG("error setting up send handler for rpc response");
167 } else {
168 LOG("error reading rpc request, skipping response");
169 vp_set_fd_handler(s->fd, NULL, NULL, NULL);
170 closesocket(s->fd);
171 qemu_free(s->data);
173 } else if (s->rpc_type == VA_RPC_RESPONSE) {
174 /* client read response, call it's cb function and complete
175 * the RPC
177 if (s->data->cb) {
178 s->data->cb(s->data);
180 vp_set_fd_handler(s->fd, NULL, NULL, NULL);
181 closesocket(s->fd);
182 qemu_free(s->data);
183 } else {
184 LOG("unknown rpc_type");
186 if (s->content != NULL) {
187 qemu_free(s->content);
189 qemu_free(s);
192 static void va_rpc_read_handler(void *opaque)
194 VARPCState *s = opaque;
195 int ret;
197 TRACE("called with opaque: %p", opaque);
199 switch (s->state) {
200 case VA_READ_START:
201 s->state = VA_READ_HDR;
202 case VA_READ_HDR:
203 while((ret = read(s->fd, s->hdr + s->hdr_pos, 1)) > 0
204 && s->hdr_pos < VA_HDR_LEN_MAX) {
205 s->hdr_pos += ret;
206 if (end_of_header(s->hdr, s->hdr_pos - 1)) {
207 break;
210 if (ret == -1) {
211 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
212 return;
213 } else {
214 LOG("error reading connection: %s", strerror(errno));
215 goto out_bad;
217 } else if (ret == 0) {
218 LOG("connected closed unexpectedly");
219 goto out_bad;
220 } else if (s->hdr_pos >= VA_HDR_LEN_MAX) {
221 LOG("http header too long");
222 goto out_bad;
223 } else {
224 s->content_len = -1;
225 va_rpc_parse_hdr(s);
226 if (s->content_len == -1) {
227 LOG("malformed http header");
228 goto out_bad;
229 } else if (s->content_len > VA_CONTENT_LEN_MAX) {
230 LOG("http content length too long");
231 goto out_bad;
233 s->content = qemu_mallocz(s->content_len);
234 s->state = VA_READ_BODY;
236 case VA_READ_BODY:
237 while(s->content_pos < s->content_len) {
238 ret = read(s->fd, s->content + s->content_pos,
239 s->content_len - s->content_pos);
240 if (ret == -1) {
241 if (errno == EAGAIN || errno == EWOULDBLOCK
242 || errno == EINTR) {
243 return;
244 } else {
245 LOG("error reading connection: %s", strerror(errno));
246 goto out_bad;
248 } else if (ret == 0) {
249 LOG("connection closed unexpectedly:"
250 " read %u bytes, expected %u bytes",
251 (unsigned int)s->content_pos, (unsigned int)s->content_len);
252 goto out_bad;
254 s->content_pos += ret;
257 if (s->rpc_type == VA_RPC_REQUEST) {
258 s->data->req_xml = s->content;
259 s->data->req_xml_len = s->content_len;
260 } else if (s->rpc_type == VA_RPC_RESPONSE) {
261 s->data->resp_xml = s->content;
262 s->data->resp_xml_len = s->content_len;
264 s->data->status = VA_RPC_STATUS_OK;
265 goto out;
266 default:
267 LOG("unknown state");
268 goto out_bad;
271 out_bad:
272 s->data->status = VA_RPC_STATUS_ERR;
273 out:
274 va_rpc_read_handler_completion(s);
277 /* called by va_rpc_send_handler after sending requests */
278 static int va_rpc_read_response(VARPCData *data, int fd)
280 VARPCState *s = va_rpc_state_new(data, fd, VA_RPC_RESPONSE, VA_READ);
282 TRACE("called");
283 if (s == NULL) {
284 LOG("failed to set up RPC state");
285 return -1;
287 TRACE("setting up read handler for RPC response");
288 vp_set_fd_handler(fd, NULL, va_rpc_read_handler, s);
290 return 0;
293 static void va_rpc_send_handler_completion(VARPCState *s) {
294 int ret;
296 if (s->rpc_type == VA_RPC_REQUEST) {
297 /* client sent request. free request's memblock, and set up read
298 * handler for server response if there weren't any communication
299 * errors
301 XMLRPC_MEMBLOCK_FREE(char, s->data->send_req_xml);
302 if (s->data->status == VA_RPC_STATUS_OK) {
303 ret = va_rpc_read_response(s->data, s->fd);
304 if (ret != 0) {
305 LOG("error setting up read handler for rpc response");
307 } else {
308 if (s->data->cb) {
309 s->data->cb(s->data);
311 LOG("error sending rpc request, skipping response");
312 vp_set_fd_handler(s->fd, NULL, NULL, NULL);
313 closesocket(s->fd);
314 qemu_free(s->data);
316 } else if (s->rpc_type == VA_RPC_RESPONSE) {
317 /* server sent response. call it's cb once more, then free
318 * response's memblock and complete the RPC
320 if (s->data->cb) {
321 s->data->cb(s->data);
323 XMLRPC_MEMBLOCK_FREE(char, s->data->send_resp_xml);
324 vp_set_fd_handler(s->fd, NULL, NULL, NULL);
325 closesocket(s->fd);
326 qemu_free(s->data);
327 } else {
328 LOG("unknown rpc_type");
330 qemu_free(s);
333 static void va_rpc_send_handler(void *opaque)
335 VARPCState *s = opaque;
336 int ret;
338 TRACE("called with opaque: %p", opaque);
340 switch (s->state) {
341 case VA_SEND_START:
342 s->state = VA_SEND_HDR;
343 case VA_SEND_HDR:
344 do {
345 ret = write(s->fd, s->hdr + s->hdr_pos, s->hdr_len - s->hdr_pos);
346 if (ret <= 0) {
347 break;
349 s->hdr_pos += ret;
350 } while (s->hdr_pos < s->hdr_len);
351 if (ret == -1) {
352 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
353 return;
354 } else {
355 LOG("error reading connection: %s", strerror(errno));
356 goto out_bad;
358 } else if (ret == 0) {
359 LOG("connected closed unexpectedly");
360 goto out_bad;
361 } else {
362 s->state = VA_SEND_BODY;
364 case VA_SEND_BODY:
365 do {
366 ret = write(s->fd, s->content + s->content_pos,
367 s->content_len - s->content_pos);
368 if (ret <= 0) {
369 break;
371 s->content_pos += ret;
372 } while (s->content_pos < s->content_len);
373 if (ret == -1) {
374 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
375 return;
376 } else {
377 LOG("error reading connection: %s", strerror(errno));
378 goto out_bad;
380 } else if (ret == 0) {
381 LOG("connected closed unexpectedly");
382 goto out_bad;
383 } else {
384 s->data->status = VA_RPC_STATUS_OK;
385 goto out;
387 default:
388 LOG("unknown state");
389 goto out_bad;
392 out_bad:
393 s->data->status = VA_RPC_STATUS_ERR;
394 out:
395 va_rpc_send_handler_completion(s);
398 /* called by rpc client
399 * one callback to data->cb after response is read.
400 * data and data->send_req_xml should be allocated by caller,
401 * callee will de-allocate these after calling data->cb(data)
403 * if non-zero returned however, caller should free data and hanging refs
405 int va_rpc_send_request(VARPCData *data, int fd)
407 VARPCState *s = va_rpc_state_new(data, fd, VA_RPC_REQUEST, VA_SEND);
409 TRACE("called");
410 if (s == NULL) {
411 LOG("failed to set up RPC state");
412 return -1;
414 TRACE("setting up send handler for RPC request");
415 vp_set_fd_handler(fd, NULL, va_rpc_send_handler, s);
417 return 0;
420 /* called by rpc server
421 * one callback to current data->cb after read, one callback after send.
422 * data should be allocated by caller, data->send_resp_xml should be
423 * allocated by first data->cb(data) callback, "callee" will de-allocate
424 * data and data->send_resp_xml after sending rpc response
426 * if non-zero returned however, caller should free data and hanging refs
428 int va_rpc_read_request(VARPCData *data, int fd)
430 VARPCState *s = va_rpc_state_new(data, fd, VA_RPC_REQUEST, VA_READ);
432 TRACE("called");
433 if (s == NULL) {
434 LOG("failed to set up RPC state");
435 return -1;
437 TRACE("setting up read handler for RPC request");
438 vp_set_fd_handler(fd, va_rpc_read_handler, NULL, s);
439 return 0;