1 /* spice-server spicevmc passthrough channel code
2
3 Copyright (C) 2011 Red Hat, Inc.
4
5 Red Hat Authors:
6 Hans de Goede <hdegoede@redhat.com>
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version.
12
13 This library 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 GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 */
21 #include <config.h>
22
23 #include <assert.h>
24 #include <string.h>
25 #ifdef USE_LZ4
26 #include <lz4.h>
27 #endif
28
29 #include <common/generated_server_marshallers.h>
30
31 #include "char-device.h"
32 #include "red-channel.h"
33 #include "red-channel-client.h"
34 #include "reds.h"
35 #include "migration-protocol.h"
36
37 /* 64K should be enough for all but the largest writes + 32 bytes hdr */
38 #define BUF_SIZE (64 * 1024 + 32)
39 #define COMPRESS_THRESHOLD 1000
40
41 // limit of the queued data, at this limit we stop reading from device to
42 // avoid DoS
43 #define QUEUED_DATA_LIMIT (1024*1024)
44
45 enum {
46 RED_PIPE_ITEM_TYPE_SPICEVMC_DATA = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
47 RED_PIPE_ITEM_TYPE_SPICEVMC_MIGRATE_DATA,
48 RED_PIPE_ITEM_TYPE_PORT_INIT,
49 RED_PIPE_ITEM_TYPE_PORT_EVENT,
50 };
51
52 struct RedVmcChannel;
53 class VmcChannelClient;
54
55 struct RedVmcPipeItem: public RedPipeItemNum<RED_PIPE_ITEM_TYPE_SPICEVMC_DATA> {
56 SpiceDataCompressionType type;
57 uint32_t uncompressed_data_size = 0;
58 /* writes which don't fit this will get split, this is not a problem */
59 uint8_t buf[BUF_SIZE];
60 uint32_t buf_used = 0;
61 };
62
63 struct RedCharDeviceSpiceVmc: public RedCharDevice
64 {
65 RedCharDeviceSpiceVmc(SpiceCharDeviceInstance *sin, RedsState *reds, RedVmcChannel *channel);
66 ~RedCharDeviceSpiceVmc() override;
67
68 RedPipeItemPtr read_one_msg_from_device() override;
69 void remove_client(RedCharDeviceClientOpaque *opaque) override;
70 void on_free_self_token() override;
71 void port_event(uint8_t event) override;
72
73 red::shared_ptr<RedVmcChannel> channel;
74 };
75
76 static void spicevmc_red_channel_queue_data(RedVmcChannel *channel, red::shared_ptr<RedVmcPipeItem>&& item);
77
78 struct RedVmcChannel: public RedChannel
79 {
80 RedVmcChannel(RedsState *reds, uint32_t type, uint32_t id);
81 ~RedVmcChannel() override;
82
83 void on_connect(RedClient *client, RedStream *stream, int migration,
84 RedChannelCapabilities *caps) override;
85
86 VmcChannelClient *rcc;
87 RedCharDevice *chardev; /* weak */
88 SpiceCharDeviceInstance *chardev_sin;
89 red::shared_ptr<RedVmcPipeItem> pipe_item;
90 RedCharDeviceWriteBuffer *recv_from_client_buf;
91 uint8_t port_opened;
92 uint32_t queued_data;
93 RedStatCounter in_data;
94 RedStatCounter in_compressed;
95 RedStatCounter in_decompressed;
96 RedStatCounter out_data;
97 RedStatCounter out_compressed;
98 RedStatCounter out_uncompressed;
99 };
100
101
102 class VmcChannelClient final: public RedChannelClient
103 {
104 using RedChannelClient::RedChannelClient;
105 public:
get_channel()106 RedVmcChannel* get_channel()
107 {
108 return static_cast<RedVmcChannel*>(RedChannelClient::get_channel());
109 }
110 protected:
111 uint8_t *alloc_recv_buf(uint16_t type, uint32_t size) override;
112 void release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg) override;
113 void on_disconnect() override;
114 bool handle_message(uint16_t type, uint32_t size, void *msg) override;
115 void send_item(RedPipeItem *item) override;
116 bool handle_migrate_data(uint32_t size, void *message) override;
117 void handle_migrate_flush_mark() override;
118 };
119
120 static VmcChannelClient *
121 vmc_channel_client_create(RedChannel *channel, RedClient *client,
122 RedStream *stream,
123 RedChannelCapabilities *caps);
124
125
RedVmcChannel(RedsState * reds,uint32_t type,uint32_t id)126 RedVmcChannel::RedVmcChannel(RedsState *reds, uint32_t type, uint32_t id):
127 RedChannel(reds, type, id, RedChannel::MigrateAll)
128 {
129 init_stat_node(nullptr, "spicevmc");
130 const RedStatNode *stat = get_stat_node();
131 stat_init_counter(&in_data, reds, stat, "in_data", TRUE);
132 stat_init_counter(&in_compressed, reds, stat, "in_compressed", TRUE);
133 stat_init_counter(&in_decompressed, reds, stat, "in_decompressed", TRUE);
134 stat_init_counter(&out_data, reds, stat, "out_data", TRUE);
135 stat_init_counter(&out_compressed, reds, stat, "out_compressed", TRUE);
136 stat_init_counter(&out_uncompressed, reds, stat, "out_uncompressed", TRUE);
137
138 #ifdef USE_LZ4
139 set_cap(SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4);
140 #endif
141
142 reds_register_channel(reds, this);
143 }
144
~RedVmcChannel()145 RedVmcChannel::~RedVmcChannel()
146 {
147 RedCharDevice::write_buffer_release(chardev, &recv_from_client_buf);
148 }
149
red_vmc_channel_new(RedsState * reds,uint8_t channel_type)150 static red::shared_ptr<RedVmcChannel> red_vmc_channel_new(RedsState *reds, uint8_t channel_type)
151 {
152 switch (channel_type) {
153 case SPICE_CHANNEL_USBREDIR:
154 case SPICE_CHANNEL_WEBDAV:
155 case SPICE_CHANNEL_PORT:
156 break;
157 default:
158 g_error("Unsupported channel_type for red_vmc_channel_new(): %u", channel_type);
159 return red::shared_ptr<RedVmcChannel>();
160 }
161
162 int id = reds_get_free_channel_id(reds, channel_type);
163 if (id < 0) {
164 g_warning("Free ID not found creating new VMC channel");
165 return red::shared_ptr<RedVmcChannel>();
166 }
167
168 return red::make_shared<RedVmcChannel>(reds, channel_type, id);
169 }
170
171 struct RedPortInitPipeItem: public RedPipeItemNum<RED_PIPE_ITEM_TYPE_PORT_INIT> {
172 RedPortInitPipeItem(const char *name, uint8_t opened);
173
174 red::glib_unique_ptr<char> name;
175 uint8_t opened;
176 };
177
178 struct RedPortEventPipeItem: public RedPipeItemNum<RED_PIPE_ITEM_TYPE_PORT_EVENT> {
179 uint8_t event;
180 };
181
182 /* msg_item -- the current pipe item with the uncompressed data
183 * This function returns:
184 * - false upon failure.
185 * - true if compression succeeded
186 */
187 static bool
try_compress_lz4(RedVmcChannel * channel,red::shared_ptr<RedVmcPipeItem> & msg_item)188 try_compress_lz4(RedVmcChannel *channel, red::shared_ptr<RedVmcPipeItem>& msg_item)
189 {
190 #ifdef USE_LZ4
191 int compressed_data_count;
192 auto n = msg_item->buf_used;
193
194 if (red_stream_get_family(channel->rcc->get_stream()) == AF_UNIX) {
195 /* AF_LOCAL - data will not be compressed */
196 return false;
197 }
198 if (n <= COMPRESS_THRESHOLD) {
199 /* n <= threshold - data will not be compressed */
200 return false;
201 }
202 if (!channel->rcc->test_remote_cap(SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4)) {
203 /* Client doesn't have compression cap - data will not be compressed */
204 return false;
205 }
206 auto msg_item_compressed = red::make_shared<RedVmcPipeItem>();
207 compressed_data_count = LZ4_compress_default((char*)&msg_item->buf,
208 (char*)&msg_item_compressed->buf,
209 n,
210 BUF_SIZE);
211
212 if (compressed_data_count > 0 && compressed_data_count < n) {
213 stat_inc_counter(channel->out_uncompressed, n);
214 stat_inc_counter(channel->out_compressed, compressed_data_count);
215 msg_item_compressed->type = SPICE_DATA_COMPRESSION_TYPE_LZ4;
216 msg_item_compressed->uncompressed_data_size = n;
217 msg_item_compressed->buf_used = compressed_data_count;
218 msg_item = std::move(msg_item_compressed);
219 return true;
220 }
221
222 /* LZ4 compression failed or did non compress, fallback a non-compressed data is to be sent */
223 #endif
224 return false;
225 }
226
227 RedPipeItemPtr
read_one_msg_from_device()228 RedCharDeviceSpiceVmc::read_one_msg_from_device()
229 {
230 red::shared_ptr<RedVmcPipeItem> msg_item;
231 int n;
232
233 if (!channel->rcc || channel->queued_data >= QUEUED_DATA_LIMIT) {
234 return RedPipeItemPtr();
235 }
236
237 if (!channel->pipe_item) {
238 msg_item = red::make_shared<RedVmcPipeItem>();
239 msg_item->type = SPICE_DATA_COMPRESSION_TYPE_NONE;
240 } else {
241 spice_assert(channel->pipe_item->buf_used == 0);
242 msg_item = std::move(channel->pipe_item);
243 }
244
245 n = read(msg_item->buf, sizeof(msg_item->buf));
246 if (n > 0) {
247 spice_debug("read from dev %d", n);
248 msg_item->uncompressed_data_size = n;
249 msg_item->buf_used = n;
250
251 if (!try_compress_lz4(channel.get(), msg_item)) {
252 stat_inc_counter(channel->out_data, n);
253 }
254 spicevmc_red_channel_queue_data(channel.get(), std::move(msg_item));
255 return RedPipeItemPtr();
256 }
257 channel->pipe_item = std::move(msg_item);
258 return RedPipeItemPtr();
259 }
260
RedPortInitPipeItem(const char * init_name,uint8_t init_opened)261 RedPortInitPipeItem::RedPortInitPipeItem(const char *init_name, uint8_t init_opened):
262 name(g_strdup(init_name)),
263 opened(init_opened)
264 {
265 }
266
spicevmc_port_send_init(VmcChannelClient * rcc)267 static void spicevmc_port_send_init(VmcChannelClient *rcc)
268 {
269 RedVmcChannel *channel = rcc->get_channel();
270 SpiceCharDeviceInstance *sin = channel->chardev_sin;
271 auto item = red::make_shared<RedPortInitPipeItem>(sin->portname, channel->port_opened);
272
273 rcc->pipe_add_push(item);
274 }
275
spicevmc_port_send_event(RedChannelClient * rcc,uint8_t event)276 static void spicevmc_port_send_event(RedChannelClient *rcc, uint8_t event)
277 {
278 auto item = red::make_shared<RedPortEventPipeItem>();
279
280 item->event = event;
281 rcc->pipe_add_push(item);
282 }
283
remove_client(RedCharDeviceClientOpaque * opaque)284 void RedCharDeviceSpiceVmc::remove_client(RedCharDeviceClientOpaque *opaque)
285 {
286 auto client = (RedClient *) opaque;
287
288 spice_assert(channel->rcc &&
289 channel->rcc->get_client() == client);
290
291 channel->rcc->shutdown();
292 }
293
on_disconnect()294 void VmcChannelClient::on_disconnect()
295 {
296 RedVmcChannel *channel;
297 SpiceCharDeviceInterface *sif;
298 RedClient *client = get_client();
299
300 channel = get_channel();
301
302 /* partial message which wasn't pushed to device */
303 RedCharDevice::write_buffer_release(channel->chardev,
304 &channel->recv_from_client_buf);
305
306 if (channel->chardev) {
307 if (channel->chardev->client_exists((RedCharDeviceClientOpaque *)client)) {
308 channel->chardev->client_remove((RedCharDeviceClientOpaque *)client);
309 } else {
310 red_channel_warning(channel,
311 "client %p have already been removed from char dev %p",
312 client, channel->chardev);
313 }
314 }
315
316 channel->rcc = nullptr;
317 sif = spice_char_device_get_interface(channel->chardev_sin);
318 if (sif->state) {
319 sif->state(channel->chardev_sin, 0);
320 }
321 }
322
handle_migrate_flush_mark()323 void VmcChannelClient::handle_migrate_flush_mark()
324 {
325 pipe_add_type(RED_PIPE_ITEM_TYPE_SPICEVMC_MIGRATE_DATA);
326 }
327
handle_migrate_data(uint32_t size,void * message)328 bool VmcChannelClient::handle_migrate_data(uint32_t size, void *message)
329 {
330 SpiceMigrateDataHeader *header;
331 SpiceMigrateDataSpiceVmc *mig_data;
332 RedVmcChannel *channel;
333
334 channel = get_channel();
335
336 header = (SpiceMigrateDataHeader *)message;
337 mig_data = (SpiceMigrateDataSpiceVmc *)(header + 1);
338 spice_assert(size >= sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataSpiceVmc));
339
340 if (!migration_protocol_validate_header(header,
341 SPICE_MIGRATE_DATA_SPICEVMC_MAGIC,
342 SPICE_MIGRATE_DATA_SPICEVMC_VERSION)) {
343 spice_error("bad header");
344 return FALSE;
345 }
346 return channel->chardev->restore(&mig_data->base);
347 }
348
handle_compressed_msg(RedVmcChannel * channel,RedChannelClient * rcc,SpiceMsgCompressedData * compressed_data_msg)349 static bool handle_compressed_msg(RedVmcChannel *channel, RedChannelClient *rcc,
350 SpiceMsgCompressedData *compressed_data_msg)
351 {
352 /* NOTE: *decompressed is free by the char-device */
353 int decompressed_size;
354 RedCharDeviceWriteBuffer *write_buf;
355
356 write_buf = channel->chardev->write_buffer_get_server(compressed_data_msg->uncompressed_size,
357 false);
358 if (!write_buf) {
359 return FALSE;
360 }
361
362 switch (compressed_data_msg->type) {
363 #ifdef USE_LZ4
364 case SPICE_DATA_COMPRESSION_TYPE_LZ4: {
365 uint8_t *decompressed = write_buf->buf;
366 decompressed_size = LZ4_decompress_safe ((char *)compressed_data_msg->compressed_data,
367 (char *)decompressed,
368 compressed_data_msg->compressed_size,
369 compressed_data_msg->uncompressed_size);
370 stat_inc_counter(channel->in_compressed, compressed_data_msg->compressed_size);
371 stat_inc_counter(channel->in_decompressed, decompressed_size);
372 break;
373 }
374 #endif
375 default:
376 spice_warning("Invalid Compression Type");
377 RedCharDevice::write_buffer_release(channel->chardev, &write_buf);
378 return FALSE;
379 }
380 if (decompressed_size != compressed_data_msg->uncompressed_size) {
381 spice_warning("Decompression Error");
382 RedCharDevice::write_buffer_release(channel->chardev, &write_buf);
383 return FALSE;
384 }
385 write_buf->buf_used = decompressed_size;
386 channel->chardev->write_buffer_add(write_buf);
387 return TRUE;
388 }
389
handle_message(uint16_t type,uint32_t size,void * msg)390 bool VmcChannelClient::handle_message(uint16_t type, uint32_t size, void *msg)
391 {
392 /* NOTE: *msg free by g_free() (when cb to VmcChannelClient::release_recv_buf
393 * with the compressed msg type) */
394 RedVmcChannel *channel;
395 SpiceCharDeviceInterface *sif;
396
397 channel = get_channel();
398 sif = spice_char_device_get_interface(channel->chardev_sin);
399
400 switch (type) {
401 case SPICE_MSGC_SPICEVMC_DATA:
402 spice_assert(channel->recv_from_client_buf->buf == msg);
403 stat_inc_counter(channel->in_data, size);
404 channel->recv_from_client_buf->buf_used = size;
405 channel->chardev->write_buffer_add(channel->recv_from_client_buf);
406 channel->recv_from_client_buf = nullptr;
407 break;
408 case SPICE_MSGC_SPICEVMC_COMPRESSED_DATA:
409 return handle_compressed_msg(channel, this, (SpiceMsgCompressedData*)msg);
410 break;
411 case SPICE_MSGC_PORT_EVENT:
412 if (size != sizeof(uint8_t)) {
413 spice_warning("bad port event message size");
414 return FALSE;
415 }
416 if (sif->base.minor_version >= 2 && sif->event != nullptr)
417 sif->event(channel->chardev_sin, *(uint8_t*)msg);
418 break;
419 default:
420 return RedChannelClient::handle_message(type, size, msg);
421 }
422
423 return TRUE;
424 }
425
426 /* if device manage to send some data attempt to unblock the channel */
on_free_self_token()427 void RedCharDeviceSpiceVmc::on_free_self_token()
428 {
429 channel->rcc->unblock_read();
430 }
431
alloc_recv_buf(uint16_t type,uint32_t size)432 uint8_t *VmcChannelClient::alloc_recv_buf(uint16_t type, uint32_t size)
433 {
434
435 switch (type) {
436 case SPICE_MSGC_SPICEVMC_DATA: {
437 RedVmcChannel *channel = get_channel();
438
439 assert(!channel->recv_from_client_buf);
440
441 channel->recv_from_client_buf = channel->chardev->write_buffer_get_server(size,
442 true);
443 if (!channel->recv_from_client_buf) {
444 block_read();
445 return nullptr;
446 }
447 return channel->recv_from_client_buf->buf;
448 }
449
450 default:
451 return (uint8_t*) g_malloc(size);
452 }
453
454 }
455
release_recv_buf(uint16_t type,uint32_t size,uint8_t * msg)456 void VmcChannelClient::release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg)
457 {
458
459 switch (type) {
460 case SPICE_MSGC_SPICEVMC_DATA: {
461 RedVmcChannel *channel = get_channel();
462 /* buffer wasn't pushed to device */
463 RedCharDevice::write_buffer_release(channel->chardev,
464 &channel->recv_from_client_buf);
465 break;
466 }
467 default:
468 g_free(msg);
469 }
470 }
471
472 static void
spicevmc_red_channel_queue_data(RedVmcChannel * channel,red::shared_ptr<RedVmcPipeItem> && item)473 spicevmc_red_channel_queue_data(RedVmcChannel *channel, red::shared_ptr<RedVmcPipeItem>&& item)
474 {
475 channel->queued_data += item->buf_used;
476 channel->rcc->pipe_add_push(item);
477 }
478
spicevmc_red_channel_send_data(VmcChannelClient * rcc,SpiceMarshaller * m,RedPipeItem * item)479 static void spicevmc_red_channel_send_data(VmcChannelClient *rcc,
480 SpiceMarshaller *m,
481 RedPipeItem *item)
482 {
483 auto i = static_cast<RedVmcPipeItem*>(item);
484 RedVmcChannel *channel = rcc->get_channel();
485
486 /* for compatibility send using not compressed data message */
487 if (i->type == SPICE_DATA_COMPRESSION_TYPE_NONE) {
488 rcc->init_send_data(SPICE_MSG_SPICEVMC_DATA);
489 } else {
490 /* send as compressed */
491 rcc->init_send_data(SPICE_MSG_SPICEVMC_COMPRESSED_DATA);
492 SpiceMsgCompressedData compressed_msg = {
493 .type = i->type,
494 .uncompressed_size = i->uncompressed_data_size
495 };
496 spice_marshall_SpiceMsgCompressedData(m, &compressed_msg);
497 }
498 item->add_to_marshaller(m, i->buf, i->buf_used);
499
500 // account for sent data and wake up device if was blocked
501 uint32_t old_queued_data = channel->queued_data;
502 channel->queued_data -= i->buf_used;
503 if (channel->chardev &&
504 old_queued_data >= QUEUED_DATA_LIMIT && channel->queued_data < QUEUED_DATA_LIMIT) {
505 channel->chardev->wakeup();
506 }
507 }
508
spicevmc_red_channel_send_migrate_data(VmcChannelClient * rcc,SpiceMarshaller * m,RedPipeItem * item)509 static void spicevmc_red_channel_send_migrate_data(VmcChannelClient *rcc,
510 SpiceMarshaller *m,
511 RedPipeItem *item)
512 {
513 RedVmcChannel *channel;
514
515 channel = rcc->get_channel();
516 rcc->init_send_data(SPICE_MSG_MIGRATE_DATA);
517 spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_SPICEVMC_MAGIC);
518 spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_SPICEVMC_VERSION);
519
520 channel->chardev->migrate_data_marshall(m);
521 }
522
spicevmc_red_channel_send_port_init(RedChannelClient * rcc,SpiceMarshaller * m,RedPipeItem * item)523 static void spicevmc_red_channel_send_port_init(RedChannelClient *rcc,
524 SpiceMarshaller *m,
525 RedPipeItem *item)
526 {
527 auto i = static_cast<RedPortInitPipeItem*>(item);
528 SpiceMsgPortInit init;
529
530 rcc->init_send_data(SPICE_MSG_PORT_INIT);
531 init.name = (uint8_t *)i->name.get();
532 init.name_size = strlen(i->name.get()) + 1;
533 init.opened = i->opened;
534 spice_marshall_msg_port_init(m, &init);
535 }
536
spicevmc_red_channel_send_port_event(RedChannelClient * rcc,SpiceMarshaller * m,RedPipeItem * item)537 static void spicevmc_red_channel_send_port_event(RedChannelClient *rcc,
538 SpiceMarshaller *m,
539 RedPipeItem *item)
540 {
541 auto i = static_cast<RedPortEventPipeItem*>(item);
542 SpiceMsgPortEvent event;
543
544 rcc->init_send_data(SPICE_MSG_PORT_EVENT);
545 event.event = i->event;
546 spice_marshall_msg_port_event(m, &event);
547 }
548
send_item(RedPipeItem * item)549 void VmcChannelClient::send_item(RedPipeItem *item)
550 {
551 SpiceMarshaller *m = get_marshaller();
552
553 switch (item->type) {
554 case RED_PIPE_ITEM_TYPE_SPICEVMC_DATA:
555 spicevmc_red_channel_send_data(this, m, item);
556 break;
557 case RED_PIPE_ITEM_TYPE_SPICEVMC_MIGRATE_DATA:
558 spicevmc_red_channel_send_migrate_data(this, m, item);
559 break;
560 case RED_PIPE_ITEM_TYPE_PORT_INIT:
561 spicevmc_red_channel_send_port_init(this, m, item);
562 break;
563 case RED_PIPE_ITEM_TYPE_PORT_EVENT:
564 spicevmc_red_channel_send_port_event(this, m, item);
565 break;
566 default:
567 spice_error("bad pipe item %d", item->type);
568 return;
569 }
570 begin_send_message();
571 }
572
573
on_connect(RedClient * client,RedStream * stream,int migration,RedChannelCapabilities * caps)574 void RedVmcChannel::on_connect(RedClient *client, RedStream *stream, int migration,
575 RedChannelCapabilities *caps)
576 {
577 RedVmcChannel *vmc_channel;
578 SpiceCharDeviceInstance *sin;
579 SpiceCharDeviceInterface *sif;
580
581 vmc_channel = this;
582 sin = vmc_channel->chardev_sin;
583
584 if (rcc) {
585 red_channel_warning(this,
586 "channel client (%p) already connected, refusing second connection",
587 rcc);
588 // TODO: notify client in advance about the in use channel using
589 // SPICE_MSG_MAIN_CHANNEL_IN_USE (for example)
590 red_stream_free(stream);
591 return;
592 }
593
594 rcc = vmc_channel_client_create(this, client, stream, caps);
595 if (!rcc) {
596 return;
597 }
598 vmc_channel->queued_data = 0;
599 rcc->ack_zero_messages_window();
600
601 if (strcmp(sin->subtype, "port") == 0) {
602 spicevmc_port_send_init(rcc);
603 }
604
605 if (!vmc_channel->chardev->client_add((RedCharDeviceClientOpaque *)client, FALSE, 0, ~0, ~0, rcc->is_waiting_for_migrate_data())) {
606 spice_warning("failed to add client to spicevmc");
607 rcc->disconnect();
608 return;
609 }
610
611 sif = spice_char_device_get_interface(sin);
612 if (sif->state) {
613 sif->state(sin, 1);
614 }
615 }
616
617 red::shared_ptr<RedCharDevice>
spicevmc_device_connect(RedsState * reds,SpiceCharDeviceInstance * sin,uint8_t channel_type)618 spicevmc_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin, uint8_t channel_type)
619 {
620 auto channel(red_vmc_channel_new(reds, channel_type));
621 if (!channel) {
622 return red::shared_ptr<RedCharDevice>();
623 }
624
625 /* char device takes ownership of channel */
626 auto dev = red::make_shared<RedCharDeviceSpiceVmc>(sin, reds, channel.get());
627
628 channel->chardev_sin = sin;
629
630 return dev;
631 }
632
port_event(uint8_t event)633 void RedCharDeviceSpiceVmc::port_event(uint8_t event)
634 {
635 if (event == SPICE_PORT_EVENT_OPENED) {
636 channel->port_opened = TRUE;
637 } else if (event == SPICE_PORT_EVENT_CLOSED) {
638 channel->port_opened = FALSE;
639 }
640
641 if (channel->rcc == nullptr) {
642 return;
643 }
644
645 spicevmc_port_send_event(channel->rcc, event);
646 }
647
RedCharDeviceSpiceVmc(SpiceCharDeviceInstance * sin,RedsState * reds,RedVmcChannel * init_channel)648 RedCharDeviceSpiceVmc::RedCharDeviceSpiceVmc(SpiceCharDeviceInstance *sin, RedsState *reds,
649 RedVmcChannel *init_channel):
650 RedCharDevice(reds, sin, 0, 128),
651 channel(init_channel)
652 {
653 if (channel) {
654 channel->chardev = this;
655 }
656 }
657
~RedCharDeviceSpiceVmc()658 RedCharDeviceSpiceVmc::~RedCharDeviceSpiceVmc()
659 {
660 if (channel) {
661 // prevent possible recursive calls
662 channel->chardev = nullptr;
663
664 // close all current connections and drop the reference
665 channel->destroy();
666 }
667 }
668
669 static VmcChannelClient *
vmc_channel_client_create(RedChannel * channel,RedClient * client,RedStream * stream,RedChannelCapabilities * caps)670 vmc_channel_client_create(RedChannel *channel, RedClient *client,
671 RedStream *stream,
672 RedChannelCapabilities *caps)
673 {
674 auto rcc = red::make_shared<VmcChannelClient>(channel, client, stream, caps);
675 if (!rcc->init()) {
676 return nullptr;
677 }
678 return rcc.get();
679 }
680