Only install modules-load.d and tmpfiles.d files when using the systemd service
[vd_agent/hramrach.git] / src / vdagent-virtio-port.c
blobde4b7480a35feaa128e4c25ec573af053e47f89c
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 <stdlib.h>
24 #include <string.h>
25 #include <syslog.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <sys/select.h>
29 #include "vdagent-virtio-port.h"
31 #define VDP_LAST_PORT VDP_SERVER_PORT
33 struct vdagent_virtio_port_buf {
34 uint8_t *buf;
35 size_t pos;
36 size_t size;
37 size_t write_pos;
39 struct vdagent_virtio_port_buf *next;
42 /* Data to keep track of the assembling of vdagent messages per chunk port,
43 for de-multiplexing the messages */
44 struct vdagent_virtio_port_chunk_port_data {
45 int message_header_read;
46 int message_data_pos;
47 VDAgentMessage message_header;
48 uint8_t *message_data;
51 struct vdagent_virtio_port {
52 int fd;
53 int opening;
55 /* Chunk read stuff, single buffer, separate header and data buffer */
56 int chunk_header_read;
57 int chunk_data_pos;
58 VDIChunkHeader chunk_header;
59 uint8_t chunk_data[VD_AGENT_MAX_DATA_SIZE];
61 /* Per chunk port data */
62 struct vdagent_virtio_port_chunk_port_data port_data[VDP_LAST_PORT + 1];
64 /* Writes are stored in a linked list of buffers, with both the header
65 + data for a single message in 1 buffer. */
66 struct vdagent_virtio_port_buf *write_buf;
68 /* Callbacks */
69 vdagent_virtio_port_read_callback read_callback;
70 vdagent_virtio_port_disconnect_callback disconnect_callback;
73 static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **vportp);
74 static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp);
76 struct vdagent_virtio_port *vdagent_virtio_port_create(const char *portname,
77 vdagent_virtio_port_read_callback read_callback,
78 vdagent_virtio_port_disconnect_callback disconnect_callback)
80 struct vdagent_virtio_port *vport;
82 vport = calloc(1, sizeof(*vport));
83 if (!vport)
84 return 0;
86 vport->fd = open(portname, O_RDWR);
87 if (vport->fd == -1) {
88 syslog(LOG_ERR, "open %s: %m", portname);
89 free(vport);
90 return NULL;
92 vport->opening = 1;
94 vport->read_callback = read_callback;
95 vport->disconnect_callback = disconnect_callback;
97 return vport;
100 void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
102 struct vdagent_virtio_port_buf *wbuf, *next_wbuf;
103 struct vdagent_virtio_port *vport = *vportp;
104 int i;
106 if (!vport)
107 return;
109 if (vport->disconnect_callback)
110 vport->disconnect_callback(vport);
112 wbuf = vport->write_buf;
113 while (wbuf) {
114 next_wbuf = wbuf->next;
115 free(wbuf->buf);
116 free(wbuf);
117 wbuf = next_wbuf;
120 for (i = 0; i <= VDP_LAST_PORT; i++) {
121 free(vport->port_data[i].message_data);
124 close(vport->fd);
125 free(vport);
126 *vportp = NULL;
129 int vdagent_virtio_port_fill_fds(struct vdagent_virtio_port *vport,
130 fd_set *readfds, fd_set *writefds)
132 if (!vport)
133 return -1;
135 FD_SET(vport->fd, readfds);
136 if (vport->write_buf)
137 FD_SET(vport->fd, writefds);
139 return vport->fd + 1;
142 void vdagent_virtio_port_handle_fds(struct vdagent_virtio_port **vportp,
143 fd_set *readfds, fd_set *writefds)
145 if (!*vportp)
146 return;
148 if (FD_ISSET((*vportp)->fd, readfds))
149 vdagent_virtio_port_do_read(vportp);
151 if (*vportp && FD_ISSET((*vportp)->fd, writefds))
152 vdagent_virtio_port_do_write(vportp);
155 static struct vdagent_virtio_port_buf* vdagent_virtio_port_get_last_wbuf(
156 struct vdagent_virtio_port *vport)
158 struct vdagent_virtio_port_buf *wbuf;
160 wbuf = vport->write_buf;
161 if (!wbuf)
162 return NULL;
164 while (wbuf->next)
165 wbuf = wbuf->next;
167 return wbuf;
170 int vdagent_virtio_port_write_start(
171 struct vdagent_virtio_port *vport,
172 uint32_t port_nr,
173 uint32_t message_type,
174 uint32_t message_opaque,
175 uint32_t data_size)
177 struct vdagent_virtio_port_buf *wbuf, *new_wbuf;
178 VDIChunkHeader chunk_header;
179 VDAgentMessage message_header;
181 new_wbuf = malloc(sizeof(*new_wbuf));
182 if (!new_wbuf)
183 return -1;
185 new_wbuf->pos = 0;
186 new_wbuf->write_pos = 0;
187 new_wbuf->size = sizeof(chunk_header) + sizeof(message_header) + data_size;
188 new_wbuf->next = NULL;
189 new_wbuf->buf = malloc(new_wbuf->size);
190 if (!new_wbuf->buf) {
191 free(new_wbuf);
192 return -1;
195 chunk_header.port = port_nr;
196 chunk_header.size = sizeof(message_header) + data_size;
197 memcpy(new_wbuf->buf + new_wbuf->write_pos, &chunk_header,
198 sizeof(chunk_header));
199 new_wbuf->write_pos += sizeof(chunk_header);
201 message_header.protocol = VD_AGENT_PROTOCOL;
202 message_header.type = message_type;
203 message_header.opaque = message_opaque;
204 message_header.size = data_size;
205 memcpy(new_wbuf->buf + new_wbuf->write_pos, &message_header,
206 sizeof(message_header));
207 new_wbuf->write_pos += sizeof(message_header);
209 if (!vport->write_buf) {
210 vport->write_buf = new_wbuf;
211 return 0;
214 wbuf = vdagent_virtio_port_get_last_wbuf(vport);
215 wbuf->next = new_wbuf;
217 return 0;
220 int vdagent_virtio_port_write_append(struct vdagent_virtio_port *vport,
221 const uint8_t *data, uint32_t size)
223 struct vdagent_virtio_port_buf *wbuf;
225 wbuf = vdagent_virtio_port_get_last_wbuf(vport);
226 if (!wbuf) {
227 syslog(LOG_ERR, "can't append without a buffer");
228 return -1;
231 if (wbuf->size - wbuf->write_pos < size) {
232 syslog(LOG_ERR, "can't append to full buffer");
233 return -1;
236 memcpy(wbuf->buf + wbuf->write_pos, data, size);
237 wbuf->write_pos += size;
238 return 0;
241 int vdagent_virtio_port_write(
242 struct vdagent_virtio_port *vport,
243 uint32_t port_nr,
244 uint32_t message_type,
245 uint32_t message_opaque,
246 const uint8_t *data,
247 uint32_t data_size)
249 if (vdagent_virtio_port_write_start(vport, port_nr, message_type,
250 message_opaque, data_size)) {
251 return -1;
253 vdagent_virtio_port_write_append(vport, data, data_size);
254 return 0;
257 void vdagent_virtio_port_flush(struct vdagent_virtio_port **vportp)
259 while (*vportp && (*vportp)->write_buf)
260 vdagent_virtio_port_do_write(vportp);
263 static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)
265 int avail, read, pos = 0;
266 struct vdagent_virtio_port *vport = *vportp;
267 struct vdagent_virtio_port_chunk_port_data *port =
268 &vport->port_data[vport->chunk_header.port];
270 if (port->message_header_read < sizeof(port->message_header)) {
271 read = sizeof(port->message_header) - port->message_header_read;
272 if (read > vport->chunk_header.size) {
273 read = vport->chunk_header.size;
275 memcpy((uint8_t *)&port->message_header + port->message_header_read,
276 vport->chunk_data, read);
277 port->message_header_read += read;
278 if (port->message_header_read == sizeof(port->message_header) &&
279 port->message_header.size) {
280 port->message_data = malloc(port->message_header.size);
281 if (!port->message_data) {
282 syslog(LOG_ERR, "out of memory, disconnecting virtio");
283 vdagent_virtio_port_destroy(vportp);
284 return;
287 pos = read;
290 if (port->message_header_read == sizeof(port->message_header)) {
291 read = port->message_header.size - port->message_data_pos;
292 avail = vport->chunk_header.size - pos;
294 if (avail > read) {
295 syslog(LOG_ERR, "chunk larger then message, lost sync?");
296 vdagent_virtio_port_destroy(vportp);
297 return;
300 if (avail < read)
301 read = avail;
303 if (read) {
304 memcpy(port->message_data + port->message_data_pos,
305 vport->chunk_data + pos, read);
306 port->message_data_pos += read;
309 if (port->message_data_pos == port->message_header.size) {
310 if (vport->read_callback) {
311 int r = vport->read_callback(vport, vport->chunk_header.port,
312 &port->message_header, port->message_data);
313 if (r == -1) {
314 vdagent_virtio_port_destroy(vportp);
315 return;
318 port->message_header_read = 0;
319 port->message_data_pos = 0;
320 free(port->message_data);
321 port->message_data = NULL;
326 static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp)
328 ssize_t n;
329 size_t to_read;
330 uint8_t *dest;
331 struct vdagent_virtio_port *vport = *vportp;
333 if (vport->chunk_header_read < sizeof(vport->chunk_header)) {
334 to_read = sizeof(vport->chunk_header) - vport->chunk_header_read;
335 dest = (uint8_t *)&vport->chunk_header + vport->chunk_header_read;
336 } else {
337 to_read = vport->chunk_header.size - vport->chunk_data_pos;
338 dest = vport->chunk_data + vport->chunk_data_pos;
341 n = read(vport->fd, dest, to_read);
342 if (n < 0) {
343 if (errno == EINTR)
344 return;
345 syslog(LOG_ERR, "reading from vdagent virtio port: %m");
347 if (n == 0 && vport->opening) {
348 /* When we open the virtio serial port, the following happens:
349 1) The linux kernel virtio_console driver sends a
350 VIRTIO_CONSOLE_PORT_OPEN message to qemu
351 2) qemu's spicevmc chardev driver calls qemu_spice_add_interface to
352 register the agent chardev with the spice-server
353 3) spice-server then calls the spicevmc chardev driver's state
354 callback to let it know it is ready to receive data
355 4) The state callback sends a CHR_EVENT_OPENED to the virtio-console
356 chardev backend
357 5) The virtio-console chardev backend sends VIRTIO_CONSOLE_PORT_OPEN
358 to the linux kernel virtio_console driver
360 Until steps 1 - 5 have completed the linux kernel virtio_console
361 driver sees the virtio serial port as being in a disconnected state
362 and read will return 0 ! So if we blindly assume that a read 0 means
363 that the channel is closed we will hit a race here.
365 Therefore we ignore read returning 0 until we've successfully read
366 or written some data. If we hit this race we also sleep a bit here
367 to avoid busy waiting until the above steps complete */
368 usleep(10000);
369 return;
371 if (n <= 0) {
372 vdagent_virtio_port_destroy(vportp);
373 return;
375 vport->opening = 0;
377 if (vport->chunk_header_read < sizeof(vport->chunk_header)) {
378 vport->chunk_header_read += n;
379 if (vport->chunk_header_read == sizeof(vport->chunk_header)) {
380 if (vport->chunk_header.size > VD_AGENT_MAX_DATA_SIZE) {
381 syslog(LOG_ERR, "chunk size too large");
382 vdagent_virtio_port_destroy(vportp);
383 return;
385 if (vport->chunk_header.port > VDP_LAST_PORT) {
386 syslog(LOG_ERR, "chunk port out of range");
387 vdagent_virtio_port_destroy(vportp);
388 return;
391 } else {
392 vport->chunk_data_pos += n;
393 if (vport->chunk_data_pos == vport->chunk_header.size) {
394 vdagent_virtio_port_do_chunk(vportp);
395 vport->chunk_header_read = 0;
396 vport->chunk_data_pos = 0;
401 static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **vportp)
403 ssize_t n;
404 size_t to_write;
405 struct vdagent_virtio_port *vport = *vportp;
407 struct vdagent_virtio_port_buf* wbuf = vport->write_buf;
408 if (!wbuf) {
409 syslog(LOG_ERR, "do_write called on a port without a write buf ?!");
410 return;
413 if (wbuf->write_pos != wbuf->size) {
414 syslog(LOG_ERR, "do_write: buffer is incomplete!!");
415 return;
418 to_write = wbuf->size - wbuf->pos;
419 n = write(vport->fd, wbuf->buf + wbuf->pos, to_write);
420 if (n < 0) {
421 if (errno == EINTR)
422 return;
423 syslog(LOG_ERR, "writing to vdagent virtio port: %m");
424 vdagent_virtio_port_destroy(vportp);
425 return;
427 if (n > 0)
428 vport->opening = 0;
430 wbuf->pos += n;
431 if (wbuf->pos == wbuf->size) {
432 vport->write_buf = wbuf->next;
433 free(wbuf->buf);
434 free(wbuf);