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/>.
27 #include <sys/select.h>
28 #include "vdagent-virtio-port.h"
30 #define VDP_LAST_PORT VDP_SERVER_PORT
32 struct vdagent_virtio_port_buf
{
37 struct vdagent_virtio_port_buf
*next
;
40 /* Data to keep track of the assembling of vdagent messages per chunk port,
41 for de-multiplexing the messages */
42 struct vdagent_virtio_port_chunk_port_data
{
43 int message_header_read
;
45 VDAgentMessage message_header
;
46 uint8_t *message_data
;
49 struct vdagent_virtio_port
{
53 /* Chunk read stuff, single buffer, separate header and data buffer */
54 int chunk_header_read
;
56 VDIChunkHeader chunk_header
;
57 uint8_t chunk_data
[VD_AGENT_MAX_DATA_SIZE
];
59 /* Per chunk port data */
60 struct vdagent_virtio_port_chunk_port_data port_data
[VDP_LAST_PORT
+ 1];
62 /* Writes are stored in a linked list of buffers, with both the header
63 + data for a single message in 1 buffer. */
64 struct vdagent_virtio_port_buf
*write_buf
;
67 vdagent_virtio_port_read_callback read_callback
;
68 vdagent_virtio_port_disconnect_callback disconnect_callback
;
71 static void vdagent_virtio_port_do_write(struct vdagent_virtio_port
**vportp
);
72 static void vdagent_virtio_port_do_read(struct vdagent_virtio_port
**vportp
);
74 struct vdagent_virtio_port
*vdagent_virtio_port_create(const char *portname
,
75 vdagent_virtio_port_read_callback read_callback
,
76 vdagent_virtio_port_disconnect_callback disconnect_callback
,
79 struct vdagent_virtio_port
*vport
;
81 vport
= calloc(1, sizeof(*vport
));
85 vport
->errfile
= errfile
;
86 vport
->fd
= open(portname
, O_RDWR
);
87 if (vport
->fd
== -1) {
88 fprintf(vport
->errfile
, "open %s: %s\n", portname
, strerror(errno
));
93 vport
->read_callback
= read_callback
;
94 vport
->disconnect_callback
= disconnect_callback
;
99 void vdagent_virtio_port_destroy(struct vdagent_virtio_port
**vportp
)
101 struct vdagent_virtio_port_buf
*wbuf
, *next_wbuf
;
102 struct vdagent_virtio_port
*vport
= *vportp
;
108 if (vport
->disconnect_callback
)
109 vport
->disconnect_callback(vport
);
111 wbuf
= vport
->write_buf
;
113 next_wbuf
= wbuf
->next
;
119 for (i
= 0; i
<= VDP_LAST_PORT
; i
++) {
120 free(vport
->port_data
[i
].message_data
);
128 int vdagent_virtio_port_fill_fds(struct vdagent_virtio_port
*vport
,
129 fd_set
*readfds
, fd_set
*writefds
)
134 FD_SET(vport
->fd
, readfds
);
135 if (vport
->write_buf
)
136 FD_SET(vport
->fd
, writefds
);
138 return vport
->fd
+ 1;
141 void vdagent_virtio_port_handle_fds(struct vdagent_virtio_port
**vportp
,
142 fd_set
*readfds
, fd_set
*writefds
)
147 if (FD_ISSET((*vportp
)->fd
, readfds
))
148 vdagent_virtio_port_do_read(vportp
);
150 if (*vportp
&& FD_ISSET((*vportp
)->fd
, writefds
))
151 vdagent_virtio_port_do_write(vportp
);
154 int vdagent_virtio_port_write(
155 struct vdagent_virtio_port
*vport
,
157 uint32_t message_type
,
158 uint32_t message_opaque
,
162 struct vdagent_virtio_port_buf
*wbuf
, *new_wbuf
;
163 VDIChunkHeader chunk_header
;
164 VDAgentMessage message_header
;
166 new_wbuf
= malloc(sizeof(*new_wbuf
));
171 new_wbuf
->size
= sizeof(chunk_header
) + sizeof(message_header
) + data_size
;
172 new_wbuf
->next
= NULL
;
173 new_wbuf
->buf
= malloc(new_wbuf
->size
);
174 if (!new_wbuf
->buf
) {
179 chunk_header
.port
= port_nr
;
180 chunk_header
.size
= sizeof(message_header
) + data_size
;
181 message_header
.protocol
= VD_AGENT_PROTOCOL
;
182 message_header
.type
= message_type
;
183 message_header
.opaque
= message_opaque
;
184 message_header
.size
= data_size
;
186 memcpy(new_wbuf
->buf
, &chunk_header
, sizeof(chunk_header
));
187 memcpy(new_wbuf
->buf
+ sizeof(chunk_header
), &message_header
,
188 sizeof(message_header
));
189 memcpy(new_wbuf
->buf
+ sizeof(chunk_header
) + sizeof(message_header
),
192 if (!vport
->write_buf
) {
193 vport
->write_buf
= new_wbuf
;
197 /* maybe we should limit the write_buf stack depth ? */
198 wbuf
= vport
->write_buf
;
202 wbuf
->next
= new_wbuf
;
207 void vdagent_virtio_port_flush(struct vdagent_virtio_port
**vportp
)
209 while (*vportp
&& (*vportp
)->write_buf
)
210 vdagent_virtio_port_do_write(vportp
);
213 static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port
**vportp
)
215 int avail
, read
, pos
= 0;
216 struct vdagent_virtio_port
*vport
= *vportp
;
217 struct vdagent_virtio_port_chunk_port_data
*port
=
218 &vport
->port_data
[vport
->chunk_header
.port
];
220 if (port
->message_header_read
< sizeof(port
->message_header
)) {
221 read
= sizeof(port
->message_header
) - port
->message_header_read
;
222 if (read
> vport
->chunk_header
.size
) {
223 read
= vport
->chunk_header
.size
;
225 memcpy((uint8_t *)&port
->message_header
+ port
->message_header_read
,
226 vport
->chunk_data
, read
);
227 port
->message_header_read
+= read
;
228 if (port
->message_header_read
== sizeof(port
->message_header
) &&
229 port
->message_header
.size
) {
230 port
->message_data
= malloc(port
->message_header
.size
);
231 if (!port
->message_data
) {
232 fprintf(vport
->errfile
, "out of memory, disconnecting virtio\n");
233 vdagent_virtio_port_destroy(vportp
);
240 if (port
->message_header_read
== sizeof(port
->message_header
)) {
241 read
= port
->message_header
.size
- port
->message_data_pos
;
242 avail
= vport
->chunk_header
.size
- pos
;
245 fprintf(vport
->errfile
, "chunk larger then message, lost sync?\n");
246 vdagent_virtio_port_destroy(vportp
);
254 memcpy(port
->message_data
+ port
->message_data_pos
,
255 vport
->chunk_data
+ pos
, read
);
256 port
->message_data_pos
+= read
;
259 if (port
->message_data_pos
== port
->message_header
.size
) {
260 if (vport
->read_callback
) {
261 int r
= vport
->read_callback(vport
, vport
->chunk_header
.port
,
262 &port
->message_header
, port
->message_data
);
264 vdagent_virtio_port_destroy(vportp
);
268 port
->message_header_read
= 0;
269 port
->message_data_pos
= 0;
270 free(port
->message_data
);
271 port
->message_data
= NULL
;
276 static void vdagent_virtio_port_do_read(struct vdagent_virtio_port
**vportp
)
281 struct vdagent_virtio_port
*vport
= *vportp
;
283 if (vport
->chunk_header_read
< sizeof(vport
->chunk_header
)) {
284 to_read
= sizeof(vport
->chunk_header
) - vport
->chunk_header_read
;
285 dest
= (uint8_t *)&vport
->chunk_header
+ vport
->chunk_header_read
;
287 to_read
= vport
->chunk_header
.size
- vport
->chunk_data_pos
;
288 dest
= vport
->chunk_data
+ vport
->chunk_data_pos
;
291 n
= read(vport
->fd
, dest
, to_read
);
295 fprintf(vport
->errfile
, "reading from vdagent virtio port: %s\n",
299 vdagent_virtio_port_destroy(vportp
);
303 if (vport
->chunk_header_read
< sizeof(vport
->chunk_header
)) {
304 vport
->chunk_header_read
+= n
;
305 if (vport
->chunk_header_read
== sizeof(vport
->chunk_header
)) {
306 if (vport
->chunk_header
.size
> VD_AGENT_MAX_DATA_SIZE
) {
307 fprintf(vport
->errfile
, "chunk size too large\n");
308 vdagent_virtio_port_destroy(vportp
);
311 if (vport
->chunk_header
.port
> VDP_LAST_PORT
) {
312 fprintf(vport
->errfile
, "chunk port out of range\n");
313 vdagent_virtio_port_destroy(vportp
);
318 vport
->chunk_data_pos
+= n
;
319 if (vport
->chunk_data_pos
== vport
->chunk_header
.size
) {
320 vdagent_virtio_port_do_chunk(vportp
);
321 vport
->chunk_header_read
= 0;
322 vport
->chunk_data_pos
= 0;
327 static void vdagent_virtio_port_do_write(struct vdagent_virtio_port
**vportp
)
331 struct vdagent_virtio_port
*vport
= *vportp
;
333 struct vdagent_virtio_port_buf
* wbuf
= vport
->write_buf
;
335 fprintf(vport
->errfile
,
336 "do_write called on a port without a write buf ?!\n");
340 to_write
= wbuf
->size
- wbuf
->pos
;
341 n
= write(vport
->fd
, wbuf
->buf
+ wbuf
->pos
, to_write
);
345 fprintf(vport
->errfile
, "writing to vdagent virtio port: %s\n",
347 vdagent_virtio_port_destroy(vportp
);
352 if (wbuf
->pos
== wbuf
->size
) {
353 vport
->write_buf
= wbuf
->next
;