/* coroutine context */ G_GNUC_INTERNAL void spice_channel_handle_migrate(SpiceChannel *channel, SpiceMsgIn *in) { SpiceMsgOut *out; SpiceMsgIn *data = NULL; SpiceMsgMigrate *mig = spice_msg_in_parsed(in); SpiceChannelPrivate *c = channel->priv; g_message("spice_channel_handle_migrate.\n"); CHANNEL_DEBUG(channel, "%s: flags %u", __FUNCTION__, mig->flags); if (mig->flags & SPICE_MIGRATE_NEED_FLUSH) { g_message("++++++++++++ SPICE_MIGRATE_NEED_FLUSH.\n"); /* if peer version > 1: pushing the mark msg before all other messgages and sending it, * and only it */ if (c->peer_hdr.major_version == 1) { /* iterate_write is blocking and flushing all pending write */ SPICE_CHANNEL_GET_CLASS(channel)->iterate_write(channel); } out = spice_msg_out_new(SPICE_CHANNEL(channel), SPICE_MSGC_MIGRATE_FLUSH_MARK); spice_msg_out_send_internal(out); } if (mig->flags & SPICE_MIGRATE_NEED_DATA_TRANSFER) { g_message("+++++++++++ SPICE_MIGRATE_NEED_DATA_TRANSFER.\n"); spice_channel_recv_msg(channel, get_msg_handler, &data); if (!data) { g_critical("expected SPICE_MSG_MIGRATE_DATA, got empty message"); goto end; } else if (spice_header_get_msg_type(data->header, c->use_mini_header) != SPICE_MSG_MIGRATE_DATA) { g_critical("expected SPICE_MSG_MIGRATE_DATA, got %d", spice_header_get_msg_type(data->header, c->use_mini_header)); goto end; } } /* swapping channels sockets */ spice_session_channel_migrate(c->session, channel); /* pushing the MIGRATE_DATA before all other pending messages */ if ((mig->flags & SPICE_MIGRATE_NEED_DATA_TRANSFER) && (data != NULL)) { out = spice_msg_out_new(SPICE_CHANNEL(channel), SPICE_MSGC_MIGRATE_DATA); spice_marshaller_add(out->marshaller, data->data, spice_header_get_msg_size(data->header, c->use_mini_header)); spice_msg_out_send_internal(out); } end: if (data) spice_msg_in_unref(data); }
G_GNUC_INTERNAL void spice_msg_in_unref(spice_msg_in *in) { g_return_if_fail(in != NULL); in->refcount--; if (in->refcount > 0) return; if (in->parsed) in->pfree(in->parsed); if (in->parent) { spice_msg_in_unref(in->parent); } else { free(in->data); } free(in); }
/* coroutine context */ G_GNUC_INTERNAL void spice_channel_recv_msg(SpiceChannel *channel, handler_msg_in msg_handler, gpointer data) { spice_channel *c = channel->priv; spice_msg_in *in; int rc; if (!c->msg_in) { c->msg_in = spice_msg_in_new(channel); } in = c->msg_in; /* receive message */ if (in->hpos < sizeof(in->header)) { rc = spice_channel_read(channel, (uint8_t*)&in->header + in->hpos, sizeof(in->header) - in->hpos); if (rc < 0) { g_critical("recv hdr: %s", strerror(errno)); return; } in->hpos += rc; if (in->hpos < sizeof(in->header)) return; in->data = spice_malloc(in->header.size); } if (in->dpos < in->header.size) { rc = spice_channel_read(channel, in->data + in->dpos, in->header.size - in->dpos); if (rc < 0) { g_critical("recv msg: %s", strerror(errno)); return; } in->dpos += rc; if (in->dpos < in->header.size) return; } if (in->header.sub_list) { SpiceSubMessageList *sub_list; SpiceSubMessage *sub; spice_msg_in *sub_in; int i; sub_list = (SpiceSubMessageList *)(in->data + in->header.sub_list); for (i = 0; i < sub_list->size; i++) { sub = (SpiceSubMessage *)(in->data + sub_list->sub_messages[i]); sub_in = spice_msg_in_sub_new(channel, in, sub); sub_in->parsed = c->parser(sub_in->data, sub_in->data + sub_in->dpos, sub_in->header.type, c->peer_hdr.minor_version, &sub_in->psize, &sub_in->pfree); if (sub_in->parsed == NULL) { g_critical("failed to parse sub-message: %s type %d", c->name, sub_in->header.type); return; } msg_handler(channel, sub_in, data); spice_msg_in_unref(sub_in); } } /* ack message */ if (c->message_ack_count) { c->message_ack_count--; if (!c->message_ack_count) { spice_msg_out *out = spice_msg_out_new(channel, SPICE_MSGC_ACK); spice_msg_out_send_internal(out); spice_msg_out_unref(out); c->message_ack_count = c->message_ack_window; } } /* parse message */ in->parsed = c->parser(in->data, in->data + in->dpos, in->header.type, c->peer_hdr.minor_version, &in->psize, &in->pfree); if (in->parsed == NULL) { g_critical("failed to parse message: %s type %d", c->name, in->header.type); return; } /* process message */ c->msg_in = NULL; /* the function is reentrant, reset state */ msg_handler(channel, in, data); /* release message */ spice_msg_in_unref(in); }