Пример #1
0
/* 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);
}