[Spice-devel] [PATCH 02/11] Move MainChannelClient to separate file
Jonathon Jongsma
jjongsma at redhat.com
Thu May 12 20:45:09 UTC 2016
Preparation for converting to GObject
---
server/Makefile.am | 2 +
server/inputs-channel.c | 2 +-
server/main-channel-client.c | 549 ++++++++++++++++++++++++++++++++++++++
server/main-channel-client.h | 153 +++++++++++
server/main-channel.c | 616 +++----------------------------------------
server/main-channel.h | 27 +-
6 files changed, 747 insertions(+), 602 deletions(-)
create mode 100644 server/main-channel-client.c
create mode 100644 server/main-channel-client.h
diff --git a/server/Makefile.am b/server/Makefile.am
index fbf4638..5a5c7e8 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -86,6 +86,8 @@ libserver_la_SOURCES = \
lz4-encoder.h \
main-channel.c \
main-channel.h \
+ main-channel-client.c \
+ main-channel-client.h \
mjpeg-encoder.c \
red-channel.c \
red-channel.h \
diff --git a/server/inputs-channel.c b/server/inputs-channel.c
index 0ce12de..584204f 100644
--- a/server/inputs-channel.c
+++ b/server/inputs-channel.c
@@ -39,7 +39,7 @@
#include "reds.h"
#include "reds-stream.h"
#include "red-channel.h"
-#include "main-channel.h"
+#include "main-channel-client.h"
#include "inputs-channel.h"
#include "migration-protocol.h"
#include "utils.h"
diff --git a/server/main-channel-client.c b/server/main-channel-client.c
new file mode 100644
index 0000000..bac5316
--- /dev/null
+++ b/server/main-channel-client.c
@@ -0,0 +1,549 @@
+/*
+ Copyright (C) 2009-2015 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 <http://www.gnu.org/licenses/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <inttypes.h>
+#include "main-channel-client.h"
+#include "main-channel-client.h"
+#include "main-channel.h"
+#include "reds.h"
+
+#define NET_TEST_WARMUP_BYTES 0
+#define NET_TEST_BYTES (1024 * 250)
+
+enum NetTestStage {
+ NET_TEST_STAGE_INVALID,
+ NET_TEST_STAGE_WARMUP,
+ NET_TEST_STAGE_LATENCY,
+ NET_TEST_STAGE_RATE,
+ NET_TEST_STAGE_COMPLETE,
+};
+
+#define CLIENT_CONNECTIVITY_TIMEOUT (MSEC_PER_SEC * 30)
+#define PING_INTERVAL (MSEC_PER_SEC * 10)
+
+struct MainChannelClient {
+ RedChannelClient base;
+ uint32_t connection_id;
+ uint32_t ping_id;
+ uint32_t net_test_id;
+ int net_test_stage;
+ uint64_t latency;
+ uint64_t bitrate_per_sec;
+#ifdef RED_STATISTICS
+ SpiceTimer *ping_timer;
+ int ping_interval;
+#endif
+ int mig_wait_connect;
+ int mig_connect_ok;
+ int mig_wait_prev_complete;
+ int mig_wait_prev_try_seamless;
+ int init_sent;
+ int seamless_mig_dst;
+};
+
+static int main_channel_client_push_ping(MainChannelClient *mcc, int size);
+
+static RedPipeItem *main_notify_item_new(void *data, int num)
+{
+ RedNotifyPipeItem *item = spice_malloc(sizeof(RedNotifyPipeItem));
+ const char *msg = data;
+
+ red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NOTIFY);
+ item->msg = spice_strdup(msg);
+ return &item->base;
+}
+
+void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate)
+{
+ if (!mcc || mcc->net_test_id) {
+ return;
+ }
+ if (test_rate) {
+ if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES)
+ && main_channel_client_push_ping(mcc, 0)
+ && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) {
+ mcc->net_test_id = mcc->ping_id - 2;
+ mcc->net_test_stage = NET_TEST_STAGE_WARMUP;
+ }
+ } else {
+ red_channel_client_start_connectivity_monitoring(&mcc->base, CLIENT_CONNECTIVITY_TIMEOUT);
+ }
+}
+
+static RedPipeItem *red_ping_item_new(int size)
+{
+ RedPingPipeItem *item = spice_malloc(sizeof(RedPingPipeItem));
+
+ red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_PING);
+ item->size = size;
+ return &item->base;
+}
+
+static int main_channel_client_push_ping(MainChannelClient *mcc, int size)
+{
+ RedPipeItem *item;
+
+ if (mcc == NULL) {
+ return FALSE;
+ }
+ item = red_ping_item_new(size);
+ red_channel_client_pipe_add_push(&mcc->base, item);
+ return TRUE;
+}
+
+static RedPipeItem *main_agent_tokens_item_new(uint32_t num_tokens)
+{
+ RedTokensPipeItem *item = spice_malloc(sizeof(RedTokensPipeItem));
+
+ red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN);
+ item->tokens = num_tokens;
+ return &item->base;
+}
+
+
+void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens)
+{
+ RedPipeItem *item = main_agent_tokens_item_new(num_tokens);
+
+ red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+static RedPipeItem *main_agent_data_item_new(uint8_t* data, size_t len,
+ spice_marshaller_item_free_func free_data,
+ void *opaque)
+{
+ RedAgentDataPipeItem *item = spice_malloc(sizeof(RedAgentDataPipeItem));
+
+ red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA);
+ item->data = data;
+ item->len = len;
+ item->free_data = free_data;
+ item->opaque = opaque;
+ return &item->base;
+}
+
+void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
+ spice_marshaller_item_free_func free_data, void *opaque)
+{
+ RedPipeItem *item;
+
+ item = main_agent_data_item_new(data, len, free_data, opaque);
+ red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+static RedPipeItem *main_init_item_new(int connection_id,
+ int display_channels_hint,
+ int current_mouse_mode,
+ int is_client_mouse_allowed,
+ int multi_media_time,
+ int ram_hint)
+{
+ RedInitPipeItem *item = spice_malloc(sizeof(RedInitPipeItem));
+
+ red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_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->base;
+}
+
+void main_channel_client_push_init(MainChannelClient *mcc,
+ int display_channels_hint,
+ int current_mouse_mode,
+ int is_client_mouse_allowed,
+ int multi_media_time,
+ int ram_hint)
+{
+ RedPipeItem *item;
+
+ item = main_init_item_new(mcc->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);
+}
+
+static RedPipeItem *main_name_item_new(const char *name)
+{
+ RedNamePipeItem *item = spice_malloc(sizeof(RedNamePipeItem) + strlen(name) + 1);
+
+ red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NAME);
+ item->msg.name_len = strlen(name) + 1;
+ memcpy(&item->msg.name, name, item->msg.name_len);
+
+ return &item->base;
+}
+
+void main_channel_client_push_name(MainChannelClient *mcc, const char *name)
+{
+ RedPipeItem *item;
+
+ if (!red_channel_client_test_remote_cap(&mcc->base,
+ SPICE_MAIN_CAP_NAME_AND_UUID))
+ return;
+
+ item = main_name_item_new(name);
+ red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+static RedPipeItem *main_uuid_item_new(const uint8_t uuid[16])
+{
+ RedUuidPipeItem *item = spice_malloc(sizeof(RedUuidPipeItem));
+
+ red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_UUID);
+ memcpy(item->msg.uuid, uuid, sizeof(item->msg.uuid));
+
+ return &item->base;
+}
+
+void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16])
+{
+ RedPipeItem *item;
+
+ if (!red_channel_client_test_remote_cap(&mcc->base,
+ SPICE_MAIN_CAP_NAME_AND_UUID))
+ return;
+
+ item = main_uuid_item_new(uuid);
+ red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg)
+{
+ RedPipeItem *item = main_notify_item_new((void *)msg, 1);
+ red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
+ int success,
+ int seamless)
+{
+ spice_printerr("client %p connected: %d seamless %d", mcc->base.client, success, seamless);
+ if (mcc->mig_wait_connect) {
+ MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel, MainChannel, base);
+
+ mcc->mig_wait_connect = FALSE;
+ mcc->mig_connect_ok = success;
+ spice_assert(main_channel->num_clients_mig_wait);
+ spice_assert(!seamless || main_channel->num_clients_mig_wait == 1);
+ if (!--main_channel->num_clients_mig_wait) {
+ reds_on_main_migrate_connected(mcc->base.channel->reds, seamless && success);
+ }
+ } else {
+ if (success) {
+ spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
+ red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
+ }
+ }
+}
+
+void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc,
+ uint32_t src_version)
+{
+ if (reds_on_migrate_dst_set_seamless(mcc->base.channel->reds, mcc, src_version)) {
+ mcc->seamless_mig_dst = TRUE;
+ red_channel_client_pipe_add_empty_msg(&mcc->base,
+ SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK);
+ } else {
+ red_channel_client_pipe_add_empty_msg(&mcc->base,
+ SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK);
+ }
+}
+void main_channel_client_handle_pong(MainChannelClient *mcc, SpiceMsgPing *ping, uint32_t size)
+{
+ uint64_t roundtrip;
+ RedChannelClient* rcc = (RedChannelClient*)mcc;
+
+ roundtrip = g_get_monotonic_time() - 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;
+ mcc->latency = roundtrip;
+ break;
+ case NET_TEST_STAGE_LATENCY:
+ mcc->net_test_id++;
+ mcc->net_test_stage = NET_TEST_STAGE_RATE;
+ mcc->latency = MIN(mcc->latency, roundtrip);
+ break;
+ case NET_TEST_STAGE_RATE:
+ mcc->net_test_id = 0;
+ if (roundtrip <= mcc->latency) {
+ // probably high load on client or server result with incorrect values
+ spice_printerr("net test: invalid values, latency %" PRIu64
+ " roundtrip %" PRIu64 ". assuming high"
+ "bandwidth", mcc->latency, roundtrip);
+ mcc->latency = 0;
+ mcc->net_test_stage = NET_TEST_STAGE_INVALID;
+ red_channel_client_start_connectivity_monitoring(&mcc->base,
+ CLIENT_CONNECTIVITY_TIMEOUT);
+ break;
+ }
+ mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000
+ / (roundtrip - mcc->latency);
+ mcc->net_test_stage = NET_TEST_STAGE_COMPLETE;
+ spice_printerr("net test: latency %f ms, bitrate %"PRIu64" bps (%f Mbps)%s",
+ (double)mcc->latency / 1000,
+ mcc->bitrate_per_sec,
+ (double)mcc->bitrate_per_sec / 1024 / 1024,
+ main_channel_client_is_low_bandwidth(mcc) ? " LOW BANDWIDTH" : "");
+ red_channel_client_start_connectivity_monitoring(&mcc->base,
+ CLIENT_CONNECTIVITY_TIMEOUT);
+ break;
+ default:
+ spice_printerr("invalid net test stage, ping id %d test id %d stage %d",
+ ping->id,
+ mcc->net_test_id,
+ mcc->net_test_stage);
+ mcc->net_test_stage = NET_TEST_STAGE_INVALID;
+ }
+ return;
+ } else {
+ /*
+ * channel client monitors the connectivity using ping-pong messages
+ */
+ red_channel_client_handle_message(rcc, size, SPICE_MSGC_PONG, ping);
+ }
+#ifdef RED_STATISTICS
+ stat_update_value(rcc->channel->reds, roundtrip);
+#endif
+}
+
+gboolean main_channel_client_get_seamless_migration(MainChannelClient *mcc)
+{
+ return mcc->seamless_mig_dst;
+}
+
+void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
+{
+ if (!red_client_during_migrate_at_target(mcc->base.client)) {
+ spice_printerr("unexpected SPICE_MSGC_MIGRATE_END");
+ return;
+ }
+ if (!red_channel_client_test_remote_cap(&mcc->base,
+ SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
+ spice_printerr("unexpected SPICE_MSGC_MIGRATE_END, "
+ "client does not support semi-seamless migration");
+ return;
+ }
+ red_client_semi_seamless_migrate_complete(mcc->base.client);
+}
+
+void main_channel_client_migrate_cancel_wait(MainChannelClient *mcc)
+{
+ if (mcc->mig_wait_connect) {
+ spice_printerr("client %p cancel wait connect", mcc->base.client);
+ mcc->mig_wait_connect = FALSE;
+ mcc->mig_connect_ok = FALSE;
+ }
+ mcc->mig_wait_prev_complete = FALSE;
+}
+
+void main_channel_client_migrate_dst_complete(MainChannelClient *mcc)
+{
+ if (mcc->mig_wait_prev_complete) {
+ if (mcc->mig_wait_prev_try_seamless) {
+ spice_assert(mcc->base.channel->clients_num == 1);
+ red_channel_client_pipe_add_type(&mcc->base,
+ RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
+ } else {
+ red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
+ }
+ mcc->mig_wait_connect = TRUE;
+ mcc->mig_wait_prev_complete = FALSE;
+ }
+}
+
+gboolean main_channel_client_migrate_src_complete(MainChannelClient *mcc,
+ gboolean success)
+{
+ gboolean ret = FALSE;
+ int semi_seamless_support = red_channel_client_test_remote_cap(&mcc->base,
+ SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
+ if (semi_seamless_support && mcc->mig_connect_ok) {
+ if (success) {
+ spice_printerr("client %p MIGRATE_END", mcc->base.client);
+ red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_END);
+ ret = TRUE;
+ } else {
+ spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
+ red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
+ }
+ } else {
+ if (success) {
+ spice_printerr("client %p SWITCH_HOST", mcc->base.client);
+ red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
+ }
+ }
+ mcc->mig_connect_ok = FALSE;
+ mcc->mig_wait_connect = FALSE;
+
+ return ret;
+}
+
+#ifdef RED_STATISTICS
+static void do_ping_client(MainChannelClient *mcc,
+ const char *opt, int has_interval, int interval)
+{
+ spice_printerr("");
+ if (!opt) {
+ main_channel_client_push_ping(mcc, 0);
+ } else if (!strcmp(opt, "on")) {
+ if (has_interval && interval > 0) {
+ mcc->ping_interval = interval * MSEC_PER_SEC;
+ }
+ reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc->ping_interval);
+ } else if (!strcmp(opt, "off")) {
+ reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
+ } else {
+ return;
+ }
+}
+
+static void ping_timer_cb(void *opaque)
+{
+ MainChannelClient *mcc = opaque;
+
+ if (!red_channel_client_is_connected(&mcc->base)) {
+ spice_printerr("not connected to peer, ping off");
+ reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
+ return;
+ }
+ do_ping_client(mcc, NULL, 0, 0);
+ reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc->ping_interval);
+}
+#endif /* RED_STATISTICS */
+
+MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
+ RedsStream *stream, uint32_t connection_id,
+ int num_common_caps, uint32_t *common_caps,
+ int num_caps, uint32_t *caps)
+{
+ MainChannelClient *mcc = (MainChannelClient*)
+ red_channel_client_create(sizeof(MainChannelClient), &main_chan->base,
+ client, stream, FALSE, num_common_caps,
+ common_caps, num_caps, caps);
+ spice_assert(mcc != NULL);
+ mcc->connection_id = connection_id;
+ mcc->bitrate_per_sec = ~0;
+#ifdef RED_STATISTICS
+ if (!(mcc->ping_timer = reds_core_timer_add(red_channel_get_server(&main_chan->base), ping_timer_cb, mcc))) {
+ spice_error("ping timer create failed");
+ }
+ mcc->ping_interval = PING_INTERVAL;
+#endif
+ return mcc;
+}
+
+int main_channel_client_is_network_info_initialized(MainChannelClient *mcc)
+{
+ return mcc->net_test_stage == NET_TEST_STAGE_COMPLETE;
+}
+
+int main_channel_client_is_low_bandwidth(MainChannelClient *mcc)
+{
+ // TODO: configurable?
+ return mcc->bitrate_per_sec < 10 * 1024 * 1024;
+}
+
+uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc)
+{
+ return mcc->bitrate_per_sec;
+}
+
+uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc)
+{
+ return mcc->latency / 1000;
+}
+
+void main_channel_client_migrate(RedChannelClient *rcc)
+{
+ reds_on_main_channel_migrate(rcc->channel->reds, SPICE_CONTAINEROF(rcc, MainChannelClient, base));
+ red_channel_client_default_migrate(rcc);
+}
+
+gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc)
+{
+ RedChannelClient *rcc = main_channel_client_get_base(mcc);
+ MainChannel* main_channel = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+ if (red_channel_client_test_remote_cap(rcc,
+ SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
+ RedClient *client = red_channel_client_get_client(rcc);
+ if (red_client_during_migrate_at_target(client)) {
+ spice_printerr("client %p: wait till previous migration completes", client);
+ mcc->mig_wait_prev_complete = TRUE;
+ mcc->mig_wait_prev_try_seamless = FALSE;
+ } else {
+ red_channel_client_pipe_add_type(rcc,
+ RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
+ mcc->mig_wait_connect = TRUE;
+ }
+ mcc->mig_connect_ok = FALSE;
+ main_channel->num_clients_mig_wait++;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void main_channel_client_connect_seamless(MainChannelClient *mcc)
+{
+ spice_assert(red_channel_client_test_remote_cap(&mcc->base,
+ SPICE_MAIN_CAP_SEAMLESS_MIGRATE));
+ if (red_client_during_migrate_at_target(mcc->base.client)) {
+ spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
+ mcc->mig_wait_prev_complete = TRUE;
+ mcc->mig_wait_prev_try_seamless = TRUE;
+ } else {
+ red_channel_client_pipe_add_type(&mcc->base,
+ RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
+ mcc->mig_wait_connect = TRUE;
+ }
+ mcc->mig_connect_ok = FALSE;
+}
+
+RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
+{
+ spice_assert(mcc);
+ return &mcc->base;
+}
+
+uint32_t main_channel_client_get_connection_id(MainChannelClient *mcc)
+{
+ return mcc->connection_id;
+}
+
+uint32_t main_channel_client_next_ping_id(MainChannelClient *mcc)
+{
+ return ++mcc->ping_id;
+}
+
+void main_channel_client_on_send_init(MainChannelClient *mcc)
+{
+ mcc->init_sent = TRUE;
+}
+
+gboolean main_channel_client_get_init_sent(MainChannelClient *mcc)
+{
+ return mcc->init_sent;
+}
diff --git a/server/main-channel-client.h b/server/main-channel-client.h
new file mode 100644
index 0000000..7e4daf9
--- /dev/null
+++ b/server/main-channel-client.h
@@ -0,0 +1,153 @@
+/*
+ Copyright (C) 2009-2015 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 <http://www.gnu.org/licenses/>.
+*/
+#ifndef __MAIN_CHANNEL_CLIENT_H__
+#define __MAIN_CHANNEL_CLIENT_H__
+
+#include "red-channel.h"
+
+typedef struct MainChannel MainChannel;
+typedef struct MainChannelClient MainChannelClient;
+
+MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
+ RedsStream *stream, uint32_t connection_id,
+ int num_common_caps, uint32_t *common_caps,
+ int num_caps, uint32_t *caps);
+
+void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens);
+void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
+ spice_marshaller_item_free_func free_data, void *opaque);
+void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate);
+// TODO: huge. Consider making a reds_* interface for these functions
+// and calling from main.
+void main_channel_client_push_init(MainChannelClient *mcc,
+ int display_channels_hint,
+ int current_mouse_mode,
+ int is_client_mouse_allowed,
+ int multi_media_time,
+ int ram_hint);
+void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg);
+void main_channel_client_migrate(RedChannelClient *rcc);
+gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc);
+void main_channel_client_connect_seamless(MainChannelClient *mcc);
+void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
+ int success,
+ int seamless);
+void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc,
+ uint32_t src_version);
+void main_channel_client_handle_migrate_end(MainChannelClient *mcc);
+void main_channel_client_migrate_cancel_wait(MainChannelClient *mcc);
+void main_channel_client_migrate_dst_complete(MainChannelClient *mcc);
+gboolean main_channel_client_migrate_src_complete(MainChannelClient *mcc,
+ gboolean success);
+
+void main_channel_client_handle_pong(MainChannelClient *mcc, SpiceMsgPing *ping, uint32_t size);
+
+/*
+ * return TRUE if network test had been completed successfully.
+ * If FALSE, bitrate_per_sec is set to MAX_UINT64 and the roundtrip is set to 0
+ */
+int main_channel_client_is_network_info_initialized(MainChannelClient *mcc);
+int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
+uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
+uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc);
+
+RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
+
+void main_channel_client_push_name(MainChannelClient *mcc, const char *name);
+void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16]);
+
+uint32_t main_channel_client_get_connection_id(MainChannelClient *mcc);
+uint32_t main_channel_client_next_ping_id(MainChannelClient *mcc);
+
+gboolean main_channel_client_get_seamless_migration(MainChannelClient *mcc);
+void main_channel_client_on_send_init(MainChannelClient *mcc);
+gboolean main_channel_client_get_init_sent(MainChannelClient *mcc);
+
+enum {
+ RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
+ RED_PIPE_ITEM_TYPE_MAIN_PING,
+ RED_PIPE_ITEM_TYPE_MAIN_MOUSE_MODE,
+ RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED,
+ RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN,
+ RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA,
+ RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA,
+ RED_PIPE_ITEM_TYPE_MAIN_INIT,
+ RED_PIPE_ITEM_TYPE_MAIN_NOTIFY,
+ RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN,
+ RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS,
+ RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST,
+ RED_PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME,
+ RED_PIPE_ITEM_TYPE_MAIN_NAME,
+ RED_PIPE_ITEM_TYPE_MAIN_UUID,
+ RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS,
+};
+
+typedef struct RedPingPipeItem {
+ RedPipeItem base;
+ int size;
+} RedPingPipeItem;
+
+typedef struct RedMouseModePipeItem {
+ RedPipeItem base;
+ int current_mode;
+ int is_client_mouse_allowed;
+} RedMouseModePipeItem;
+
+typedef struct RedTokensPipeItem {
+ RedPipeItem base;
+ int tokens;
+} RedTokensPipeItem;
+
+typedef struct RedAgentDataPipeItem {
+ RedPipeItem base;
+ uint8_t* data;
+ size_t len;
+ spice_marshaller_item_free_func free_data;
+ void *opaque;
+} RedAgentDataPipeItem;
+
+typedef struct RedInitPipeItem {
+ RedPipeItem base;
+ int connection_id;
+ int display_channels_hint;
+ int current_mouse_mode;
+ int is_client_mouse_allowed;
+ int multi_media_time;
+ int ram_hint;
+} RedInitPipeItem;
+
+typedef struct RedNamePipeItem {
+ RedPipeItem base;
+ SpiceMsgMainName msg;
+} RedNamePipeItem;
+
+typedef struct RedUuidPipeItem {
+ RedPipeItem base;
+ SpiceMsgMainUuid msg;
+} RedUuidPipeItem;
+
+typedef struct RedNotifyPipeItem {
+ RedPipeItem base;
+ char *msg;
+} RedNotifyPipeItem;
+
+typedef struct RedMultiMediaTimePipeItem {
+ RedPipeItem base;
+ int time;
+} RedMultiMediaTimePipeItem;
+
+#endif /* __MAIN_CHANNEL_CLIENT_H__ */
diff --git a/server/main-channel.c b/server/main-channel.c
index 98ce660..7ec8b55 100644
--- a/server/main-channel.c
+++ b/server/main-channel.c
@@ -41,6 +41,7 @@
#include "demarshallers.h"
#include "main-channel.h"
+#include "main-channel-client.h"
#include "red-channel.h"
#include "red-common.h"
#include "reds.h"
@@ -50,121 +51,8 @@
#define ZERO_BUF_SIZE 4096
-#define NET_TEST_WARMUP_BYTES 0
-#define NET_TEST_BYTES (1024 * 250)
-
-#define PING_INTERVAL (MSEC_PER_SEC * 10)
-
-#define CLIENT_CONNECTIVITY_TIMEOUT (MSEC_PER_SEC * 30)
-
static const uint8_t zero_page[ZERO_BUF_SIZE] = {0};
-enum {
- RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
- RED_PIPE_ITEM_TYPE_MAIN_PING,
- RED_PIPE_ITEM_TYPE_MAIN_MOUSE_MODE,
- RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED,
- RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN,
- RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA,
- RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA,
- RED_PIPE_ITEM_TYPE_MAIN_INIT,
- RED_PIPE_ITEM_TYPE_MAIN_NOTIFY,
- RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN,
- RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS,
- RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST,
- RED_PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME,
- RED_PIPE_ITEM_TYPE_MAIN_NAME,
- RED_PIPE_ITEM_TYPE_MAIN_UUID,
- RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS,
-};
-
-typedef struct RedRefsPipeItem {
- RedPipeItem base;
- int *refs;
-} RedRefsPipeItem;
-
-typedef struct RedPingPipeItem {
- RedPipeItem base;
- int size;
-} RedPingPipeItem;
-
-typedef struct RedMouseModePipeItem {
- RedPipeItem base;
- int current_mode;
- int is_client_mouse_allowed;
-} RedMouseModePipeItem;
-
-typedef struct RedTokensPipeItem {
- RedPipeItem base;
- int tokens;
-} RedTokensPipeItem;
-
-typedef struct RedAgentDataPipeItem {
- RedPipeItem base;
- uint8_t* data;
- size_t len;
- spice_marshaller_item_free_func free_data;
- void *opaque;
-} RedAgentDataPipeItem;
-
-typedef struct RedInitPipeItem {
- RedPipeItem base;
- int connection_id;
- int display_channels_hint;
- int current_mouse_mode;
- int is_client_mouse_allowed;
- int multi_media_time;
- int ram_hint;
-} RedInitPipeItem;
-
-typedef struct RedNamePipeItem {
- RedPipeItem base;
- SpiceMsgMainName msg;
-} RedNamePipeItem;
-
-typedef struct RedUuidPipeItem {
- RedPipeItem base;
- SpiceMsgMainUuid msg;
-} RedUuidPipeItem;
-
-typedef struct RedNotifyPipeItem {
- RedPipeItem base;
- char *msg;
-} RedNotifyPipeItem;
-
-typedef struct RedMultiMediaTimePipeItem {
- RedPipeItem base;
- int time;
-} RedMultiMediaTimePipeItem;
-
-struct MainChannelClient {
- RedChannelClient base;
- uint32_t connection_id;
- uint32_t ping_id;
- uint32_t net_test_id;
- int net_test_stage;
- uint64_t latency;
- uint64_t bitrate_per_sec;
-#ifdef RED_STATISTICS
- SpiceTimer *ping_timer;
- int ping_interval;
-#endif
- int mig_wait_connect;
- int mig_connect_ok;
- int mig_wait_prev_complete;
- int mig_wait_prev_try_seamless;
- int init_sent;
- int seamless_mig_dst;
-};
-
-enum NetTestStage {
- NET_TEST_STAGE_INVALID,
- NET_TEST_STAGE_WARMUP,
- NET_TEST_STAGE_LATENCY,
- NET_TEST_STAGE_RATE,
- NET_TEST_STAGE_COMPLETE,
-};
-
static void main_channel_release_pipe_item(RedChannelClient *rcc,
RedPipeItem *base, int item_pushed);
@@ -187,35 +75,18 @@ RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t c
{
RingItem *link;
MainChannelClient *mcc;
+ RedChannelClient *rcc;
RING_FOREACH(link, &main_chan->base.clients) {
- mcc = SPICE_CONTAINEROF(link, MainChannelClient, base.channel_link);
- if (mcc->connection_id == connection_id) {
- return mcc->base.client;
+ rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+ mcc = (MainChannelClient*) rcc;
+ if (main_channel_client_get_connection_id(mcc) == connection_id) {
+ return rcc->client;
}
}
return NULL;
}
-static int main_channel_client_push_ping(MainChannelClient *mcc, int size);
-
-void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate)
-{
- if (!mcc || mcc->net_test_id) {
- return;
- }
- if (test_rate) {
- if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES)
- && main_channel_client_push_ping(mcc, 0)
- && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) {
- mcc->net_test_id = mcc->ping_id - 2;
- mcc->net_test_stage = NET_TEST_STAGE_WARMUP;
- }
- } else {
- red_channel_client_start_connectivity_monitoring(&mcc->base, CLIENT_CONNECTIVITY_TIMEOUT);
- }
-}
-
typedef struct MainMouseModeItemInfo {
int current_mode;
int is_client_mouse_allowed;
@@ -232,88 +103,8 @@ static RedPipeItem *main_mouse_mode_item_new(RedChannelClient *rcc, void *data,
return &item->base;
}
-static RedPipeItem *red_ping_item_new(MainChannelClient *mcc, int size)
-{
- RedPingPipeItem *item = spice_malloc(sizeof(RedPingPipeItem));
-
- red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_PING);
- item->size = size;
- return &item->base;
-}
-
-static RedPipeItem *main_agent_tokens_item_new(RedChannelClient *rcc, uint32_t num_tokens)
-{
- RedTokensPipeItem *item = spice_malloc(sizeof(RedTokensPipeItem));
-
- red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN);
- item->tokens = num_tokens;
- return &item->base;
-}
-
-static RedPipeItem *main_agent_data_item_new(RedChannelClient *rcc, uint8_t* data, size_t len,
- spice_marshaller_item_free_func free_data,
- void *opaque)
-{
- RedAgentDataPipeItem *item = spice_malloc(sizeof(RedAgentDataPipeItem));
-
- red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA);
- item->data = data;
- item->len = len;
- item->free_data = free_data;
- item->opaque = opaque;
- return &item->base;
-}
-
-static RedPipeItem *main_init_item_new(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)
-{
- RedInitPipeItem *item = spice_malloc(sizeof(RedInitPipeItem));
-
- red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_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->base;
-}
-
-static RedPipeItem *main_name_item_new(MainChannelClient *mcc, const char *name)
-{
- RedNamePipeItem *item = spice_malloc(sizeof(RedNamePipeItem) + strlen(name) + 1);
-
- red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NAME);
- item->msg.name_len = strlen(name) + 1;
- memcpy(&item->msg.name, name, item->msg.name_len);
-
- return &item->base;
-}
-
-static RedPipeItem *main_uuid_item_new(MainChannelClient *mcc, const uint8_t uuid[16])
-{
- RedUuidPipeItem *item = spice_malloc(sizeof(RedUuidPipeItem));
-
- red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_UUID);
- memcpy(item->msg.uuid, uuid, sizeof(item->msg.uuid));
-
- return &item->base;
-}
-
-static RedPipeItem *main_notify_item_new(RedChannelClient *rcc, void *data, int num)
-{
- RedNotifyPipeItem *item = spice_malloc(sizeof(RedNotifyPipeItem));
- const char *msg = data;
-
- red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NOTIFY);
- item->msg = spice_strdup(msg);
- return &item->base;
-}
-
-static RedPipeItem *main_multi_media_time_item_new(
- RedChannelClient *rcc, void *data, int num)
+static RedPipeItem *main_multi_media_time_item_new(RedChannelClient *rcc,
+ void *data, int num)
{
RedMultiMediaTimePipeItem *item, *info = data;
@@ -325,12 +116,12 @@ static RedPipeItem *main_multi_media_time_item_new(
static void main_channel_push_channels(MainChannelClient *mcc)
{
- if (red_client_during_migrate_at_target(mcc->base.client)) {
+ if (red_client_during_migrate_at_target((main_channel_client_get_base(mcc))->client)) {
spice_printerr("warning: ignoring unexpected SPICE_MSGC_MAIN_ATTACH_CHANNELS"
"during migration");
return;
}
- red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST);
+ red_channel_client_pipe_add_type(main_channel_client_get_base(mcc), RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST);
}
static void main_channel_marshall_channels(RedChannelClient *rcc,
@@ -345,28 +136,16 @@ static void main_channel_marshall_channels(RedChannelClient *rcc,
free(channels_info);
}
-int main_channel_client_push_ping(MainChannelClient *mcc, int size)
-{
- RedPipeItem *item;
-
- if (mcc == NULL) {
- return FALSE;
- }
- item = red_ping_item_new(mcc, size);
- red_channel_client_pipe_add_push(&mcc->base, item);
- return TRUE;
-}
-
static void main_channel_marshall_ping(RedChannelClient *rcc,
SpiceMarshaller *m,
RedPingPipeItem *item)
{
- MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
+ MainChannelClient *mcc = (MainChannelClient*)rcc;
SpiceMsgPing ping;
int size_left = item->size;
red_channel_client_init_send_data(rcc, SPICE_MSG_PING, &item->base);
- ping.id = ++(mcc->ping_id);
+ ping.id = main_channel_client_next_ping_id(mcc);
ping.timestamp = g_get_monotonic_time();
spice_marshall_msg_ping(m, &ping);
@@ -440,13 +219,6 @@ static void main_channel_marshall_agent_disconnected(RedChannelClient *rcc,
spice_marshall_msg_main_agent_disconnected(m, &disconnect);
}
-void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens)
-{
- RedPipeItem *item = main_agent_tokens_item_new(&mcc->base, num_tokens);
-
- red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
static void main_channel_marshall_tokens(RedChannelClient *rcc,
SpiceMarshaller *m, RedTokensPipeItem *item)
{
@@ -457,15 +229,6 @@ static void main_channel_marshall_tokens(RedChannelClient *rcc,
spice_marshall_msg_main_agent_token(m, &tokens);
}
-void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
- spice_marshaller_item_free_func free_data, void *opaque)
-{
- RedPipeItem *item;
-
- item = main_agent_data_item_new(&mcc->base, data, len, free_data, opaque);
- red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
static void main_channel_marshall_agent_data(RedChannelClient *rcc,
SpiceMarshaller *m,
RedAgentDataPipeItem *item)
@@ -490,7 +253,7 @@ static void main_channel_marshall_migrate_data_item(RedChannelClient *rcc,
static int main_channel_handle_migrate_data(RedChannelClient *rcc,
uint32_t size, void *message)
{
- MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
+ MainChannelClient *mcc = (MainChannelClient*)rcc;
SpiceMigrateDataHeader *header = (SpiceMigrateDataHeader *)message;
/* not supported with multi-clients */
@@ -509,21 +272,6 @@ static int main_channel_handle_migrate_data(RedChannelClient *rcc,
return reds_handle_migrate_data(rcc->channel->reds, mcc, (SpiceMigrateDataMain *)(header + 1), size);
}
-void main_channel_client_push_init(MainChannelClient *mcc,
- int display_channels_hint,
- int current_mouse_mode,
- int is_client_mouse_allowed,
- int multi_media_time,
- int ram_hint)
-{
- RedPipeItem *item;
-
- item = main_init_item_new(mcc,
- mcc->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);
-}
-
static void main_channel_marshall_init(RedChannelClient *rcc,
SpiceMarshaller *m,
RedInitPipeItem *item)
@@ -546,36 +294,6 @@ static void main_channel_marshall_init(RedChannelClient *rcc,
spice_marshall_msg_main_init(m, &init);
}
-void main_channel_client_push_name(MainChannelClient *mcc, const char *name)
-{
- RedPipeItem *item;
-
- if (!red_channel_client_test_remote_cap(&mcc->base,
- SPICE_MAIN_CAP_NAME_AND_UUID))
- return;
-
- item = main_name_item_new(mcc, name);
- red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
-void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16])
-{
- RedPipeItem *item;
-
- if (!red_channel_client_test_remote_cap(&mcc->base,
- SPICE_MAIN_CAP_NAME_AND_UUID))
- return;
-
- item = main_uuid_item_new(mcc, uuid);
- red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
-void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg)
-{
- RedPipeItem *item = main_notify_item_new(&mcc->base, (void *)msg, 1);
- red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
static void main_channel_marshall_notify(RedChannelClient *rcc,
SpiceMarshaller *m, RedNotifyPipeItem *item)
{
@@ -701,14 +419,16 @@ static void main_channel_marshall_multi_media_time(RedChannelClient *rcc,
static void main_channel_send_item(RedChannelClient *rcc, RedPipeItem *base)
{
- MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
+ MainChannelClient *mcc = (MainChannelClient*)rcc;
SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
/* In semi-seamless migration (dest side), the connection is started from scratch, and
* we ignore any pipe item that arrives before the INIT msg is sent.
* For seamless we don't send INIT, and the connection continues from the same place
* it stopped on the src side. */
- if (!mcc->init_sent && !mcc->seamless_mig_dst && base->type != RED_PIPE_ITEM_TYPE_MAIN_INIT) {
+ if (!main_channel_client_get_init_sent(mcc) &&
+ !main_channel_client_get_seamless_migration(mcc) &&
+ base->type != RED_PIPE_ITEM_TYPE_MAIN_INIT) {
spice_printerr("Init msg for client %p was not sent yet "
"(client is probably during semi-seamless migration). Ignoring msg type %d",
rcc->client, base->type);
@@ -745,7 +465,7 @@ static void main_channel_send_item(RedChannelClient *rcc, RedPipeItem *base)
main_channel_marshall_migrate_data_item(rcc, m, base);
break;
case RED_PIPE_ITEM_TYPE_MAIN_INIT:
- mcc->init_sent = TRUE;
+ main_channel_client_on_send_init(mcc);
main_channel_marshall_init(rcc, m,
SPICE_CONTAINEROF(base, RedInitPipeItem, base));
break;
@@ -804,77 +524,11 @@ static void main_channel_release_pipe_item(RedChannelClient *rcc,
free(base);
}
-static void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
- int success,
- int seamless)
-{
- spice_printerr("client %p connected: %d seamless %d", mcc->base.client, success, seamless);
- if (mcc->mig_wait_connect) {
- MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel, MainChannel, base);
-
- mcc->mig_wait_connect = FALSE;
- mcc->mig_connect_ok = success;
- spice_assert(main_channel->num_clients_mig_wait);
- spice_assert(!seamless || main_channel->num_clients_mig_wait == 1);
- if (!--main_channel->num_clients_mig_wait) {
- reds_on_main_migrate_connected(mcc->base.channel->reds, seamless && success);
- }
- } else {
- if (success) {
- spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
- red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
- }
- }
-}
-
-void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc,
- uint32_t src_version)
-{
- if (reds_on_migrate_dst_set_seamless(mcc->base.channel->reds, mcc, src_version)) {
- mcc->seamless_mig_dst = TRUE;
- red_channel_client_pipe_add_empty_msg(&mcc->base,
- SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK);
- } else {
- red_channel_client_pipe_add_empty_msg(&mcc->base,
- SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK);
- }
-}
-
-void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
-{
- if (!red_client_during_migrate_at_target(mcc->base.client)) {
- spice_printerr("unexpected SPICE_MSGC_MIGRATE_END");
- return;
- }
- if (!red_channel_client_test_remote_cap(&mcc->base,
- SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
- spice_printerr("unexpected SPICE_MSGC_MIGRATE_END, "
- "client does not support semi-seamless migration");
- return;
- }
- red_client_semi_seamless_migrate_complete(mcc->base.client);
-}
-
-void main_channel_client_migrate_dst_complete(MainChannelClient *mcc)
-{
- if (mcc->mig_wait_prev_complete) {
- if (mcc->mig_wait_prev_try_seamless) {
- spice_assert(mcc->base.channel->clients_num == 1);
- red_channel_client_pipe_add_type(&mcc->base,
- RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
- } else {
- red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
- }
- mcc->mig_wait_connect = TRUE;
- mcc->mig_wait_prev_complete = FALSE;
- }
-}
-
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);
+ MainChannelClient *mcc = (MainChannelClient*)rcc;
switch (type) {
case SPICE_MSGC_MAIN_AGENT_START: {
@@ -923,64 +577,7 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
reds_on_main_mouse_mode_request(rcc->channel->reds, message, size);
break;
case SPICE_MSGC_PONG: {
- SpiceMsgPing *ping = (SpiceMsgPing *)message;
- uint64_t roundtrip;
-
- roundtrip = g_get_monotonic_time() - 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;
- mcc->latency = roundtrip;
- break;
- case NET_TEST_STAGE_LATENCY:
- mcc->net_test_id++;
- mcc->net_test_stage = NET_TEST_STAGE_RATE;
- mcc->latency = MIN(mcc->latency, roundtrip);
- break;
- case NET_TEST_STAGE_RATE:
- mcc->net_test_id = 0;
- if (roundtrip <= mcc->latency) {
- // probably high load on client or server result with incorrect values
- spice_printerr("net test: invalid values, latency %" PRIu64
- " roundtrip %" PRIu64 ". assuming high"
- " bandwidth", mcc->latency, roundtrip);
- mcc->latency = 0;
- mcc->net_test_stage = NET_TEST_STAGE_INVALID;
- red_channel_client_start_connectivity_monitoring(&mcc->base,
- CLIENT_CONNECTIVITY_TIMEOUT);
- break;
- }
- mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000
- / (roundtrip - mcc->latency);
- mcc->net_test_stage = NET_TEST_STAGE_COMPLETE;
- spice_printerr("net test: latency %f ms, bitrate %"PRIu64" bps (%f Mbps)%s",
- (double)mcc->latency / 1000,
- mcc->bitrate_per_sec,
- (double)mcc->bitrate_per_sec / 1024 / 1024,
- main_channel_client_is_low_bandwidth(mcc) ? " LOW BANDWIDTH" : "");
- red_channel_client_start_connectivity_monitoring(&mcc->base,
- CLIENT_CONNECTIVITY_TIMEOUT);
- break;
- default:
- spice_printerr("invalid net test stage, ping id %d test id %d stage %d",
- ping->id,
- mcc->net_test_id,
- mcc->net_test_stage);
- mcc->net_test_stage = NET_TEST_STAGE_INVALID;
- }
- break;
- } else {
- /*
- * channel client monitors the connectivity using ping-pong messages
- */
- red_channel_client_handle_message(rcc, size, type, message);
- }
-#ifdef RED_STATISTICS
- stat_update_value(rcc->channel->reds, roundtrip);
-#endif
+ main_channel_client_handle_pong(mcc, (SpiceMsgPing *)message, size);
break;
}
case SPICE_MSGC_DISCONNECTING:
@@ -999,7 +596,7 @@ static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
uint32_t size)
{
MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
- MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
+ MainChannelClient *mcc = (MainChannelClient*)rcc;
if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
return reds_get_agent_data_buffer(rcc->channel->reds, mcc, size);
@@ -1035,60 +632,6 @@ static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
return TRUE;
}
-#ifdef RED_STATISTICS
-static void do_ping_client(MainChannelClient *mcc,
- const char *opt, int has_interval, int interval)
-{
- spice_printerr("");
- if (!opt) {
- main_channel_client_push_ping(mcc, 0);
- } else if (!strcmp(opt, "on")) {
- if (has_interval && interval > 0) {
- mcc->ping_interval = interval * MSEC_PER_SEC;
- }
- reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc->ping_interval);
- } else if (!strcmp(opt, "off")) {
- reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
- } else {
- return;
- }
-}
-
-static void ping_timer_cb(void *opaque)
-{
- MainChannelClient *mcc = opaque;
-
- if (!red_channel_client_is_connected(&mcc->base)) {
- spice_printerr("not connected to peer, ping off");
- reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
- return;
- }
- do_ping_client(mcc, NULL, 0, 0);
- reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc->ping_interval);
-}
-#endif /* RED_STATISTICS */
-
-static MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
- RedsStream *stream, uint32_t connection_id,
- int num_common_caps, uint32_t *common_caps,
- int num_caps, uint32_t *caps)
-{
- MainChannelClient *mcc = (MainChannelClient*)
- red_channel_client_create(sizeof(MainChannelClient), &main_chan->base,
- client, stream, FALSE, num_common_caps,
- common_caps, num_caps, caps);
- spice_assert(mcc != NULL);
- mcc->connection_id = connection_id;
- mcc->bitrate_per_sec = ~0;
-#ifdef RED_STATISTICS
- if (!(mcc->ping_timer = reds_core_timer_add(red_channel_get_server(&main_chan->base), ping_timer_cb, mcc))) {
- spice_error("ping timer create failed");
- }
- mcc->ping_interval = PING_INTERVAL;
-#endif
- return mcc;
-}
-
MainChannelClient *main_channel_link(MainChannel *channel, RedClient *client,
RedsStream *stream, uint32_t connection_id, int migration,
int num_common_caps, uint32_t *common_caps, int num_caps,
@@ -1128,33 +671,6 @@ void main_channel_close(MainChannel *main_chan)
}
}
-int main_channel_client_is_network_info_initialized(MainChannelClient *mcc)
-{
- return mcc->net_test_stage == NET_TEST_STAGE_COMPLETE;
-}
-
-int main_channel_client_is_low_bandwidth(MainChannelClient *mcc)
-{
- // TODO: configurable?
- return mcc->bitrate_per_sec < 10 * 1024 * 1024;
-}
-
-uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc)
-{
- return mcc->bitrate_per_sec;
-}
-
-uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc)
-{
- return mcc->latency / 1000;
-}
-
-static void main_channel_client_migrate(RedChannelClient *rcc)
-{
- reds_on_main_channel_migrate(rcc->channel->reds, SPICE_CONTAINEROF(rcc, MainChannelClient, base));
- red_channel_client_default_migrate(rcc);
-}
-
MainChannel* main_channel_new(RedsState *reds)
{
RedChannel *channel;
@@ -1190,33 +706,16 @@ MainChannel* main_channel_new(RedsState *reds)
return (MainChannel *)channel;
}
-RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
-{
- spice_assert(mcc);
- return &mcc->base;
-}
-
static int main_channel_connect_semi_seamless(MainChannel *main_channel)
{
RingItem *client_link;
RING_FOREACH(client_link, &main_channel->base.clients) {
- MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
- base.channel_link);
- if (red_channel_client_test_remote_cap(&mcc->base,
- SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
- if (red_client_during_migrate_at_target(mcc->base.client)) {
- spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
- mcc->mig_wait_prev_complete = TRUE;
- mcc->mig_wait_prev_try_seamless = FALSE;
- } else {
- red_channel_client_pipe_add_type(&mcc->base,
- RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
- mcc->mig_wait_connect = TRUE;
- }
- mcc->mig_connect_ok = FALSE;
+ RedChannelClient *rcc = SPICE_CONTAINEROF(client_link, RedChannelClient,
+ channel_link);
+ MainChannelClient *mcc = (MainChannelClient*)rcc;
+ if (main_channel_client_connect_semi_seamless(mcc))
main_channel->num_clients_mig_wait++;
- }
}
return main_channel->num_clients_mig_wait;
}
@@ -1228,20 +727,10 @@ static int main_channel_connect_seamless(MainChannel *main_channel)
spice_assert(main_channel->base.clients_num == 1);
RING_FOREACH(client_link, &main_channel->base.clients) {
- MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
- base.channel_link);
- spice_assert(red_channel_client_test_remote_cap(&mcc->base,
- SPICE_MAIN_CAP_SEAMLESS_MIGRATE));
- if (red_client_during_migrate_at_target(mcc->base.client)) {
- spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
- mcc->mig_wait_prev_complete = TRUE;
- mcc->mig_wait_prev_try_seamless = TRUE;
- } else {
- red_channel_client_pipe_add_type(&mcc->base,
- RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
- mcc->mig_wait_connect = TRUE;
- }
- mcc->mig_connect_ok = FALSE;
+ RedChannelClient *rcc = SPICE_CONTAINEROF(client_link, RedChannelClient,
+ channel_link);
+ MainChannelClient *mcc = (MainChannelClient*)rcc;
+ main_channel_client_connect_seamless(mcc);
main_channel->num_clients_mig_wait++;
}
return main_channel->num_clients_mig_wait;
@@ -1261,12 +750,12 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta
return main_channel_connect_semi_seamless(main_channel);
} else {
RingItem *client_item;
- MainChannelClient *mcc;
+ RedChannelClient *rcc;
client_item = ring_get_head(&main_channel->base.clients);
- mcc = SPICE_CONTAINEROF(client_item, MainChannelClient, base.channel_link);
+ rcc = SPICE_CONTAINEROF(client_item, RedChannelClient, channel_link);
- if (!red_channel_client_test_remote_cap(&mcc->base,
+ if (!red_channel_client_test_remote_cap(rcc,
SPICE_MAIN_CAP_SEAMLESS_MIGRATE)) {
return main_channel_connect_semi_seamless(main_channel);
} else {
@@ -1281,15 +770,10 @@ void main_channel_migrate_cancel_wait(MainChannel *main_chan)
RingItem *client_link;
RING_FOREACH(client_link, &main_chan->base.clients) {
- MainChannelClient *mcc;
-
- mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
- if (mcc->mig_wait_connect) {
- spice_printerr("client %p cancel wait connect", mcc->base.client);
- mcc->mig_wait_connect = FALSE;
- mcc->mig_connect_ok = FALSE;
- }
- mcc->mig_wait_prev_complete = FALSE;
+ RedChannelClient *rcc = SPICE_CONTAINEROF(client_link, RedChannelClient,
+ channel_link);
+ MainChannelClient *mcc = (MainChannelClient*)rcc;
+ main_channel_client_migrate_cancel_wait(mcc);
}
main_chan->num_clients_mig_wait = 0;
}
@@ -1307,29 +791,11 @@ int main_channel_migrate_src_complete(MainChannel *main_chan, int success)
}
RING_FOREACH(client_link, &main_chan->base.clients) {
- MainChannelClient *mcc;
- int semi_seamless_support;
-
- mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
- semi_seamless_support = red_channel_client_test_remote_cap(&mcc->base,
- SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
- if (semi_seamless_support && mcc->mig_connect_ok) {
- if (success) {
- spice_printerr("client %p MIGRATE_END", mcc->base.client);
- red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_END);
- semi_seamless_count++;
- } else {
- spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
- red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
- }
- } else {
- if (success) {
- spice_printerr("client %p SWITCH_HOST", mcc->base.client);
- red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
- }
- }
- mcc->mig_connect_ok = FALSE;
- mcc->mig_wait_connect = FALSE;
+ RedChannelClient *rcc = SPICE_CONTAINEROF(client_link, RedChannelClient,
+ channel_link);
+ MainChannelClient *mcc = (MainChannelClient*)rcc;
+ if (main_channel_client_migrate_src_complete(mcc, success))
+ semi_seamless_count++;
}
return semi_seamless_count;
}
diff --git a/server/main-channel.h b/server/main-channel.h
index 4eb9a9c..868a14a 100644
--- a/server/main-channel.h
+++ b/server/main-channel.h
@@ -23,6 +23,7 @@
#include <common/marshaller.h>
#include "red-channel.h"
+#include "main-channel-client.h"
// TODO: Defines used to calculate receive buffer size, and also by reds.c
// other options: is to make a reds_main_consts.h, to duplicate defines.
@@ -59,34 +60,11 @@ void main_channel_close(MainChannel *main_chan); // not destroy, just socket clo
void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode, int is_client_mouse_allowed);
void main_channel_push_agent_connected(MainChannel *main_chan);
void main_channel_push_agent_disconnected(MainChannel *main_chan);
-void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens);
-void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
- spice_marshaller_item_free_func free_data, void *opaque);
-void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate);
-// TODO: huge. Consider making a reds_* interface for these functions
-// and calling from main.
-void main_channel_client_push_init(MainChannelClient *mcc,
- int display_channels_hint,
- int current_mouse_mode,
- int is_client_mouse_allowed,
- int multi_media_time,
- int ram_hint);
-void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg);
void main_channel_push_multi_media_time(MainChannel *main_chan, int time);
int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
-/*
- * return TRUE if network test had been completed successfully.
- * If FALSE, bitrate_per_sec is set to MAX_UINT64 and the roundtrip is set to 0
- */
-int main_channel_client_is_network_info_initialized(MainChannelClient *mcc);
-int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
-uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
-uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc);
-
int main_channel_is_connected(MainChannel *main_chan);
-RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
/* switch host migration */
void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target);
@@ -100,8 +78,5 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta
void main_channel_migrate_cancel_wait(MainChannel *main_chan);
/* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/
int main_channel_migrate_src_complete(MainChannel *main_chan, int success);
-void main_channel_client_migrate_dst_complete(MainChannelClient *mcc);
-void main_channel_client_push_name(MainChannelClient *mcc, const char *name);
-void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16]);
#endif
--
2.4.11
More information about the Spice-devel
mailing list