mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-26 22:48:19 +00:00
When using config.h, it must be the very first include in all source files since it contains #define that may change the compilation process (eg libc structure layout changes when it's used to enable large file support on 32 bit x86 archs). This commit adds it at the beginning of all .c and .cpp files
874 lines
27 KiB
C
874 lines
27 KiB
C
/* -*- 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <limits.h>
|
|
#include <time.h>
|
|
#include <pthread.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
|
|
#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;
|
|
|
|
typedef struct MainChannel {
|
|
RedChannel base;
|
|
uint8_t recv_buf[RECEIVE_BUF_SIZE];
|
|
uint32_t ping_id;
|
|
uint32_t net_test_id;
|
|
int net_test_stage;
|
|
} MainChannel;
|
|
|
|
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_disconnect(MainChannel *main_chan)
|
|
{
|
|
main_chan->ping_id = 0;
|
|
main_chan->net_test_id = 0;
|
|
main_chan->net_test_stage = NET_TEST_STAGE_INVALID;
|
|
red_channel_destroy(&main_chan->base);
|
|
|
|
latency = 0;
|
|
bitrate_per_sec = ~0;
|
|
}
|
|
|
|
void main_channel_start_net_test(Channel *channel)
|
|
{
|
|
MainChannel *main_chan = channel->data;
|
|
|
|
if (!main_chan || main_chan->net_test_id) {
|
|
return;
|
|
}
|
|
|
|
if (main_channel_push_ping(channel, NET_TEST_WARMUP_BYTES)
|
|
&& main_channel_push_ping(channel, 0)
|
|
&& main_channel_push_ping(channel, NET_TEST_BYTES)) {
|
|
main_chan->net_test_id = main_chan->ping_id - 2;
|
|
main_chan->net_test_stage = NET_TEST_STAGE_WARMUP;
|
|
}
|
|
}
|
|
|
|
static RedsOutItem *main_pipe_item_new(MainChannel *main_chan, int type)
|
|
{
|
|
RedsOutItem *item = spice_malloc(sizeof(RedsOutItem));
|
|
|
|
red_channel_pipe_item_init(&main_chan->base, &item->base, type);
|
|
return item;
|
|
}
|
|
|
|
static MouseModePipeItem *main_mouse_mode_item_new(MainChannel *main_chan,
|
|
int current_mode, int is_client_mouse_allowed)
|
|
{
|
|
MouseModePipeItem *item = spice_malloc(sizeof(MouseModePipeItem));
|
|
|
|
red_channel_pipe_item_init(&main_chan->base, &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(MainChannel *channel, int size)
|
|
{
|
|
PingPipeItem *item = spice_malloc(sizeof(PingPipeItem));
|
|
|
|
red_channel_pipe_item_init(&channel->base, &item->base, SPICE_MSG_PING);
|
|
item->size = size;
|
|
return item;
|
|
}
|
|
|
|
static TokensPipeItem *main_tokens_item_new(MainChannel *main_chan, int tokens)
|
|
{
|
|
TokensPipeItem *item = spice_malloc(sizeof(TokensPipeItem));
|
|
|
|
red_channel_pipe_item_init(&main_chan->base, &item->base,
|
|
SPICE_MSG_MAIN_AGENT_TOKEN);
|
|
item->tokens = tokens;
|
|
return item;
|
|
}
|
|
|
|
static AgentDataPipeItem *main_agent_data_item_new(MainChannel *channel,
|
|
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(&channel->base, &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(MainChannel *main_chan,
|
|
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(&main_chan->base, &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(MainChannel *main_chan,
|
|
uint8_t *mess, const int mess_len)
|
|
{
|
|
NotifyPipeItem *item = spice_malloc(sizeof(NotifyPipeItem));
|
|
|
|
red_channel_pipe_item_init(&main_chan->base, &item->base,
|
|
SPICE_MSG_NOTIFY);
|
|
item->mess = mess;
|
|
item->mess_len = mess_len;
|
|
return item;
|
|
}
|
|
|
|
static MigrateBeginPipeItem *main_migrate_begin_item_new(
|
|
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)
|
|
{
|
|
MigrateBeginPipeItem *item = spice_malloc(sizeof(MigrateBeginPipeItem));
|
|
|
|
red_channel_pipe_item_init(&main_chan->base, &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(
|
|
MainChannel *main_chan, int time)
|
|
{
|
|
MultiMediaTimePipeItem *item;
|
|
|
|
item = spice_malloc(sizeof(MultiMediaTimePipeItem));
|
|
red_channel_pipe_item_init(&main_chan->base, &item->base,
|
|
SPICE_MSG_MAIN_MULTI_MEDIA_TIME);
|
|
item->time = time;
|
|
return item;
|
|
}
|
|
|
|
static void main_channel_push_channels(MainChannel *main_chan)
|
|
{
|
|
RedsOutItem *item;
|
|
|
|
item = main_pipe_item_new(main_chan, SPICE_MSG_MAIN_CHANNELS_LIST);
|
|
red_channel_pipe_add_push(&main_chan->base, &item->base);
|
|
}
|
|
|
|
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_push_ping(Channel *channel, int size)
|
|
{
|
|
MainChannel *main_chan = channel->data;
|
|
PingPipeItem *item;
|
|
|
|
if (main_chan == NULL) {
|
|
return FALSE;
|
|
}
|
|
item = main_ping_item_new(main_chan, size);
|
|
red_channel_pipe_add_push(&main_chan->base, &item->base);
|
|
return TRUE;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void main_channel_push_mouse_mode(Channel *channel, int current_mode,
|
|
int is_client_mouse_allowed)
|
|
{
|
|
MainChannel *main_chan = channel->data;
|
|
MouseModePipeItem *item;
|
|
|
|
item = main_mouse_mode_item_new(main_chan, current_mode,
|
|
is_client_mouse_allowed);
|
|
red_channel_pipe_add_push(&main_chan->base, &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(Channel *channel)
|
|
{
|
|
RedsOutItem *item;
|
|
MainChannel *main_chan = channel->data;
|
|
|
|
item = main_pipe_item_new(main_chan, SPICE_MSG_MAIN_AGENT_CONNECTED);
|
|
red_channel_pipe_add_push(&main_chan->base, &item->base);
|
|
}
|
|
|
|
void main_channel_push_agent_disconnected(Channel *channel)
|
|
{
|
|
RedsOutItem *item;
|
|
MainChannel *main_chan = channel->data;
|
|
|
|
item = main_pipe_item_new(main_chan, SPICE_MSG_MAIN_AGENT_DISCONNECTED);
|
|
red_channel_pipe_add_push(&main_chan->base, &item->base);
|
|
}
|
|
|
|
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(Channel *channel, uint32_t num_tokens)
|
|
{
|
|
MainChannel *main_chan = channel->data;
|
|
TokensPipeItem *item = main_tokens_item_new(main_chan, num_tokens);
|
|
|
|
red_channel_pipe_add_push(&main_chan->base, &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(Channel *channel, uint8_t* data, size_t len,
|
|
spice_marshaller_item_free_func free_data, void *opaque)
|
|
{
|
|
MainChannel *main_chan = channel->data;
|
|
AgentDataPipeItem *item;
|
|
|
|
item = main_agent_data_item_new(main_chan, data, len, free_data, opaque);
|
|
red_channel_pipe_add_push(&main_chan->base, &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)
|
|
{
|
|
RedsOutItem *item = main_pipe_item_new(main_chan, SPICE_MSG_MIGRATE_DATA);
|
|
|
|
red_channel_pipe_add_push(&main_chan->base, &item->base);
|
|
}
|
|
|
|
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(RedChannel *base,
|
|
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(RedChannel *base,
|
|
uint32_t size, void *message)
|
|
{
|
|
MainChannel *main_chan = SPICE_CONTAINEROF(base, MainChannel, base);
|
|
MainMigrateData *data = message;
|
|
|
|
if (size < sizeof(*data)) {
|
|
red_printf("bad message size");
|
|
return FALSE;
|
|
}
|
|
main_chan->ping_id = data->ping_id;
|
|
reds_on_main_receive_migrate_data(data, ((uint8_t*)message) + size);
|
|
return TRUE;
|
|
}
|
|
|
|
void main_channel_push_init(Channel *channel, 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;
|
|
MainChannel *main_chan = channel->data;
|
|
|
|
item = main_init_item_new(main_chan,
|
|
connection_id, display_channels_hint, current_mouse_mode,
|
|
is_client_mouse_allowed, multi_media_time, ram_hint);
|
|
red_channel_pipe_add_push(&main_chan->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(Channel *channel, uint8_t *mess, const int mess_len)
|
|
{
|
|
MainChannel *main_chan = channel->data;
|
|
NotifyPipeItem *item = main_notify_item_new(main_chan, mess, mess_len);
|
|
|
|
red_channel_pipe_add_push(&main_chan->base, &item->base);
|
|
}
|
|
|
|
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(Channel *channel, int port, int sport,
|
|
char *host, uint16_t cert_pub_key_type, uint32_t cert_pub_key_len,
|
|
uint8_t *cert_pub_key)
|
|
{
|
|
MainChannel *main_chan = channel->data;
|
|
MigrateBeginPipeItem *item = main_migrate_begin_item_new(main_chan, port,
|
|
sport, host, cert_pub_key_type, cert_pub_key_len, cert_pub_key);
|
|
|
|
red_channel_pipe_add_push(&main_chan->base, &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(Channel *channel)
|
|
{
|
|
MainChannel *main_chan = channel->data;
|
|
RedsOutItem *item = main_pipe_item_new(main_chan, SPICE_MSG_MIGRATE);
|
|
|
|
red_channel_pipe_add_push(&main_chan->base, &item->base);
|
|
}
|
|
|
|
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(Channel *channel)
|
|
{
|
|
MainChannel *main_chan = channel->data;
|
|
RedsOutItem *item = main_pipe_item_new(main_chan,
|
|
SPICE_MSG_MAIN_MIGRATE_CANCEL);
|
|
|
|
red_channel_pipe_add_push(&main_chan->base, &item->base);
|
|
}
|
|
|
|
void main_channel_push_multi_media_time(Channel *channel, int time)
|
|
{
|
|
MainChannel *main_chan = channel->data;
|
|
|
|
MultiMediaTimePipeItem *item =
|
|
main_multi_media_time_item_new(main_chan, time);
|
|
red_channel_pipe_add_push(&main_chan->base, &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(Channel *channel)
|
|
{
|
|
MainChannel *main_chan = channel->data;
|
|
|
|
red_channel_pipe_add_push(&main_chan->base,
|
|
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(RedChannel *channel, PipeItem *base)
|
|
{
|
|
SpiceMarshaller *m = red_channel_get_marshaller(channel);
|
|
MainChannel *main_chan = SPICE_CONTAINEROF(channel, MainChannel, base);
|
|
|
|
red_channel_init_send_data(channel, 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, ++main_chan->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_get_message_serial(&main_chan->base),
|
|
main_chan->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_begin_send_message(channel);
|
|
}
|
|
|
|
static void main_channel_release_pipe_item(RedChannel *channel,
|
|
PipeItem *base, int item_pushed)
|
|
{
|
|
free(base);
|
|
}
|
|
|
|
static int main_channel_handle_parsed(RedChannel *channel, uint32_t size, uint16_t type, void *message)
|
|
{
|
|
MainChannel *main_chan = SPICE_CONTAINEROF(channel, MainChannel, 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(main_chan);
|
|
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 == main_chan->net_test_id) {
|
|
switch (main_chan->net_test_stage) {
|
|
case NET_TEST_STAGE_WARMUP:
|
|
main_chan->net_test_id++;
|
|
main_chan->net_test_stage = NET_TEST_STAGE_LATENCY;
|
|
break;
|
|
case NET_TEST_STAGE_LATENCY:
|
|
main_chan->net_test_id++;
|
|
main_chan->net_test_stage = NET_TEST_STAGE_RATE;
|
|
latency = roundtrip;
|
|
break;
|
|
case NET_TEST_STAGE_RATE:
|
|
main_chan->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" : "");
|
|
main_chan->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,
|
|
main_chan->net_test_id,
|
|
main_chan->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(RedChannel *channel)
|
|
{
|
|
reds_disconnect();
|
|
}
|
|
|
|
static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannel *channel, SpiceDataHeader *msg_header)
|
|
{
|
|
MainChannel *main_chan = SPICE_CONTAINEROF(channel, MainChannel, base);
|
|
|
|
return main_chan->recv_buf;
|
|
}
|
|
|
|
static void main_channel_release_msg_rcv_buf(RedChannel *channel, SpiceDataHeader *msg_header,
|
|
uint8_t *msg)
|
|
{
|
|
}
|
|
|
|
static int main_channel_config_socket(RedChannel *channel)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static void main_channel_hold_pipe_item(RedChannel *channel, PipeItem *item)
|
|
{
|
|
}
|
|
|
|
static int main_channel_handle_migrate_flush_mark(RedChannel *base)
|
|
{
|
|
main_channel_push_migrate_data_item(SPICE_CONTAINEROF(base, MainChannel, base));
|
|
return TRUE;
|
|
}
|
|
|
|
static void main_channel_link(Channel *channel, RedsStream *stream, int migration,
|
|
int num_common_caps, uint32_t *common_caps, int num_caps,
|
|
uint32_t *caps)
|
|
{
|
|
MainChannel *main_chan;
|
|
red_printf("");
|
|
ASSERT(channel->data == NULL);
|
|
|
|
main_chan = (MainChannel*)red_channel_create_parser(
|
|
sizeof(*main_chan), stream, core, migration, FALSE /* handle_acks */
|
|
,main_channel_config_socket
|
|
,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(main_chan);
|
|
channel->data = main_chan;
|
|
}
|
|
|
|
int main_channel_getsockname(Channel *channel, struct sockaddr *sa, socklen_t *salen)
|
|
{
|
|
MainChannel *main_chan = channel->data;
|
|
|
|
return main_chan ? getsockname(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1;
|
|
}
|
|
|
|
int main_channel_getpeername(Channel *channel, struct sockaddr *sa, socklen_t *salen)
|
|
{
|
|
MainChannel *main_chan = channel->data;
|
|
|
|
return main_chan ? getpeername(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1;
|
|
}
|
|
|
|
void main_channel_close(Channel *channel)
|
|
{
|
|
MainChannel *main_chan = channel->data;
|
|
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 = main_channel_link;
|
|
channel->shutdown = main_channel_shutdown;
|
|
channel->migrate = main_channel_migrate;
|
|
return channel;
|
|
}
|
|
|