/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* Copyright (C) 2009 Red Hat, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "server/red_common.h" #include "server/demarshallers.h" #include "common/ring.h" #include "common/messages.h" #include "reds.h" #include "main_channel.h" #include "red_channel.h" #include "generated_marshallers.h" #define ZERO_BUF_SIZE 4096 // approximate max receive message size for main channel #define RECEIVE_BUF_SIZE \ (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE) #define REDS_MAX_SEND_IOVEC 100 #define NET_TEST_WARMUP_BYTES 0 #define NET_TEST_BYTES (1024 * 250) static uint8_t zero_page[ZERO_BUF_SIZE] = {0}; typedef struct RedsOutItem RedsOutItem; struct RedsOutItem { PipeItem base; }; typedef struct PingPipeItem { PipeItem base; int size; } PingPipeItem; typedef struct MouseModePipeItem { PipeItem base; int current_mode; int is_client_mouse_allowed; } MouseModePipeItem; typedef struct TokensPipeItem { PipeItem base; int tokens; } TokensPipeItem; typedef struct AgentDataPipeItem { PipeItem base; uint8_t* data; size_t len; spice_marshaller_item_free_func free_data; void *opaque; } AgentDataPipeItem; typedef struct InitPipeItem { PipeItem base; int connection_id; int display_channels_hint; int current_mouse_mode; int is_client_mouse_allowed; int multi_media_time; int ram_hint; } InitPipeItem; typedef struct NotifyPipeItem { PipeItem base; uint8_t *mess; int mess_len; } NotifyPipeItem; typedef struct MigrateBeginPipeItem { PipeItem base; int port; int sport; char *host; uint16_t cert_pub_key_type; uint32_t cert_pub_key_len; uint8_t *cert_pub_key; } MigrateBeginPipeItem; typedef struct MultiMediaTimePipeItem { PipeItem base; int time; } MultiMediaTimePipeItem; struct MainChannelClient { RedChannelClient base; uint32_t ping_id; uint32_t net_test_id; int net_test_stage; }; struct MainChannel { RedChannel base; uint8_t recv_buf[RECEIVE_BUF_SIZE]; }; enum NetTestStage { NET_TEST_STAGE_INVALID, NET_TEST_STAGE_WARMUP, NET_TEST_STAGE_LATENCY, NET_TEST_STAGE_RATE, }; static uint64_t latency = 0; uint64_t bitrate_per_sec = ~0; static void main_channel_client_disconnect(RedChannelClient *rcc) { red_channel_client_disconnect(rcc); } static void main_disconnect(MainChannel *main_chan) { red_channel_destroy(&main_chan->base); latency = 0; bitrate_per_sec = ~0; } static int main_channel_client_push_ping(RedChannelClient *rcc, int size); void main_channel_start_net_test(MainChannelClient *mcc) { if (!mcc) { return; } if (main_channel_client_push_ping(&mcc->base, NET_TEST_WARMUP_BYTES) && main_channel_client_push_ping(&mcc->base, 0) && main_channel_client_push_ping(&mcc->base, NET_TEST_BYTES)) { mcc->net_test_id = mcc->ping_id - 2; mcc->net_test_stage = NET_TEST_STAGE_WARMUP; } } static MouseModePipeItem *main_mouse_mode_item_new(RedChannelClient *rcc, int current_mode, int is_client_mouse_allowed) { MouseModePipeItem *item = spice_malloc(sizeof(MouseModePipeItem)); red_channel_pipe_item_init(rcc->channel, &item->base, SPICE_MSG_MAIN_MOUSE_MODE); item->current_mode = current_mode; item->is_client_mouse_allowed = is_client_mouse_allowed; return item; } static PingPipeItem *main_ping_item_new(RedChannelClient *rcc, int size) { PingPipeItem *item = spice_malloc(sizeof(PingPipeItem)); red_channel_pipe_item_init(rcc->channel, &item->base, SPICE_MSG_PING); item->size = size; return item; } static TokensPipeItem *main_tokens_item_new(RedChannelClient *rcc, int tokens) { TokensPipeItem *item = spice_malloc(sizeof(TokensPipeItem)); red_channel_pipe_item_init(rcc->channel, &item->base, SPICE_MSG_MAIN_AGENT_TOKEN); item->tokens = tokens; return item; } static AgentDataPipeItem *main_agent_data_item_new(RedChannelClient *rcc, uint8_t* data, size_t len, spice_marshaller_item_free_func free_data, void *opaque) { AgentDataPipeItem *item = spice_malloc(sizeof(AgentDataPipeItem)); red_channel_pipe_item_init(rcc->channel, &item->base, SPICE_MSG_MAIN_AGENT_DATA); item->data = data; item->len = len; item->free_data = free_data; item->opaque = opaque; return item; } static InitPipeItem *main_init_item_new(RedChannelClient *rcc, int connection_id, int display_channels_hint, int current_mouse_mode, int is_client_mouse_allowed, int multi_media_time, int ram_hint) { InitPipeItem *item = spice_malloc(sizeof(InitPipeItem)); red_channel_pipe_item_init(rcc->channel, &item->base, SPICE_MSG_MAIN_INIT); item->connection_id = connection_id; item->display_channels_hint = display_channels_hint; item->current_mouse_mode = current_mouse_mode; item->is_client_mouse_allowed = is_client_mouse_allowed; item->multi_media_time = multi_media_time; item->ram_hint = ram_hint; return item; } static NotifyPipeItem *main_notify_item_new(RedChannelClient *rcc, uint8_t *mess, const int mess_len) { NotifyPipeItem *item = spice_malloc(sizeof(NotifyPipeItem)); red_channel_pipe_item_init(rcc->channel, &item->base, SPICE_MSG_NOTIFY); item->mess = mess; item->mess_len = mess_len; return item; } static MigrateBeginPipeItem *main_migrate_begin_item_new( RedChannelClient *rcc, int port, int sport, char *host, uint16_t cert_pub_key_type, uint32_t cert_pub_key_len, uint8_t *cert_pub_key) { MigrateBeginPipeItem *item = spice_malloc(sizeof(MigrateBeginPipeItem)); red_channel_pipe_item_init(rcc->channel, &item->base, SPICE_MSG_MAIN_MIGRATE_BEGIN); item->port = port; item->sport = sport; item->host = host; item->cert_pub_key_type = cert_pub_key_type; item->cert_pub_key_len = cert_pub_key_len; item->cert_pub_key = cert_pub_key; return item; } static MultiMediaTimePipeItem *main_multi_media_time_item_new( RedChannelClient *rcc, int time) { MultiMediaTimePipeItem *item; item = spice_malloc(sizeof(MultiMediaTimePipeItem)); red_channel_pipe_item_init(rcc->channel, &item->base, SPICE_MSG_MAIN_MULTI_MEDIA_TIME); item->time = time; return item; } static void main_channel_push_channels(RedChannelClient *rcc) { red_channel_client_pipe_add_type(rcc, SPICE_MSG_MAIN_CHANNELS_LIST); } static void main_channel_marshall_channels(SpiceMarshaller *m) { SpiceMsgChannels* channels_info; channels_info = (SpiceMsgChannels *)spice_malloc(sizeof(SpiceMsgChannels) + reds_num_of_channels() * sizeof(SpiceChannelId)); reds_fill_channels(channels_info); spice_marshall_msg_main_channels_list(m, channels_info); free(channels_info); } int main_channel_client_push_ping(RedChannelClient *rcc, int size) { PingPipeItem *item; item = main_ping_item_new(rcc, size); red_channel_client_pipe_add_push(rcc, &item->base); return TRUE; } int main_channel_push_ping(MainChannel *main_chan, int size) { if (main_chan->base.rcc == NULL) { return FALSE; } return main_channel_client_push_ping(main_chan->base.rcc, size); } static void main_channel_marshall_ping(SpiceMarshaller *m, int size, int ping_id) { struct timespec time_space; SpiceMsgPing ping; ping.id = ping_id; clock_gettime(CLOCK_MONOTONIC, &time_space); ping.timestamp = time_space.tv_sec * 1000000LL + time_space.tv_nsec / 1000LL; spice_marshall_msg_ping(m, &ping); while (size > 0) { int now = MIN(ZERO_BUF_SIZE, size); size -= now; spice_marshaller_add_ref(m, zero_page, now); } } static void main_channel_client_push_mouse_mode(RedChannelClient *rcc, int current_mode, int is_client_mouse_allowed); void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode, int is_client_mouse_allowed) { if (main_chan && main_chan->base.rcc != NULL) { main_channel_client_push_mouse_mode(main_chan->base.rcc, current_mode, is_client_mouse_allowed); } } static void main_channel_client_push_mouse_mode(RedChannelClient *rcc, int current_mode, int is_client_mouse_allowed) { MouseModePipeItem *item; item = main_mouse_mode_item_new(rcc, current_mode, is_client_mouse_allowed); red_channel_client_pipe_add_push(rcc, &item->base); } static void main_channel_marshall_mouse_mode(SpiceMarshaller *m, int current_mode, int is_client_mouse_allowed) { SpiceMsgMainMouseMode mouse_mode; mouse_mode.supported_modes = SPICE_MOUSE_MODE_SERVER; if (is_client_mouse_allowed) { mouse_mode.supported_modes |= SPICE_MOUSE_MODE_CLIENT; } mouse_mode.current_mode = current_mode; spice_marshall_msg_main_mouse_mode(m, &mouse_mode); } void main_channel_push_agent_connected(MainChannel *main_chan) { RedChannelClient *rcc = main_chan->base.rcc; red_channel_client_pipe_add_type(rcc, SPICE_MSG_MAIN_AGENT_CONNECTED); } void main_channel_push_agent_disconnected(MainChannel *main_chan) { RedChannelClient *rcc = main_chan->base.rcc; if (!rcc) { return; } red_channel_client_pipe_add_type(rcc, SPICE_MSG_MAIN_AGENT_DISCONNECTED); } static void main_channel_marshall_agent_disconnected(SpiceMarshaller *m) { SpiceMsgMainAgentDisconnect disconnect; disconnect.error_code = SPICE_LINK_ERR_OK; spice_marshall_msg_main_agent_disconnected(m, &disconnect); } void main_channel_push_tokens(MainChannel *main_chan, uint32_t num_tokens) { TokensPipeItem *item; RedChannelClient *rcc = main_chan->base.rcc; if (!rcc) { return; } item = main_tokens_item_new(rcc, num_tokens); red_channel_client_pipe_add_push(rcc, &item->base); } static void main_channel_marshall_tokens(SpiceMarshaller *m, uint32_t num_tokens) { SpiceMsgMainAgentTokens tokens; tokens.num_tokens = num_tokens; spice_marshall_msg_main_agent_token(m, &tokens); } void main_channel_push_agent_data(MainChannel *main_chan, uint8_t* data, size_t len, spice_marshaller_item_free_func free_data, void *opaque) { RedChannelClient *rcc = main_chan->base.rcc; AgentDataPipeItem *item; item = main_agent_data_item_new(rcc, data, len, free_data, opaque); red_channel_client_pipe_add_push(rcc, &item->base); } static void main_channel_marshall_agent_data(SpiceMarshaller *m, AgentDataPipeItem *item) { spice_marshaller_add_ref_full(m, item->data, item->len, item->free_data, item->opaque); } static void main_channel_push_migrate_data_item(MainChannel *main_chan) { RedChannelClient *rcc = main_chan->base.rcc; if (!rcc) { return; } red_channel_client_pipe_add_type(rcc, SPICE_MSG_MIGRATE_DATA); } static void main_channel_marshall_migrate_data_item(SpiceMarshaller *m, int serial, int ping_id) { MainMigrateData *data = (MainMigrateData *)spice_marshaller_reserve_space(m, sizeof(MainMigrateData)); reds_marshall_migrate_data_item(m, data); // TODO: from reds split. ugly separation. data->serial = serial; data->ping_id = ping_id; } static uint64_t main_channel_handle_migrate_data_get_serial( RedChannelClient *rcc, uint32_t size, void *message) { MainMigrateData *data = message; if (size < sizeof(*data)) { red_printf("bad message size"); return 0; } return data->serial; } static uint64_t main_channel_handle_migrate_data(RedChannelClient *rcc, uint32_t size, void *message) { MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base); MainMigrateData *data = message; if (size < sizeof(*data)) { red_printf("bad message size"); return FALSE; } mcc->ping_id = data->ping_id; reds_on_main_receive_migrate_data(data, ((uint8_t*)message) + size); return TRUE; } void main_channel_push_init(MainChannelClient *mcc, int connection_id, int display_channels_hint, int current_mouse_mode, int is_client_mouse_allowed, int multi_media_time, int ram_hint) { InitPipeItem *item; item = main_init_item_new(&mcc->base, connection_id, display_channels_hint, current_mouse_mode, is_client_mouse_allowed, multi_media_time, ram_hint); red_channel_client_pipe_add_push(&mcc->base, &item->base); } static void main_channel_marshall_init(SpiceMarshaller *m, InitPipeItem *item) { SpiceMsgMainInit init; init.session_id = item->connection_id; init.display_channels_hint = item->display_channels_hint; init.current_mouse_mode = item->current_mouse_mode; init.supported_mouse_modes = SPICE_MOUSE_MODE_SERVER; if (item->is_client_mouse_allowed) { init.supported_mouse_modes |= SPICE_MOUSE_MODE_CLIENT; } init.agent_connected = reds_has_vdagent(); init.agent_tokens = REDS_AGENT_WINDOW_SIZE; init.multi_media_time = item->multi_media_time; init.ram_hint = item->ram_hint; spice_marshall_msg_main_init(m, &init); } void main_channel_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len) { RedChannelClient *rcc = main_chan->base.rcc; NotifyPipeItem *item; item = main_notify_item_new(rcc, mess, mess_len); red_channel_client_pipe_add_push(rcc, &item->base); } static uint64_t get_time_stamp(void) { struct timespec time_space; clock_gettime(CLOCK_MONOTONIC, &time_space); return time_space.tv_sec * 1000 * 1000 * 1000 + time_space.tv_nsec; } static void main_channel_marshall_notify(SpiceMarshaller *m, NotifyPipeItem *item) { SpiceMsgNotify notify; notify.time_stamp = get_time_stamp(); // TODO - move to main_new_notify_item notify.severity = SPICE_NOTIFY_SEVERITY_WARN; notify.visibilty = SPICE_NOTIFY_VISIBILITY_HIGH; notify.what = SPICE_WARN_GENERAL; notify.message_len = item->mess_len; spice_marshall_msg_notify(m, ¬ify); spice_marshaller_add(m, item->mess, item->mess_len + 1); } void main_channel_push_migrate_begin(MainChannel *main_chan, int port, int sport, char *host, uint16_t cert_pub_key_type, uint32_t cert_pub_key_len, uint8_t *cert_pub_key) { RedChannelClient *rcc = main_chan->base.rcc; MigrateBeginPipeItem *item; item = main_migrate_begin_item_new(rcc, port, sport, host, cert_pub_key_type, cert_pub_key_len, cert_pub_key); red_channel_client_pipe_add_push(rcc, &item->base); } static void main_channel_marshall_migrate_begin(SpiceMarshaller *m, MigrateBeginPipeItem *item) { SpiceMsgMainMigrationBegin migrate; migrate.port = item->port; migrate.sport = item->sport; migrate.host_size = strlen(item->host) + 1; migrate.host_data = (uint8_t *)item->host; migrate.pub_key_type = item->cert_pub_key_type; migrate.pub_key_size = item->cert_pub_key_len; migrate.pub_key_data = item->cert_pub_key; spice_marshall_msg_main_migrate_begin(m, &migrate); } void main_channel_push_migrate(MainChannel *main_chan) { RedChannelClient *rcc = main_chan->base.rcc; if (!rcc) { return; } red_channel_client_pipe_add_type(rcc, SPICE_MSG_MIGRATE); } static void main_channel_marshall_migrate(SpiceMarshaller *m) { SpiceMsgMigrate migrate; migrate.flags = SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER; spice_marshall_msg_migrate(m, &migrate); } void main_channel_push_migrate_cancel(MainChannel *main_chan) { RedChannelClient *rcc = main_chan->base.rcc; if (!rcc) { return; } red_channel_client_pipe_add_type(rcc, SPICE_MSG_MAIN_MIGRATE_CANCEL); } void main_channel_push_multi_media_time(MainChannel *main_chan, int time) { RedChannelClient *rcc = main_chan->base.rcc; MultiMediaTimePipeItem *item; item =main_multi_media_time_item_new(rcc, time); red_channel_client_pipe_add_push(rcc, &item->base); } static PipeItem *main_migrate_switch_item_new(MainChannel *main_chan) { PipeItem *item = spice_malloc(sizeof(*item)); red_channel_pipe_item_init(&main_chan->base, item, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST); return item; } void main_channel_push_migrate_switch(MainChannel *main_chan) { RedChannelClient *rcc = main_chan->base.rcc; red_channel_client_pipe_add_push(rcc, main_migrate_switch_item_new(main_chan)); } static void main_channel_marshall_migrate_switch(SpiceMarshaller *m) { SpiceMsgMainMigrationSwitchHost migrate; red_printf(""); reds_fill_mig_switch(&migrate); spice_marshall_msg_main_migrate_switch_host(m, &migrate); reds_mig_release(); } static void main_channel_marshall_multi_media_time(SpiceMarshaller *m, MultiMediaTimePipeItem *item) { SpiceMsgMainMultiMediaTime time_mes; time_mes.time = item->time; spice_marshall_msg_main_multi_media_time(m, &time_mes); } static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base) { MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base); SpiceMarshaller *m = red_channel_client_get_marshaller(rcc); red_channel_client_init_send_data(rcc, base->type, base); switch (base->type) { case SPICE_MSG_MAIN_CHANNELS_LIST: main_channel_marshall_channels(m); break; case SPICE_MSG_PING: main_channel_marshall_ping(m, SPICE_CONTAINEROF(base, PingPipeItem, base)->size, ++(mcc->ping_id)); break; case SPICE_MSG_MAIN_MOUSE_MODE: { MouseModePipeItem *item = SPICE_CONTAINEROF(base, MouseModePipeItem, base); main_channel_marshall_mouse_mode(m, item->current_mode, item->is_client_mouse_allowed); break; } case SPICE_MSG_MAIN_AGENT_DISCONNECTED: main_channel_marshall_agent_disconnected(m); break; case SPICE_MSG_MAIN_AGENT_TOKEN: main_channel_marshall_tokens(m, SPICE_CONTAINEROF(base, TokensPipeItem, base)->tokens); break; case SPICE_MSG_MAIN_AGENT_DATA: main_channel_marshall_agent_data(m, SPICE_CONTAINEROF(base, AgentDataPipeItem, base)); break; case SPICE_MSG_MIGRATE_DATA: main_channel_marshall_migrate_data_item(m, red_channel_client_get_message_serial(rcc), mcc->ping_id); break; case SPICE_MSG_MAIN_INIT: main_channel_marshall_init(m, SPICE_CONTAINEROF(base, InitPipeItem, base)); break; case SPICE_MSG_NOTIFY: main_channel_marshall_notify(m, SPICE_CONTAINEROF(base, NotifyPipeItem, base)); break; case SPICE_MSG_MIGRATE: main_channel_marshall_migrate(m); break; case SPICE_MSG_MAIN_MIGRATE_BEGIN: main_channel_marshall_migrate_begin(m, SPICE_CONTAINEROF(base, MigrateBeginPipeItem, base)); break; case SPICE_MSG_MAIN_MULTI_MEDIA_TIME: main_channel_marshall_multi_media_time(m, SPICE_CONTAINEROF(base, MultiMediaTimePipeItem, base)); break; case SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST: main_channel_marshall_migrate_switch(m); break; }; red_channel_client_begin_send_message(rcc); } static void main_channel_release_pipe_item(RedChannelClient *rcc, PipeItem *base, int item_pushed) { free(base); } static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message) { MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base); MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base); switch (type) { case SPICE_MSGC_MAIN_AGENT_START: red_printf("agent start"); if (!main_chan) { return FALSE; } reds_on_main_agent_start(); break; case SPICE_MSGC_MAIN_AGENT_DATA: { reds_on_main_agent_data(message, size); break; } case SPICE_MSGC_MAIN_AGENT_TOKEN: break; case SPICE_MSGC_MAIN_ATTACH_CHANNELS: main_channel_push_channels(rcc); break; case SPICE_MSGC_MAIN_MIGRATE_CONNECTED: red_printf("connected"); reds_on_main_migrate_connected(); break; case SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR: red_printf("mig connect error"); reds_on_main_migrate_connect_error(); break; case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST: reds_on_main_mouse_mode_request(message, size); break; case SPICE_MSGC_PONG: { SpiceMsgPing *ping = (SpiceMsgPing *)message; uint64_t roundtrip; struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); roundtrip = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL - ping->timestamp; if (ping->id == mcc->net_test_id) { switch (mcc->net_test_stage) { case NET_TEST_STAGE_WARMUP: mcc->net_test_id++; mcc->net_test_stage = NET_TEST_STAGE_LATENCY; break; case NET_TEST_STAGE_LATENCY: mcc->net_test_id++; mcc->net_test_stage = NET_TEST_STAGE_RATE; latency = roundtrip; break; case NET_TEST_STAGE_RATE: mcc->net_test_id = 0; if (roundtrip <= latency) { // probably high load on client or server result with incorrect values latency = 0; red_printf("net test: invalid values, latency %lu roundtrip %lu. assuming high" "bandwidth", latency, roundtrip); break; } bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000 / (roundtrip - latency); red_printf("net test: latency %f ms, bitrate %lu bps (%f Mbps)%s", (double)latency / 1000, bitrate_per_sec, (double)bitrate_per_sec / 1024 / 1024, IS_LOW_BANDWIDTH() ? " LOW BANDWIDTH" : ""); mcc->net_test_stage = NET_TEST_STAGE_INVALID; break; default: red_printf("invalid net test stage, ping id %d test id %d stage %d", ping->id, mcc->net_test_id, mcc->net_test_stage); } break; } #ifdef RED_STATISTICS reds_update_stat_value(roundtrip); #endif break; } case SPICE_MSGC_MIGRATE_FLUSH_MARK: break; case SPICE_MSGC_MIGRATE_DATA: { } case SPICE_MSGC_DISCONNECTING: break; default: red_printf("unexpected type %d", type); } return TRUE; } static void main_channel_on_error(RedChannelClient *rcc) { reds_client_disconnect(rcc->client); } static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, SpiceDataHeader *msg_header) { MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base); return main_chan->recv_buf; } static void main_channel_release_msg_rcv_buf(RedChannelClient *rcc, SpiceDataHeader *msg_header, uint8_t *msg) { } static int main_channel_config_socket(RedChannelClient *rcc) { return TRUE; } static void main_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item) { } static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc) { main_channel_push_migrate_data_item(SPICE_CONTAINEROF(rcc->channel, MainChannel, base)); return TRUE; } MainChannelClient *main_channel_link(Channel *channel, RedClient *client, RedsStream *stream, int migration, int num_common_caps, uint32_t *common_caps, int num_caps, uint32_t *caps) { MainChannel *main_chan; MainChannelClient *mcc; if (channel->data == NULL) { red_printf("create main channel"); channel->data = red_channel_create_parser( sizeof(*main_chan), core, migration, FALSE /* handle_acks */ ,main_channel_config_socket ,main_channel_client_disconnect ,spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL) ,main_channel_handle_parsed ,main_channel_alloc_msg_rcv_buf ,main_channel_release_msg_rcv_buf ,main_channel_hold_pipe_item ,main_channel_send_item ,main_channel_release_pipe_item ,main_channel_on_error ,main_channel_on_error ,main_channel_handle_migrate_flush_mark ,main_channel_handle_migrate_data ,main_channel_handle_migrate_data_get_serial); ASSERT(channel->data); } main_chan = (MainChannel*)channel->data; red_printf("add main channel client"); mcc = (MainChannelClient*) red_channel_client_create(sizeof(MainChannelClient), &main_chan->base, client, stream); return mcc; } int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen) { return main_chan ? getsockname(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1; } int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen) { return main_chan ? getpeername(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1; } void main_channel_close(MainChannel *main_chan) { int socketfd; if (main_chan && (socketfd = red_channel_get_first_socket(&main_chan->base)) != -1) { close(socketfd); } } static void main_channel_shutdown(Channel *channel) { MainChannel *main_chan = channel->data; if (main_chan != NULL) { main_disconnect(main_chan); } } static void main_channel_migrate() { } Channel* main_channel_init(void) { Channel *channel; channel = spice_new0(Channel, 1); channel->type = SPICE_CHANNEL_MAIN; channel->link = NULL; /* the main channel client is created by reds.c explicitly */ channel->shutdown = main_channel_shutdown; channel->migrate = main_channel_migrate; return channel; }