1 #include "qemu/osdep.h"
2 #include "qapi/error.h"
3 #include "include/qemu-common.h"
4 #include "chardev/char.h"
5 #include "qemu/buffer.h"
6 #include "qemu/units.h"
9 #include "qapi/qapi-types-char.h"
11 #include "spice/vd_agent.h"
13 #define VDAGENT_BUFFER_LIMIT (1 * MiB)
15 struct VDAgentChardev
{
28 typedef struct VDAgentChardev VDAgentChardev
;
30 #define TYPE_CHARDEV_QEMU_VDAGENT "chardev-qemu-vdagent"
32 DECLARE_INSTANCE_CHECKER(VDAgentChardev
, QEMU_VDAGENT_CHARDEV
,
33 TYPE_CHARDEV_QEMU_VDAGENT
);
35 /* ------------------------------------------------------------------ */
36 /* names, for debug logging */
38 static const char *cap_name
[] = {
39 [VD_AGENT_CAP_MOUSE_STATE
] = "mouse-state",
40 [VD_AGENT_CAP_MONITORS_CONFIG
] = "monitors-config",
41 [VD_AGENT_CAP_REPLY
] = "reply",
42 [VD_AGENT_CAP_CLIPBOARD
] = "clipboard",
43 [VD_AGENT_CAP_DISPLAY_CONFIG
] = "display-config",
44 [VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
] = "clipboard-by-demand",
45 [VD_AGENT_CAP_CLIPBOARD_SELECTION
] = "clipboard-selection",
46 [VD_AGENT_CAP_SPARSE_MONITORS_CONFIG
] = "sparse-monitors-config",
47 [VD_AGENT_CAP_GUEST_LINEEND_LF
] = "guest-lineend-lf",
48 [VD_AGENT_CAP_GUEST_LINEEND_CRLF
] = "guest-lineend-crlf",
49 [VD_AGENT_CAP_MAX_CLIPBOARD
] = "max-clipboard",
50 [VD_AGENT_CAP_AUDIO_VOLUME_SYNC
] = "audio-volume-sync",
51 [VD_AGENT_CAP_MONITORS_CONFIG_POSITION
] = "monitors-config-position",
52 [VD_AGENT_CAP_FILE_XFER_DISABLED
] = "file-xfer-disabled",
53 [VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS
] = "file-xfer-detailed-errors",
55 [VD_AGENT_CAP_GRAPHICS_DEVICE_INFO
] = "graphics-device-info",
56 [VD_AGENT_CAP_CLIPBOARD_NO_RELEASE_ON_REGRAB
] = "clipboard-no-release-on-regrab",
57 [VD_AGENT_CAP_CLIPBOARD_GRAB_SERIAL
] = "clipboard-grab-serial",
61 static const char *msg_name
[] = {
62 [VD_AGENT_MOUSE_STATE
] = "mouse-state",
63 [VD_AGENT_MONITORS_CONFIG
] = "monitors-config",
64 [VD_AGENT_REPLY
] = "reply",
65 [VD_AGENT_CLIPBOARD
] = "clipboard",
66 [VD_AGENT_DISPLAY_CONFIG
] = "display-config",
67 [VD_AGENT_ANNOUNCE_CAPABILITIES
] = "announce-capabilities",
68 [VD_AGENT_CLIPBOARD_GRAB
] = "clipboard-grab",
69 [VD_AGENT_CLIPBOARD_REQUEST
] = "clipboard-request",
70 [VD_AGENT_CLIPBOARD_RELEASE
] = "clipboard-release",
71 [VD_AGENT_FILE_XFER_START
] = "file-xfer-start",
72 [VD_AGENT_FILE_XFER_STATUS
] = "file-xfer-status",
73 [VD_AGENT_FILE_XFER_DATA
] = "file-xfer-data",
74 [VD_AGENT_CLIENT_DISCONNECTED
] = "client-disconnected",
75 [VD_AGENT_MAX_CLIPBOARD
] = "max-clipboard",
76 [VD_AGENT_AUDIO_VOLUME_SYNC
] = "audio-volume-sync",
78 [VD_AGENT_GRAPHICS_DEVICE_INFO
] = "graphics-device-info",
82 #define GET_NAME(_m, _v) \
83 (((_v) < ARRAY_SIZE(_m) && (_m[_v])) ? (_m[_v]) : "???")
85 /* ------------------------------------------------------------------ */
88 static void vdagent_send_buf(VDAgentChardev
*vd
)
92 while (!buffer_empty(&vd
->outbuf
)) {
93 len
= qemu_chr_be_can_write(CHARDEV(vd
));
97 if (len
> vd
->outbuf
.offset
) {
98 len
= vd
->outbuf
.offset
;
100 qemu_chr_be_write(CHARDEV(vd
), vd
->outbuf
.buffer
, len
);
101 buffer_advance(&vd
->outbuf
, len
);
105 static void vdagent_send_msg(VDAgentChardev
*vd
, VDAgentMessage
*msg
)
107 uint8_t *msgbuf
= (void *)msg
;
108 uint32_t msgsize
= sizeof(VDAgentMessage
) + msg
->size
;
110 VDIChunkHeader chunk
;
112 trace_vdagent_send(GET_NAME(msg_name
, msg
->type
));
114 msg
->protocol
= VD_AGENT_PROTOCOL
;
116 if (vd
->outbuf
.offset
+ msgsize
> VDAGENT_BUFFER_LIMIT
) {
117 error_report("buffer full, dropping message");
121 while (msgoff
< msgsize
) {
122 chunk
.port
= VDP_CLIENT_PORT
;
123 chunk
.size
= msgsize
- msgoff
;
124 if (chunk
.size
> 1024) {
127 buffer_reserve(&vd
->outbuf
, sizeof(chunk
) + chunk
.size
);
128 buffer_append(&vd
->outbuf
, &chunk
, sizeof(chunk
));
129 buffer_append(&vd
->outbuf
, msgbuf
+ msgoff
, chunk
.size
);
130 msgoff
+= chunk
.size
;
132 vdagent_send_buf(vd
);
135 static void vdagent_send_caps(VDAgentChardev
*vd
)
137 g_autofree VDAgentMessage
*msg
= g_malloc0(sizeof(VDAgentMessage
) +
138 sizeof(VDAgentAnnounceCapabilities
) +
141 msg
->type
= VD_AGENT_ANNOUNCE_CAPABILITIES
;
142 msg
->size
= sizeof(VDAgentAnnounceCapabilities
) + sizeof(uint32_t);
144 vdagent_send_msg(vd
, msg
);
147 /* ------------------------------------------------------------------ */
148 /* chardev backend */
150 static void vdagent_chr_open(Chardev
*chr
,
151 ChardevBackend
*backend
,
155 #if defined(HOST_WORDS_BIGENDIAN)
157 * TODO: vdagent protocol is defined to be LE,
158 * so we have to byteswap everything on BE hosts.
160 error_setg(errp
, "vdagent is not supported on bigendian hosts");
167 static void vdagent_chr_recv_caps(VDAgentChardev
*vd
, VDAgentMessage
*msg
)
169 VDAgentAnnounceCapabilities
*caps
= (void *)msg
->data
;
172 if (msg
->size
< (sizeof(VDAgentAnnounceCapabilities
) +
177 for (i
= 0; i
< ARRAY_SIZE(cap_name
); i
++) {
178 if (caps
->caps
[0] & (1 << i
)) {
179 trace_vdagent_peer_cap(GET_NAME(cap_name
, i
));
183 vd
->caps
= caps
->caps
[0];
185 vdagent_send_caps(vd
);
189 static void vdagent_chr_recv_msg(VDAgentChardev
*vd
, VDAgentMessage
*msg
)
191 trace_vdagent_recv_msg(GET_NAME(msg_name
, msg
->type
), msg
->size
);
194 case VD_AGENT_ANNOUNCE_CAPABILITIES
:
195 vdagent_chr_recv_caps(vd
, msg
);
202 static void vdagent_reset_xbuf(VDAgentChardev
*vd
)
204 g_clear_pointer(&vd
->xbuf
, g_free
);
209 static void vdagent_chr_recv_chunk(VDAgentChardev
*vd
)
211 VDAgentMessage
*msg
= (void *)vd
->msgbuf
;
214 if (vd
->msgsize
< sizeof(*msg
)) {
215 error_report("%s: message too small: %d < %zd", __func__
,
216 vd
->msgsize
, sizeof(*msg
));
219 if (vd
->msgsize
== msg
->size
+ sizeof(*msg
)) {
220 vdagent_chr_recv_msg(vd
, msg
);
226 vd
->xsize
= msg
->size
+ sizeof(*msg
);
227 vd
->xbuf
= g_malloc0(vd
->xsize
);
230 if (vd
->xoff
+ vd
->msgsize
> vd
->xsize
) {
231 error_report("%s: Oops: %d+%d > %d", __func__
,
232 vd
->xoff
, vd
->msgsize
, vd
->xsize
);
233 vdagent_reset_xbuf(vd
);
237 memcpy(vd
->xbuf
+ vd
->xoff
, vd
->msgbuf
, vd
->msgsize
);
238 vd
->xoff
+= vd
->msgsize
;
239 if (vd
->xoff
< vd
->xsize
) {
243 msg
= (void *)vd
->xbuf
;
244 vdagent_chr_recv_msg(vd
, msg
);
245 vdagent_reset_xbuf(vd
);
248 static void vdagent_reset_bufs(VDAgentChardev
*vd
)
250 memset(&vd
->chunk
, 0, sizeof(vd
->chunk
));
257 static int vdagent_chr_write(Chardev
*chr
, const uint8_t *buf
, int len
)
259 VDAgentChardev
*vd
= QEMU_VDAGENT_CHARDEV(chr
);
260 uint32_t copy
, ret
= len
;
263 if (vd
->chunksize
< sizeof(vd
->chunk
)) {
264 copy
= sizeof(vd
->chunk
) - vd
->chunksize
;
268 memcpy((void *)(&vd
->chunk
) + vd
->chunksize
, buf
, copy
);
269 vd
->chunksize
+= copy
;
272 if (vd
->chunksize
< sizeof(vd
->chunk
)) {
276 assert(vd
->msgbuf
== NULL
);
277 vd
->msgbuf
= g_malloc0(vd
->chunk
.size
);
280 copy
= vd
->chunk
.size
- vd
->msgsize
;
284 memcpy(vd
->msgbuf
+ vd
->msgsize
, buf
, copy
);
289 if (vd
->msgsize
== vd
->chunk
.size
) {
290 trace_vdagent_recv_chunk(vd
->chunk
.size
);
291 vdagent_chr_recv_chunk(vd
);
292 vdagent_reset_bufs(vd
);
299 static void vdagent_chr_accept_input(Chardev
*chr
)
301 VDAgentChardev
*vd
= QEMU_VDAGENT_CHARDEV(chr
);
303 vdagent_send_buf(vd
);
306 static void vdagent_chr_set_fe_open(struct Chardev
*chr
, int fe_open
)
308 VDAgentChardev
*vd
= QEMU_VDAGENT_CHARDEV(chr
);
311 trace_vdagent_close();
313 vdagent_reset_bufs(vd
);
318 trace_vdagent_open();
321 /* ------------------------------------------------------------------ */
323 static void vdagent_chr_class_init(ObjectClass
*oc
, void *data
)
325 ChardevClass
*cc
= CHARDEV_CLASS(oc
);
327 cc
->open
= vdagent_chr_open
;
328 cc
->chr_write
= vdagent_chr_write
;
329 cc
->chr_set_fe_open
= vdagent_chr_set_fe_open
;
330 cc
->chr_accept_input
= vdagent_chr_accept_input
;
333 static void vdagent_chr_init(Object
*obj
)
335 VDAgentChardev
*vd
= QEMU_VDAGENT_CHARDEV(obj
);
337 buffer_init(&vd
->outbuf
, "vdagent-outbuf");
340 static void vdagent_chr_fini(Object
*obj
)
342 VDAgentChardev
*vd
= QEMU_VDAGENT_CHARDEV(obj
);
344 buffer_free(&vd
->outbuf
);
347 static const TypeInfo vdagent_chr_type_info
= {
348 .name
= TYPE_CHARDEV_QEMU_VDAGENT
,
349 .parent
= TYPE_CHARDEV
,
350 .instance_size
= sizeof(VDAgentChardev
),
351 .instance_init
= vdagent_chr_init
,
352 .instance_finalize
= vdagent_chr_fini
,
353 .class_init
= vdagent_chr_class_init
,
356 static void register_types(void)
358 type_register_static(&vdagent_chr_type_info
);
361 type_init(register_types
);