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/option.h" 7 #include "qemu/units.h" 8 #include "hw/qdev-core.h" 9 #include "ui/clipboard.h" 10 #include "ui/console.h" 11 #include "ui/input.h" 12 #include "trace.h" 13 14 #include "qapi/qapi-types-char.h" 15 #include "qapi/qapi-types-ui.h" 16 17 #include "spice/vd_agent.h" 18 19 #define VDAGENT_BUFFER_LIMIT (1 * MiB) 20 #define VDAGENT_MOUSE_DEFAULT true 21 #define VDAGENT_CLIPBOARD_DEFAULT false 22 23 struct VDAgentChardev { 24 Chardev parent; 25 26 /* config */ 27 bool mouse; 28 bool clipboard; 29 30 /* guest vdagent */ 31 uint32_t caps; 32 VDIChunkHeader chunk; 33 uint32_t chunksize; 34 uint8_t *msgbuf; 35 uint32_t msgsize; 36 uint8_t *xbuf; 37 uint32_t xoff, xsize; 38 Buffer outbuf; 39 40 /* mouse */ 41 DeviceState mouse_dev; 42 uint32_t mouse_x; 43 uint32_t mouse_y; 44 uint32_t mouse_btn; 45 uint32_t mouse_display; 46 QemuInputHandlerState *mouse_hs; 47 48 /* clipboard */ 49 QemuClipboardPeer cbpeer; 50 QemuClipboardInfo *cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT]; 51 uint32_t cbpending[QEMU_CLIPBOARD_SELECTION__COUNT]; 52 }; 53 typedef struct VDAgentChardev VDAgentChardev; 54 55 #define TYPE_CHARDEV_QEMU_VDAGENT "chardev-qemu-vdagent" 56 57 DECLARE_INSTANCE_CHECKER(VDAgentChardev, QEMU_VDAGENT_CHARDEV, 58 TYPE_CHARDEV_QEMU_VDAGENT); 59 60 /* ------------------------------------------------------------------ */ 61 /* names, for debug logging */ 62 63 static const char *cap_name[] = { 64 [VD_AGENT_CAP_MOUSE_STATE] = "mouse-state", 65 [VD_AGENT_CAP_MONITORS_CONFIG] = "monitors-config", 66 [VD_AGENT_CAP_REPLY] = "reply", 67 [VD_AGENT_CAP_CLIPBOARD] = "clipboard", 68 [VD_AGENT_CAP_DISPLAY_CONFIG] = "display-config", 69 [VD_AGENT_CAP_CLIPBOARD_BY_DEMAND] = "clipboard-by-demand", 70 [VD_AGENT_CAP_CLIPBOARD_SELECTION] = "clipboard-selection", 71 [VD_AGENT_CAP_SPARSE_MONITORS_CONFIG] = "sparse-monitors-config", 72 [VD_AGENT_CAP_GUEST_LINEEND_LF] = "guest-lineend-lf", 73 [VD_AGENT_CAP_GUEST_LINEEND_CRLF] = "guest-lineend-crlf", 74 [VD_AGENT_CAP_MAX_CLIPBOARD] = "max-clipboard", 75 [VD_AGENT_CAP_AUDIO_VOLUME_SYNC] = "audio-volume-sync", 76 [VD_AGENT_CAP_MONITORS_CONFIG_POSITION] = "monitors-config-position", 77 [VD_AGENT_CAP_FILE_XFER_DISABLED] = "file-xfer-disabled", 78 [VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS] = "file-xfer-detailed-errors", 79 #if 0 80 [VD_AGENT_CAP_GRAPHICS_DEVICE_INFO] = "graphics-device-info", 81 [VD_AGENT_CAP_CLIPBOARD_NO_RELEASE_ON_REGRAB] = "clipboard-no-release-on-regrab", 82 [VD_AGENT_CAP_CLIPBOARD_GRAB_SERIAL] = "clipboard-grab-serial", 83 #endif 84 }; 85 86 static const char *msg_name[] = { 87 [VD_AGENT_MOUSE_STATE] = "mouse-state", 88 [VD_AGENT_MONITORS_CONFIG] = "monitors-config", 89 [VD_AGENT_REPLY] = "reply", 90 [VD_AGENT_CLIPBOARD] = "clipboard", 91 [VD_AGENT_DISPLAY_CONFIG] = "display-config", 92 [VD_AGENT_ANNOUNCE_CAPABILITIES] = "announce-capabilities", 93 [VD_AGENT_CLIPBOARD_GRAB] = "clipboard-grab", 94 [VD_AGENT_CLIPBOARD_REQUEST] = "clipboard-request", 95 [VD_AGENT_CLIPBOARD_RELEASE] = "clipboard-release", 96 [VD_AGENT_FILE_XFER_START] = "file-xfer-start", 97 [VD_AGENT_FILE_XFER_STATUS] = "file-xfer-status", 98 [VD_AGENT_FILE_XFER_DATA] = "file-xfer-data", 99 [VD_AGENT_CLIENT_DISCONNECTED] = "client-disconnected", 100 [VD_AGENT_MAX_CLIPBOARD] = "max-clipboard", 101 [VD_AGENT_AUDIO_VOLUME_SYNC] = "audio-volume-sync", 102 #if 0 103 [VD_AGENT_GRAPHICS_DEVICE_INFO] = "graphics-device-info", 104 #endif 105 }; 106 107 static const char *sel_name[] = { 108 [VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD] = "clipboard", 109 [VD_AGENT_CLIPBOARD_SELECTION_PRIMARY] = "primary", 110 [VD_AGENT_CLIPBOARD_SELECTION_SECONDARY] = "secondary", 111 }; 112 113 static const char *type_name[] = { 114 [VD_AGENT_CLIPBOARD_NONE] = "none", 115 [VD_AGENT_CLIPBOARD_UTF8_TEXT] = "text", 116 [VD_AGENT_CLIPBOARD_IMAGE_PNG] = "png", 117 [VD_AGENT_CLIPBOARD_IMAGE_BMP] = "bmp", 118 [VD_AGENT_CLIPBOARD_IMAGE_TIFF] = "tiff", 119 [VD_AGENT_CLIPBOARD_IMAGE_JPG] = "jpg", 120 #if 0 121 [VD_AGENT_CLIPBOARD_FILE_LIST] = "files", 122 #endif 123 }; 124 125 #define GET_NAME(_m, _v) \ 126 (((_v) < ARRAY_SIZE(_m) && (_m[_v])) ? (_m[_v]) : "???") 127 128 /* ------------------------------------------------------------------ */ 129 /* send messages */ 130 131 static void vdagent_send_buf(VDAgentChardev *vd) 132 { 133 uint32_t len; 134 135 while (!buffer_empty(&vd->outbuf)) { 136 len = qemu_chr_be_can_write(CHARDEV(vd)); 137 if (len == 0) { 138 return; 139 } 140 if (len > vd->outbuf.offset) { 141 len = vd->outbuf.offset; 142 } 143 qemu_chr_be_write(CHARDEV(vd), vd->outbuf.buffer, len); 144 buffer_advance(&vd->outbuf, len); 145 } 146 } 147 148 static void vdagent_send_msg(VDAgentChardev *vd, VDAgentMessage *msg) 149 { 150 uint8_t *msgbuf = (void *)msg; 151 uint32_t msgsize = sizeof(VDAgentMessage) + msg->size; 152 uint32_t msgoff = 0; 153 VDIChunkHeader chunk; 154 155 trace_vdagent_send(GET_NAME(msg_name, msg->type)); 156 157 msg->protocol = VD_AGENT_PROTOCOL; 158 159 if (vd->outbuf.offset + msgsize > VDAGENT_BUFFER_LIMIT) { 160 error_report("buffer full, dropping message"); 161 return; 162 } 163 164 while (msgoff < msgsize) { 165 chunk.port = VDP_CLIENT_PORT; 166 chunk.size = msgsize - msgoff; 167 if (chunk.size > 1024) { 168 chunk.size = 1024; 169 } 170 buffer_reserve(&vd->outbuf, sizeof(chunk) + chunk.size); 171 buffer_append(&vd->outbuf, &chunk, sizeof(chunk)); 172 buffer_append(&vd->outbuf, msgbuf + msgoff, chunk.size); 173 msgoff += chunk.size; 174 } 175 vdagent_send_buf(vd); 176 } 177 178 static void vdagent_send_caps(VDAgentChardev *vd) 179 { 180 g_autofree VDAgentMessage *msg = g_malloc0(sizeof(VDAgentMessage) + 181 sizeof(VDAgentAnnounceCapabilities) + 182 sizeof(uint32_t)); 183 VDAgentAnnounceCapabilities *caps = (void *)msg->data; 184 185 msg->type = VD_AGENT_ANNOUNCE_CAPABILITIES; 186 msg->size = sizeof(VDAgentAnnounceCapabilities) + sizeof(uint32_t); 187 if (vd->mouse) { 188 caps->caps[0] |= (1 << VD_AGENT_CAP_MOUSE_STATE); 189 } 190 if (vd->clipboard) { 191 caps->caps[0] |= (1 << VD_AGENT_CAP_CLIPBOARD_BY_DEMAND); 192 caps->caps[0] |= (1 << VD_AGENT_CAP_CLIPBOARD_SELECTION); 193 } 194 195 vdagent_send_msg(vd, msg); 196 } 197 198 /* ------------------------------------------------------------------ */ 199 /* mouse events */ 200 201 static bool have_mouse(VDAgentChardev *vd) 202 { 203 return vd->mouse && 204 (vd->caps & (1 << VD_AGENT_CAP_MOUSE_STATE)); 205 } 206 207 static void vdagent_send_mouse(VDAgentChardev *vd) 208 { 209 g_autofree VDAgentMessage *msg = g_malloc0(sizeof(VDAgentMessage) + 210 sizeof(VDAgentMouseState)); 211 VDAgentMouseState *mouse = (void *)msg->data; 212 213 msg->type = VD_AGENT_MOUSE_STATE; 214 msg->size = sizeof(VDAgentMouseState); 215 216 mouse->x = vd->mouse_x; 217 mouse->y = vd->mouse_y; 218 mouse->buttons = vd->mouse_btn; 219 mouse->display_id = vd->mouse_display; 220 221 vdagent_send_msg(vd, msg); 222 } 223 224 static void vdagent_pointer_event(DeviceState *dev, QemuConsole *src, 225 InputEvent *evt) 226 { 227 static const int bmap[INPUT_BUTTON__MAX] = { 228 [INPUT_BUTTON_LEFT] = VD_AGENT_LBUTTON_MASK, 229 [INPUT_BUTTON_RIGHT] = VD_AGENT_RBUTTON_MASK, 230 [INPUT_BUTTON_MIDDLE] = VD_AGENT_MBUTTON_MASK, 231 [INPUT_BUTTON_WHEEL_UP] = VD_AGENT_UBUTTON_MASK, 232 [INPUT_BUTTON_WHEEL_DOWN] = VD_AGENT_DBUTTON_MASK, 233 #ifdef VD_AGENT_EBUTTON_MASK 234 [INPUT_BUTTON_SIDE] = VD_AGENT_SBUTTON_MASK, 235 [INPUT_BUTTON_EXTRA] = VD_AGENT_EBUTTON_MASK, 236 #endif 237 }; 238 239 VDAgentChardev *vd = container_of(dev, struct VDAgentChardev, mouse_dev); 240 InputMoveEvent *move; 241 InputBtnEvent *btn; 242 uint32_t xres, yres; 243 244 switch (evt->type) { 245 case INPUT_EVENT_KIND_ABS: 246 move = evt->u.abs.data; 247 xres = qemu_console_get_width(src, 1024); 248 yres = qemu_console_get_height(src, 768); 249 if (move->axis == INPUT_AXIS_X) { 250 vd->mouse_x = qemu_input_scale_axis(move->value, 251 INPUT_EVENT_ABS_MIN, 252 INPUT_EVENT_ABS_MAX, 253 0, xres); 254 } else if (move->axis == INPUT_AXIS_Y) { 255 vd->mouse_y = qemu_input_scale_axis(move->value, 256 INPUT_EVENT_ABS_MIN, 257 INPUT_EVENT_ABS_MAX, 258 0, yres); 259 } 260 vd->mouse_display = qemu_console_get_index(src); 261 break; 262 263 case INPUT_EVENT_KIND_BTN: 264 btn = evt->u.btn.data; 265 if (btn->down) { 266 vd->mouse_btn |= bmap[btn->button]; 267 } else { 268 vd->mouse_btn &= ~bmap[btn->button]; 269 } 270 break; 271 272 default: 273 /* keep gcc happy */ 274 break; 275 } 276 } 277 278 static void vdagent_pointer_sync(DeviceState *dev) 279 { 280 VDAgentChardev *vd = container_of(dev, struct VDAgentChardev, mouse_dev); 281 282 if (vd->caps & (1 << VD_AGENT_CAP_MOUSE_STATE)) { 283 vdagent_send_mouse(vd); 284 } 285 } 286 287 static QemuInputHandler vdagent_mouse_handler = { 288 .name = "vdagent mouse", 289 .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, 290 .event = vdagent_pointer_event, 291 .sync = vdagent_pointer_sync, 292 }; 293 294 /* ------------------------------------------------------------------ */ 295 /* clipboard */ 296 297 static bool have_clipboard(VDAgentChardev *vd) 298 { 299 return vd->clipboard && 300 (vd->caps & (1 << VD_AGENT_CAP_CLIPBOARD_BY_DEMAND)); 301 } 302 303 static bool have_selection(VDAgentChardev *vd) 304 { 305 return vd->caps & (1 << VD_AGENT_CAP_CLIPBOARD_SELECTION); 306 } 307 308 static uint32_t type_qemu_to_vdagent(enum QemuClipboardType type) 309 { 310 switch (type) { 311 case QEMU_CLIPBOARD_TYPE_TEXT: 312 return VD_AGENT_CLIPBOARD_UTF8_TEXT; 313 default: 314 return VD_AGENT_CLIPBOARD_NONE; 315 } 316 } 317 318 static void vdagent_send_clipboard_grab(VDAgentChardev *vd, 319 QemuClipboardInfo *info) 320 { 321 g_autofree VDAgentMessage *msg = 322 g_malloc0(sizeof(VDAgentMessage) + 323 sizeof(uint32_t) * (QEMU_CLIPBOARD_TYPE__COUNT + 1)); 324 uint8_t *s = msg->data; 325 uint32_t *data = (uint32_t *)msg->data; 326 uint32_t q, type; 327 328 if (have_selection(vd)) { 329 *s = info->selection; 330 data++; 331 msg->size += sizeof(uint32_t); 332 } else if (info->selection != QEMU_CLIPBOARD_SELECTION_CLIPBOARD) { 333 return; 334 } 335 336 for (q = 0; q < QEMU_CLIPBOARD_TYPE__COUNT; q++) { 337 type = type_qemu_to_vdagent(q); 338 if (type != VD_AGENT_CLIPBOARD_NONE && info->types[q].available) { 339 *data = type; 340 data++; 341 msg->size += sizeof(uint32_t); 342 } 343 } 344 345 msg->type = VD_AGENT_CLIPBOARD_GRAB; 346 vdagent_send_msg(vd, msg); 347 } 348 349 static void vdagent_send_clipboard_data(VDAgentChardev *vd, 350 QemuClipboardInfo *info, 351 QemuClipboardType type) 352 { 353 g_autofree VDAgentMessage *msg = g_malloc0(sizeof(VDAgentMessage) + 354 sizeof(uint32_t) * 2 + 355 info->types[type].size); 356 357 uint8_t *s = msg->data; 358 uint32_t *data = (uint32_t *)msg->data; 359 360 if (have_selection(vd)) { 361 *s = info->selection; 362 data++; 363 msg->size += sizeof(uint32_t); 364 } else if (info->selection != QEMU_CLIPBOARD_SELECTION_CLIPBOARD) { 365 return; 366 } 367 368 *data = type_qemu_to_vdagent(type); 369 data++; 370 msg->size += sizeof(uint32_t); 371 372 memcpy(data, info->types[type].data, info->types[type].size); 373 msg->size += info->types[type].size; 374 375 msg->type = VD_AGENT_CLIPBOARD; 376 vdagent_send_msg(vd, msg); 377 } 378 379 static void vdagent_clipboard_notify(Notifier *notifier, void *data) 380 { 381 VDAgentChardev *vd = container_of(notifier, VDAgentChardev, cbpeer.update); 382 QemuClipboardInfo *info = data; 383 QemuClipboardSelection s = info->selection; 384 QemuClipboardType type; 385 bool self_update = info->owner == &vd->cbpeer; 386 387 if (info != vd->cbinfo[s]) { 388 qemu_clipboard_info_unref(vd->cbinfo[s]); 389 vd->cbinfo[s] = qemu_clipboard_info_ref(info); 390 vd->cbpending[s] = 0; 391 if (!self_update) { 392 vdagent_send_clipboard_grab(vd, info); 393 } 394 return; 395 } 396 397 if (self_update) { 398 return; 399 } 400 401 for (type = 0; type < QEMU_CLIPBOARD_TYPE__COUNT; type++) { 402 if (vd->cbpending[s] & (1 << type)) { 403 vd->cbpending[s] &= ~(1 << type); 404 vdagent_send_clipboard_data(vd, info, type); 405 } 406 } 407 } 408 409 static void vdagent_clipboard_request(QemuClipboardInfo *info, 410 QemuClipboardType qtype) 411 { 412 VDAgentChardev *vd = container_of(info->owner, VDAgentChardev, cbpeer); 413 g_autofree VDAgentMessage *msg = g_malloc0(sizeof(VDAgentMessage) + 414 sizeof(uint32_t) * 2); 415 uint32_t type = type_qemu_to_vdagent(qtype); 416 uint8_t *s = msg->data; 417 uint32_t *data = (uint32_t *)msg->data; 418 419 if (type == VD_AGENT_CLIPBOARD_NONE) { 420 return; 421 } 422 423 if (have_selection(vd)) { 424 *s = info->selection; 425 data++; 426 msg->size += sizeof(uint32_t); 427 } 428 429 *data = type; 430 msg->size += sizeof(uint32_t); 431 432 msg->type = VD_AGENT_CLIPBOARD_REQUEST; 433 vdagent_send_msg(vd, msg); 434 } 435 436 static void vdagent_chr_recv_clipboard(VDAgentChardev *vd, VDAgentMessage *msg) 437 { 438 uint8_t s = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD; 439 uint32_t size = msg->size; 440 void *data = msg->data; 441 QemuClipboardInfo *info; 442 QemuClipboardType type; 443 444 if (have_selection(vd)) { 445 if (size < 4) { 446 return; 447 } 448 s = *(uint8_t *)data; 449 if (s >= QEMU_CLIPBOARD_SELECTION__COUNT) { 450 return; 451 } 452 data += 4; 453 size -= 4; 454 } 455 456 switch (msg->type) { 457 case VD_AGENT_CLIPBOARD_GRAB: 458 trace_vdagent_cb_grab_selection(GET_NAME(sel_name, s)); 459 info = qemu_clipboard_info_new(&vd->cbpeer, s); 460 if (size > sizeof(uint32_t) * 10) { 461 /* 462 * spice has 6 types as of 2021. Limiting to 10 entries 463 * so we we have some wiggle room. 464 */ 465 return; 466 } 467 while (size >= sizeof(uint32_t)) { 468 trace_vdagent_cb_grab_type(GET_NAME(type_name, *(uint32_t *)data)); 469 switch (*(uint32_t *)data) { 470 case VD_AGENT_CLIPBOARD_UTF8_TEXT: 471 info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true; 472 break; 473 default: 474 break; 475 } 476 data += sizeof(uint32_t); 477 size -= sizeof(uint32_t); 478 } 479 qemu_clipboard_update(info); 480 qemu_clipboard_info_unref(info); 481 break; 482 case VD_AGENT_CLIPBOARD_REQUEST: 483 if (size < sizeof(uint32_t)) { 484 return; 485 } 486 switch (*(uint32_t *)data) { 487 case VD_AGENT_CLIPBOARD_UTF8_TEXT: 488 type = QEMU_CLIPBOARD_TYPE_TEXT; 489 break; 490 default: 491 return; 492 } 493 if (vd->cbinfo[s] && 494 vd->cbinfo[s]->types[type].available && 495 vd->cbinfo[s]->owner != &vd->cbpeer) { 496 if (vd->cbinfo[s]->types[type].data) { 497 vdagent_send_clipboard_data(vd, vd->cbinfo[s], type); 498 } else { 499 vd->cbpending[s] |= (1 << type); 500 qemu_clipboard_request(vd->cbinfo[s], type); 501 } 502 } 503 break; 504 case VD_AGENT_CLIPBOARD: /* data */ 505 if (size < sizeof(uint32_t)) { 506 return; 507 } 508 switch (*(uint32_t *)data) { 509 case VD_AGENT_CLIPBOARD_UTF8_TEXT: 510 type = QEMU_CLIPBOARD_TYPE_TEXT; 511 break; 512 default: 513 return; 514 } 515 data += 4; 516 size -= 4; 517 qemu_clipboard_set_data(&vd->cbpeer, vd->cbinfo[s], type, 518 size, data, true); 519 break; 520 case VD_AGENT_CLIPBOARD_RELEASE: /* data */ 521 if (vd->cbinfo[s] && 522 vd->cbinfo[s]->owner == &vd->cbpeer) { 523 /* set empty clipboard info */ 524 info = qemu_clipboard_info_new(NULL, s); 525 qemu_clipboard_update(info); 526 qemu_clipboard_info_unref(info); 527 } 528 break; 529 } 530 } 531 532 /* ------------------------------------------------------------------ */ 533 /* chardev backend */ 534 535 static void vdagent_chr_open(Chardev *chr, 536 ChardevBackend *backend, 537 bool *be_opened, 538 Error **errp) 539 { 540 VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr); 541 ChardevQemuVDAgent *cfg = backend->u.qemu_vdagent.data; 542 543 #if defined(HOST_WORDS_BIGENDIAN) 544 /* 545 * TODO: vdagent protocol is defined to be LE, 546 * so we have to byteswap everything on BE hosts. 547 */ 548 error_setg(errp, "vdagent is not supported on bigendian hosts"); 549 return; 550 #endif 551 552 vd->mouse = VDAGENT_MOUSE_DEFAULT; 553 if (cfg->has_mouse) { 554 vd->mouse = cfg->mouse; 555 } 556 557 vd->clipboard = VDAGENT_CLIPBOARD_DEFAULT; 558 if (cfg->has_clipboard) { 559 vd->clipboard = cfg->clipboard; 560 } 561 562 if (vd->mouse) { 563 vd->mouse_hs = qemu_input_handler_register(&vd->mouse_dev, 564 &vdagent_mouse_handler); 565 } 566 567 *be_opened = true; 568 } 569 570 static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg) 571 { 572 VDAgentAnnounceCapabilities *caps = (void *)msg->data; 573 int i; 574 575 if (msg->size < (sizeof(VDAgentAnnounceCapabilities) + 576 sizeof(uint32_t))) { 577 return; 578 } 579 580 for (i = 0; i < ARRAY_SIZE(cap_name); i++) { 581 if (caps->caps[0] & (1 << i)) { 582 trace_vdagent_peer_cap(GET_NAME(cap_name, i)); 583 } 584 } 585 586 vd->caps = caps->caps[0]; 587 if (caps->request) { 588 vdagent_send_caps(vd); 589 } 590 if (have_mouse(vd) && vd->mouse_hs) { 591 qemu_input_handler_activate(vd->mouse_hs); 592 } 593 if (have_clipboard(vd) && vd->cbpeer.update.notify == NULL) { 594 vd->cbpeer.name = "vdagent"; 595 vd->cbpeer.update.notify = vdagent_clipboard_notify; 596 vd->cbpeer.request = vdagent_clipboard_request; 597 qemu_clipboard_peer_register(&vd->cbpeer); 598 } 599 } 600 601 static void vdagent_chr_recv_msg(VDAgentChardev *vd, VDAgentMessage *msg) 602 { 603 trace_vdagent_recv_msg(GET_NAME(msg_name, msg->type), msg->size); 604 605 switch (msg->type) { 606 case VD_AGENT_ANNOUNCE_CAPABILITIES: 607 vdagent_chr_recv_caps(vd, msg); 608 break; 609 case VD_AGENT_CLIPBOARD: 610 case VD_AGENT_CLIPBOARD_GRAB: 611 case VD_AGENT_CLIPBOARD_REQUEST: 612 case VD_AGENT_CLIPBOARD_RELEASE: 613 if (have_clipboard(vd)) { 614 vdagent_chr_recv_clipboard(vd, msg); 615 } 616 break; 617 default: 618 break; 619 } 620 } 621 622 static void vdagent_reset_xbuf(VDAgentChardev *vd) 623 { 624 g_clear_pointer(&vd->xbuf, g_free); 625 vd->xoff = 0; 626 vd->xsize = 0; 627 } 628 629 static void vdagent_chr_recv_chunk(VDAgentChardev *vd) 630 { 631 VDAgentMessage *msg = (void *)vd->msgbuf; 632 633 if (!vd->xsize) { 634 if (vd->msgsize < sizeof(*msg)) { 635 error_report("%s: message too small: %d < %zd", __func__, 636 vd->msgsize, sizeof(*msg)); 637 return; 638 } 639 if (vd->msgsize == msg->size + sizeof(*msg)) { 640 vdagent_chr_recv_msg(vd, msg); 641 return; 642 } 643 } 644 645 if (!vd->xsize) { 646 vd->xsize = msg->size + sizeof(*msg); 647 vd->xbuf = g_malloc0(vd->xsize); 648 } 649 650 if (vd->xoff + vd->msgsize > vd->xsize) { 651 error_report("%s: Oops: %d+%d > %d", __func__, 652 vd->xoff, vd->msgsize, vd->xsize); 653 vdagent_reset_xbuf(vd); 654 return; 655 } 656 657 memcpy(vd->xbuf + vd->xoff, vd->msgbuf, vd->msgsize); 658 vd->xoff += vd->msgsize; 659 if (vd->xoff < vd->xsize) { 660 return; 661 } 662 663 msg = (void *)vd->xbuf; 664 vdagent_chr_recv_msg(vd, msg); 665 vdagent_reset_xbuf(vd); 666 } 667 668 static void vdagent_reset_bufs(VDAgentChardev *vd) 669 { 670 memset(&vd->chunk, 0, sizeof(vd->chunk)); 671 vd->chunksize = 0; 672 g_free(vd->msgbuf); 673 vd->msgbuf = NULL; 674 vd->msgsize = 0; 675 } 676 677 static int vdagent_chr_write(Chardev *chr, const uint8_t *buf, int len) 678 { 679 VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr); 680 uint32_t copy, ret = len; 681 682 while (len) { 683 if (vd->chunksize < sizeof(vd->chunk)) { 684 copy = sizeof(vd->chunk) - vd->chunksize; 685 if (copy > len) { 686 copy = len; 687 } 688 memcpy((void *)(&vd->chunk) + vd->chunksize, buf, copy); 689 vd->chunksize += copy; 690 buf += copy; 691 len -= copy; 692 if (vd->chunksize < sizeof(vd->chunk)) { 693 break; 694 } 695 696 assert(vd->msgbuf == NULL); 697 vd->msgbuf = g_malloc0(vd->chunk.size); 698 } 699 700 copy = vd->chunk.size - vd->msgsize; 701 if (copy > len) { 702 copy = len; 703 } 704 memcpy(vd->msgbuf + vd->msgsize, buf, copy); 705 vd->msgsize += copy; 706 buf += copy; 707 len -= copy; 708 709 if (vd->msgsize == vd->chunk.size) { 710 trace_vdagent_recv_chunk(vd->chunk.size); 711 vdagent_chr_recv_chunk(vd); 712 vdagent_reset_bufs(vd); 713 } 714 } 715 716 return ret; 717 } 718 719 static void vdagent_chr_accept_input(Chardev *chr) 720 { 721 VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr); 722 723 vdagent_send_buf(vd); 724 } 725 726 static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open) 727 { 728 VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr); 729 730 if (!fe_open) { 731 trace_vdagent_close(); 732 /* reset state */ 733 vdagent_reset_bufs(vd); 734 vd->caps = 0; 735 if (vd->mouse_hs) { 736 qemu_input_handler_deactivate(vd->mouse_hs); 737 } 738 if (vd->cbpeer.update.notify) { 739 qemu_clipboard_peer_unregister(&vd->cbpeer); 740 memset(&vd->cbpeer, 0, sizeof(vd->cbpeer)); 741 } 742 return; 743 } 744 745 trace_vdagent_open(); 746 } 747 748 static void vdagent_chr_parse(QemuOpts *opts, ChardevBackend *backend, 749 Error **errp) 750 { 751 ChardevQemuVDAgent *cfg; 752 753 backend->type = CHARDEV_BACKEND_KIND_QEMU_VDAGENT; 754 cfg = backend->u.qemu_vdagent.data = g_new0(ChardevQemuVDAgent, 1); 755 qemu_chr_parse_common(opts, qapi_ChardevQemuVDAgent_base(cfg)); 756 cfg->has_mouse = true; 757 cfg->mouse = qemu_opt_get_bool(opts, "mouse", VDAGENT_MOUSE_DEFAULT); 758 cfg->has_clipboard = true; 759 cfg->clipboard = qemu_opt_get_bool(opts, "clipboard", VDAGENT_CLIPBOARD_DEFAULT); 760 } 761 762 /* ------------------------------------------------------------------ */ 763 764 static void vdagent_chr_class_init(ObjectClass *oc, void *data) 765 { 766 ChardevClass *cc = CHARDEV_CLASS(oc); 767 768 cc->parse = vdagent_chr_parse; 769 cc->open = vdagent_chr_open; 770 cc->chr_write = vdagent_chr_write; 771 cc->chr_set_fe_open = vdagent_chr_set_fe_open; 772 cc->chr_accept_input = vdagent_chr_accept_input; 773 } 774 775 static void vdagent_chr_init(Object *obj) 776 { 777 VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj); 778 779 buffer_init(&vd->outbuf, "vdagent-outbuf"); 780 } 781 782 static void vdagent_chr_fini(Object *obj) 783 { 784 VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj); 785 786 buffer_free(&vd->outbuf); 787 } 788 789 static const TypeInfo vdagent_chr_type_info = { 790 .name = TYPE_CHARDEV_QEMU_VDAGENT, 791 .parent = TYPE_CHARDEV, 792 .instance_size = sizeof(VDAgentChardev), 793 .instance_init = vdagent_chr_init, 794 .instance_finalize = vdagent_chr_fini, 795 .class_init = vdagent_chr_class_init, 796 }; 797 798 static void register_types(void) 799 { 800 type_register_static(&vdagent_chr_type_info); 801 } 802 803 type_init(register_types); 804