vdagentd: Cache monitor configuration and forward
[vd_agent/hramrach.git] / vdagent-virtio-port.c
blob454cb041a4452a0633ffea6f7d4a18f6f78dd798
1 /* vdagent-virtio-port.c virtio port communication code
3 Copyright 2010 Red Hat, Inc.
5 Red Hat Authors:
6 Hans de Goede <hdegoede@redhat.com>
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <sys/select.h>
29 #include "vdagent-virtio-port.h"
31 struct vdagent_virtio_port_buf {
32 uint8_t *buf;
33 size_t pos;
34 size_t size;
36 struct vdagent_virtio_port_buf *next;
39 struct vdagent_virtio_port {
40 int fd;
42 /* Read stuff, single buffer, separate header and data buffer */
43 int chunk_header_read;
44 int message_header_read;
45 VDIChunkHeader chunk_header;
46 VDAgentMessage message_header;
47 struct vdagent_virtio_port_buf data;
49 /* Writes are stored in a linked list of buffers, with both the header
50 + data for a single message in 1 buffer. */
51 struct vdagent_virtio_port_buf *write_buf;
53 /* Callbacks */
54 vdagent_virtio_port_read_callback read_callback;
55 vdagent_virtio_port_disconnect_callback disconnect_callback;
58 static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **portp);
59 static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **portp);
61 struct vdagent_virtio_port *vdagent_virtio_port_create(const char *portname,
62 vdagent_virtio_port_read_callback read_callback,
63 vdagent_virtio_port_disconnect_callback disconnect_callback)
65 struct vdagent_virtio_port *port;
67 port = calloc(1, sizeof(*port));
68 if (!port)
69 return 0;
71 port->fd = open(portname, O_RDWR);
72 if (port->fd == -1) {
73 fprintf(stderr, "open %s: %s\n", portname, strerror(errno));
74 free(port);
75 return NULL;
78 port->read_callback = read_callback;
79 port->disconnect_callback = disconnect_callback;
81 return port;
84 void vdagent_virtio_port_destroy(struct vdagent_virtio_port **portp)
86 struct vdagent_virtio_port_buf *wbuf, *next_wbuf;
87 struct vdagent_virtio_port *port = *portp;
89 if (!port)
90 return;
92 if (port->disconnect_callback)
93 port->disconnect_callback(port);
95 wbuf = port->write_buf;
96 while (wbuf) {
97 next_wbuf = wbuf->next;
98 free(wbuf->buf);
99 free(wbuf);
100 wbuf = next_wbuf;
103 free(port->data.buf);
105 close(port->fd);
106 free(port);
107 *portp = NULL;
110 int vdagent_virtio_port_fill_fds(struct vdagent_virtio_port *port,
111 fd_set *readfds, fd_set *writefds)
113 if (!port)
114 return -1;
116 FD_SET(port->fd, readfds);
117 if (port->write_buf)
118 FD_SET(port->fd, writefds);
120 return port->fd + 1;
123 void vdagent_virtio_port_handle_fds(struct vdagent_virtio_port **portp,
124 fd_set *readfds, fd_set *writefds)
126 if (!*portp)
127 return;
129 if (FD_ISSET((*portp)->fd, readfds))
130 vdagent_virtio_port_do_read(portp);
132 if (*portp && FD_ISSET((*portp)->fd, writefds))
133 vdagent_virtio_port_do_write(portp);
136 int vdagent_virtio_port_write(
137 struct vdagent_virtio_port *port,
138 VDIChunkHeader *chunk_header,
139 VDAgentMessage *message_header,
140 uint8_t *data)
142 struct vdagent_virtio_port_buf *wbuf, *new_wbuf;
144 if (message_header->size !=
145 (chunk_header->size - sizeof(*message_header))) {
146 fprintf(stderr, "write: chunk vs message header size mismatch\n");
147 return -1;
150 new_wbuf = malloc(sizeof(*new_wbuf));
151 if (!new_wbuf)
152 return -1;
154 new_wbuf->pos = 0;
155 new_wbuf->size = sizeof(*chunk_header) + sizeof(*message_header) +
156 message_header->size;
157 new_wbuf->next = NULL;
158 new_wbuf->buf = malloc(new_wbuf->size);
159 if (!new_wbuf->buf) {
160 free(new_wbuf);
161 return -1;
164 memcpy(new_wbuf->buf, chunk_header, sizeof(*chunk_header));
165 memcpy(new_wbuf->buf + sizeof(*chunk_header), message_header,
166 sizeof(*message_header));
167 memcpy(new_wbuf->buf + sizeof(*chunk_header) + sizeof(*message_header),
168 data, message_header->size);
170 if (!port->write_buf) {
171 port->write_buf = new_wbuf;
172 return 0;
175 /* FIXME maybe limit the write_buf stack depth ? */
176 wbuf = port->write_buf;
177 while (wbuf->next)
178 wbuf = wbuf->next;
180 wbuf->next = wbuf;
182 return 0;
185 static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **portp)
187 ssize_t n;
188 size_t to_read;
189 uint8_t *dest;
190 int r;
191 struct vdagent_virtio_port *port = *portp;
193 if (port->chunk_header_read < sizeof(port->chunk_header)) {
194 to_read = sizeof(port->chunk_header) - port->chunk_header_read;
195 dest = (uint8_t *)&port->chunk_header + port->chunk_header_read;
196 } else if (port->message_header_read < sizeof(port->message_header)) {
197 to_read = sizeof(port->message_header) - port->message_header_read;
198 dest = (uint8_t *)&port->message_header + port->message_header_read;
199 } else {
200 to_read = port->data.size - port->data.pos;
201 dest = port->data.buf + port->data.pos;
204 n = read(port->fd, dest, to_read);
205 if (n < 0) {
206 if (errno == EINTR)
207 return;
208 perror("reading from vdagent virtio port");
210 if (n <= 0) {
211 vdagent_virtio_port_destroy(portp);
212 return;
215 if (port->chunk_header_read < sizeof(port->chunk_header)) {
216 port->chunk_header_read += n;
217 if (port->chunk_header_read == sizeof(port->chunk_header)) {
218 if (port->chunk_header.size < sizeof(port->message_header)) {
219 fprintf(stderr, "chunk size < message header size\n");
220 vdagent_virtio_port_destroy(portp);
221 return;
223 port->message_header_read = 0;
225 } else if (port->message_header_read < sizeof(port->message_header)) {
226 port->message_header_read += n;
227 if (port->message_header_read == sizeof(port->message_header)) {
228 if (port->message_header.size !=
229 (port->chunk_header.size - sizeof(port->message_header))) {
230 fprintf(stderr,
231 "read: chunk vs message header size mismatch\n");
232 vdagent_virtio_port_destroy(portp);
233 return;
235 if (port->message_header.size == 0) {
236 if (port->read_callback) {
237 r = port->read_callback(port, &port->chunk_header,
238 &port->message_header, NULL);
239 if (r == -1) {
240 vdagent_virtio_port_destroy(portp);
241 return;
244 port->chunk_header_read = 0;
245 port->message_header_read = 0;
246 } else {
247 port->data.pos = 0;
248 port->data.size = port->message_header.size;
249 port->data.buf = malloc(port->data.size);
250 if (!port->data.buf) {
251 fprintf(stderr, "out of memory, disportecting client\n");
252 vdagent_virtio_port_destroy(portp);
253 return;
257 } else {
258 port->data.pos += n;
259 if (port->data.pos == port->data.size) {
260 if (port->read_callback) {
261 r = port->read_callback(port, &port->chunk_header,
262 &port->message_header, port->data.buf);
263 if (r == -1) {
264 vdagent_virtio_port_destroy(portp);
265 return;
268 free(port->data.buf);
269 port->chunk_header_read = 0;
270 port->message_header_read = 0;
271 memset(&port->data, 0, sizeof(port->data));
276 static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **portp)
278 ssize_t n;
279 size_t to_write;
280 struct vdagent_virtio_port *port = *portp;
282 struct vdagent_virtio_port_buf* wbuf = port->write_buf;
283 if (!wbuf) {
284 fprintf(stderr,
285 "do_write called on a port without a write buf ?!\n");
286 return;
289 to_write = wbuf->size - wbuf->pos;
290 n = write(port->fd, wbuf->buf + wbuf->pos, to_write);
291 if (n < 0) {
292 if (errno == EINTR)
293 return;
294 perror("writing to vdagent virtio port");
295 vdagent_virtio_port_destroy(portp);
296 return;
299 wbuf->pos += n;
300 if (wbuf->pos == wbuf->size) {
301 port->write_buf = wbuf->next;
302 free(wbuf->buf);
303 free(wbuf);