1 /* vdagent-virtio-port.c virtio port communication code
3 Copyright 2010 Red Hat, Inc.
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/>.
28 #include <sys/select.h>
29 #include <sys/socket.h>
33 #include "virtio-port.h"
36 struct vdagent_virtio_port_buf
{
42 struct vdagent_virtio_port_buf
*next
;
45 /* Data to keep track of the assembling of vdagent messages per chunk port,
46 for de-multiplexing the messages */
47 struct vdagent_virtio_port_chunk_port_data
{
48 int message_header_read
;
50 VDAgentMessage message_header
;
51 uint8_t *message_data
;
54 struct vdagent_virtio_port
{
59 /* Chunk read stuff, single buffer, separate header and data buffer */
60 int chunk_header_read
;
62 VDIChunkHeader chunk_header
;
63 uint8_t chunk_data
[VD_AGENT_MAX_DATA_SIZE
];
65 /* Per chunk port data */
66 struct vdagent_virtio_port_chunk_port_data port_data
[VDP_END_PORT
];
68 /* Writes are stored in a linked list of buffers, with both the header
69 + data for a single message in 1 buffer. */
70 struct vdagent_virtio_port_buf
*write_buf
;
73 vdagent_virtio_port_read_callback read_callback
;
74 vdagent_virtio_port_disconnect_callback disconnect_callback
;
77 static void vdagent_virtio_port_do_write(struct vdagent_virtio_port
**vportp
);
78 static void vdagent_virtio_port_do_read(struct vdagent_virtio_port
**vportp
);
80 struct vdagent_virtio_port
*vdagent_virtio_port_create(const char *portname
,
81 vdagent_virtio_port_read_callback read_callback
,
82 vdagent_virtio_port_disconnect_callback disconnect_callback
)
84 struct vdagent_virtio_port
*vport
;
85 struct sockaddr_un address
;
88 vport
= calloc(1, sizeof(*vport
));
92 vport
->fd
= open(portname
, O_RDWR
);
93 if (vport
->fd
== -1) {
94 vport
->fd
= socket(PF_UNIX
, SOCK_STREAM
, 0);
95 if (vport
->fd
== -1) {
98 address
.sun_family
= AF_UNIX
;
99 snprintf(address
.sun_path
, sizeof(address
.sun_path
), "%s", portname
);
100 c
= connect(vport
->fd
, (struct sockaddr
*)&address
, sizeof(address
));
111 vport
->read_callback
= read_callback
;
112 vport
->disconnect_callback
= disconnect_callback
;
117 syslog(LOG_ERR
, "open %s: %m", portname
);
118 if (vport
->fd
!= -1) {
125 void vdagent_virtio_port_destroy(struct vdagent_virtio_port
**vportp
)
127 struct vdagent_virtio_port_buf
*wbuf
, *next_wbuf
;
128 struct vdagent_virtio_port
*vport
= *vportp
;
134 if (vport
->disconnect_callback
)
135 vport
->disconnect_callback(vport
);
137 wbuf
= vport
->write_buf
;
139 next_wbuf
= wbuf
->next
;
145 for (i
= 0; i
< VDP_END_PORT
; i
++) {
146 free(vport
->port_data
[i
].message_data
);
154 int vdagent_virtio_port_fill_fds(struct vdagent_virtio_port
*vport
,
155 fd_set
*readfds
, fd_set
*writefds
)
160 FD_SET(vport
->fd
, readfds
);
161 if (vport
->write_buf
)
162 FD_SET(vport
->fd
, writefds
);
164 return vport
->fd
+ 1;
167 void vdagent_virtio_port_handle_fds(struct vdagent_virtio_port
**vportp
,
168 fd_set
*readfds
, fd_set
*writefds
)
173 if (FD_ISSET((*vportp
)->fd
, readfds
))
174 vdagent_virtio_port_do_read(vportp
);
176 if (*vportp
&& FD_ISSET((*vportp
)->fd
, writefds
))
177 vdagent_virtio_port_do_write(vportp
);
180 static struct vdagent_virtio_port_buf
* vdagent_virtio_port_get_last_wbuf(
181 struct vdagent_virtio_port
*vport
)
183 struct vdagent_virtio_port_buf
*wbuf
;
185 wbuf
= vport
->write_buf
;
195 int vdagent_virtio_port_write_start(
196 struct vdagent_virtio_port
*vport
,
198 uint32_t message_type
,
199 uint32_t message_opaque
,
202 struct vdagent_virtio_port_buf
*wbuf
, *new_wbuf
;
203 VDIChunkHeader chunk_header
;
204 VDAgentMessage message_header
;
206 new_wbuf
= malloc(sizeof(*new_wbuf
));
211 new_wbuf
->write_pos
= 0;
212 new_wbuf
->size
= sizeof(chunk_header
) + sizeof(message_header
) + data_size
;
213 new_wbuf
->next
= NULL
;
214 new_wbuf
->buf
= malloc(new_wbuf
->size
);
215 if (!new_wbuf
->buf
) {
220 chunk_header
.port
= GUINT32_TO_LE(port_nr
);
221 chunk_header
.size
= GUINT32_TO_LE(sizeof(message_header
) + data_size
);
222 memcpy(new_wbuf
->buf
+ new_wbuf
->write_pos
, &chunk_header
,
223 sizeof(chunk_header
));
224 new_wbuf
->write_pos
+= sizeof(chunk_header
);
226 message_header
.protocol
= GUINT32_TO_LE(VD_AGENT_PROTOCOL
);
227 message_header
.type
= GUINT32_TO_LE(message_type
);
228 message_header
.opaque
= GUINT64_TO_LE(message_opaque
);
229 message_header
.size
= GUINT32_TO_LE(data_size
);
230 memcpy(new_wbuf
->buf
+ new_wbuf
->write_pos
, &message_header
,
231 sizeof(message_header
));
232 new_wbuf
->write_pos
+= sizeof(message_header
);
234 if (!vport
->write_buf
) {
235 vport
->write_buf
= new_wbuf
;
239 wbuf
= vdagent_virtio_port_get_last_wbuf(vport
);
240 wbuf
->next
= new_wbuf
;
245 int vdagent_virtio_port_write_append(struct vdagent_virtio_port
*vport
,
246 const uint8_t *data
, uint32_t size
)
248 struct vdagent_virtio_port_buf
*wbuf
;
250 wbuf
= vdagent_virtio_port_get_last_wbuf(vport
);
252 syslog(LOG_ERR
, "can't append without a buffer");
256 if (wbuf
->size
- wbuf
->write_pos
< size
) {
257 syslog(LOG_ERR
, "can't append to full buffer");
261 memcpy(wbuf
->buf
+ wbuf
->write_pos
, data
, size
);
262 wbuf
->write_pos
+= size
;
266 int vdagent_virtio_port_write(
267 struct vdagent_virtio_port
*vport
,
269 uint32_t message_type
,
270 uint32_t message_opaque
,
274 if (vdagent_virtio_port_write_start(vport
, port_nr
, message_type
,
275 message_opaque
, data_size
)) {
278 vdagent_virtio_port_write_append(vport
, data
, data_size
);
282 void vdagent_virtio_port_flush(struct vdagent_virtio_port
**vportp
)
284 while (*vportp
&& (*vportp
)->write_buf
)
285 vdagent_virtio_port_do_write(vportp
);
288 void vdagent_virtio_port_reset(struct vdagent_virtio_port
*vport
, int port
)
290 if (port
>= VDP_END_PORT
) {
291 syslog(LOG_ERR
, "vdagent_virtio_port_reset port out of range");
294 free(vport
->port_data
[port
].message_data
);
295 memset(&vport
->port_data
[port
], 0, sizeof(vport
->port_data
[0]));
298 static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port
**vportp
)
300 int avail
, read
, pos
= 0;
301 struct vdagent_virtio_port
*vport
= *vportp
;
302 struct vdagent_virtio_port_chunk_port_data
*port
=
303 &vport
->port_data
[vport
->chunk_header
.port
];
305 if (port
->message_header_read
< sizeof(port
->message_header
)) {
306 read
= sizeof(port
->message_header
) - port
->message_header_read
;
307 if (read
> vport
->chunk_header
.size
) {
308 read
= vport
->chunk_header
.size
;
310 memcpy((uint8_t *)&port
->message_header
+ port
->message_header_read
,
311 vport
->chunk_data
, read
);
312 port
->message_header_read
+= read
;
313 if (port
->message_header_read
== sizeof(port
->message_header
)) {
315 port
->message_header
.protocol
= GUINT32_FROM_LE(port
->message_header
.protocol
);
316 port
->message_header
.type
= GUINT32_FROM_LE(port
->message_header
.type
);
317 port
->message_header
.opaque
= GUINT64_FROM_LE(port
->message_header
.opaque
);
318 port
->message_header
.size
= GUINT32_FROM_LE(port
->message_header
.size
);
320 if (port
->message_header
.size
) {
321 port
->message_data
= malloc(port
->message_header
.size
);
322 if (!port
->message_data
) {
323 syslog(LOG_ERR
, "out of memory, disconnecting virtio");
324 vdagent_virtio_port_destroy(vportp
);
332 if (port
->message_header_read
== sizeof(port
->message_header
)) {
333 read
= port
->message_header
.size
- port
->message_data_pos
;
334 avail
= vport
->chunk_header
.size
- pos
;
337 syslog(LOG_ERR
, "chunk larger then message, lost sync?");
338 vdagent_virtio_port_destroy(vportp
);
346 memcpy(port
->message_data
+ port
->message_data_pos
,
347 vport
->chunk_data
+ pos
, read
);
348 port
->message_data_pos
+= read
;
351 if (port
->message_data_pos
== port
->message_header
.size
) {
352 if (vport
->read_callback
) {
353 int r
= vport
->read_callback(vport
, vport
->chunk_header
.port
,
354 &port
->message_header
, port
->message_data
);
356 vdagent_virtio_port_destroy(vportp
);
360 port
->message_header_read
= 0;
361 port
->message_data_pos
= 0;
362 free(port
->message_data
);
363 port
->message_data
= NULL
;
368 static int vport_read(struct vdagent_virtio_port
*vport
, uint8_t *buf
, int len
)
371 return recv(vport
->fd
, buf
, len
, 0);
373 return read(vport
->fd
, buf
, len
);
377 static void vdagent_virtio_port_do_read(struct vdagent_virtio_port
**vportp
)
382 struct vdagent_virtio_port
*vport
= *vportp
;
384 if (vport
->chunk_header_read
< sizeof(vport
->chunk_header
)) {
385 to_read
= sizeof(vport
->chunk_header
) - vport
->chunk_header_read
;
386 dest
= (uint8_t *)&vport
->chunk_header
+ vport
->chunk_header_read
;
388 to_read
= vport
->chunk_header
.size
- vport
->chunk_data_pos
;
389 dest
= vport
->chunk_data
+ vport
->chunk_data_pos
;
392 n
= vport_read(vport
, dest
, to_read
);
396 syslog(LOG_ERR
, "reading from vdagent virtio port: %m");
398 if (n
== 0 && vport
->opening
) {
399 /* When we open the virtio serial port, the following happens:
400 1) The linux kernel virtio_console driver sends a
401 VIRTIO_CONSOLE_PORT_OPEN message to qemu
402 2) qemu's spicevmc chardev driver calls qemu_spice_add_interface to
403 register the agent chardev with the spice-server
404 3) spice-server then calls the spicevmc chardev driver's state
405 callback to let it know it is ready to receive data
406 4) The state callback sends a CHR_EVENT_OPENED to the virtio-console
408 5) The virtio-console chardev backend sends VIRTIO_CONSOLE_PORT_OPEN
409 to the linux kernel virtio_console driver
411 Until steps 1 - 5 have completed the linux kernel virtio_console
412 driver sees the virtio serial port as being in a disconnected state
413 and read will return 0 ! So if we blindly assume that a read 0 means
414 that the channel is closed we will hit a race here.
416 Therefore we ignore read returning 0 until we've successfully read
417 or written some data. If we hit this race we also sleep a bit here
418 to avoid busy waiting until the above steps complete */
423 vdagent_virtio_port_destroy(vportp
);
428 if (vport
->chunk_header_read
< sizeof(vport
->chunk_header
)) {
429 vport
->chunk_header_read
+= n
;
430 if (vport
->chunk_header_read
== sizeof(vport
->chunk_header
)) {
431 vport
->chunk_header
.size
= GUINT32_FROM_LE(vport
->chunk_header
.size
);
432 vport
->chunk_header
.port
= GUINT32_FROM_LE(vport
->chunk_header
.port
);
433 if (vport
->chunk_header
.size
> VD_AGENT_MAX_DATA_SIZE
) {
434 syslog(LOG_ERR
, "chunk size %u too large",
435 vport
->chunk_header
.size
);
436 vdagent_virtio_port_destroy(vportp
);
439 if (vport
->chunk_header
.port
>= VDP_END_PORT
) {
440 syslog(LOG_ERR
, "chunk port %u out of range",
441 vport
->chunk_header
.port
);
442 vdagent_virtio_port_destroy(vportp
);
447 vport
->chunk_data_pos
+= n
;
448 if (vport
->chunk_data_pos
== vport
->chunk_header
.size
) {
449 vdagent_virtio_port_do_chunk(vportp
);
452 vport
->chunk_header_read
= 0;
453 vport
->chunk_data_pos
= 0;
458 static int vport_write(struct vdagent_virtio_port
*vport
, uint8_t *buf
, int len
)
461 return send(vport
->fd
, buf
, len
, 0);
463 return write(vport
->fd
, buf
, len
);
467 static void vdagent_virtio_port_do_write(struct vdagent_virtio_port
**vportp
)
471 struct vdagent_virtio_port
*vport
= *vportp
;
473 struct vdagent_virtio_port_buf
* wbuf
= vport
->write_buf
;
475 syslog(LOG_ERR
, "do_write called on a port without a write buf ?!");
479 if (wbuf
->write_pos
!= wbuf
->size
) {
480 syslog(LOG_ERR
, "do_write: buffer is incomplete!!");
484 to_write
= wbuf
->size
- wbuf
->pos
;
485 n
= vport_write(vport
, wbuf
->buf
+ wbuf
->pos
, to_write
);
489 syslog(LOG_ERR
, "writing to vdagent virtio port: %m");
490 vdagent_virtio_port_destroy(vportp
);
497 if (wbuf
->pos
== wbuf
->size
) {
498 vport
->write_buf
= wbuf
->next
;