summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2010-06-18 10:22:57 +0200
committerAlexander Larsson <alexl@redhat.com>2010-06-18 20:27:32 +0200
commitc553fafe4b1c9db32ea3e83b314ff23dfd44f554 (patch)
tree31a9f89259f52a2c54d0e75b27b226cf1dc1804e
parentec10a1662fb8b7c0f9ba8c26c506028a6b21ee44 (diff)
downloadspice-c553fafe4b1c9db32ea3e83b314ff23dfd44f554.tar.gz
spice-c553fafe4b1c9db32ea3e83b314ff23dfd44f554.tar.xz
spice-c553fafe4b1c9db32ea3e83b314ff23dfd44f554.zip
Use generated demarshallers in server
-rw-r--r--server/red_worker.c75
-rw-r--r--server/reds.c54
-rw-r--r--server/snd_worker.c62
3 files changed, 118 insertions, 73 deletions
diff --git a/server/red_worker.c b/server/red_worker.c
index 8b40116f..387861c5 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -51,6 +51,7 @@
#include "jpeg_encoder.h"
#include "rect.h"
#include "marshaller.h"
+#include "demarshallers.h"
#include "generated_marshallers.h"
//#define COMPRESS_STAT
@@ -348,11 +349,12 @@ typedef struct RedChannel RedChannel;
typedef void (*disconnect_channel_proc)(RedChannel *channel);
typedef void (*hold_item_proc)(void *item);
typedef void (*release_item_proc)(RedChannel *channel, void *item);
-typedef int (*handle_message_proc)(RedChannel *channel, SpiceDataHeader *message);
+typedef int (*handle_message_proc)(RedChannel *channel, size_t size, uint32_t type, void *message);
struct RedChannel {
EventListener listener;
uint32_t id;
+ spice_parse_channel_func_t parser;
struct RedWorker *worker;
RedsStreamContext *peer;
int migrate;
@@ -9888,15 +9890,11 @@ static void on_new_display_channel(RedWorker *worker)
}
}
-static int channel_handle_message(RedChannel *channel, SpiceDataHeader *message)
+static int channel_handle_message(RedChannel *channel, size_t size, uint32_t type, void *message)
{
- switch (message->type) {
+ switch (type) {
case SPICE_MSGC_ACK_SYNC:
- if (message->size != sizeof(uint32_t)) {
- red_printf("bad message size");
- return FALSE;
- }
- channel->client_ack_generation = *(uint32_t *)(message + 1);
+ channel->client_ack_generation = *(uint32_t *)message;
break;
case SPICE_MSGC_ACK:
if (channel->client_ack_generation == channel->ack_generation) {
@@ -9906,7 +9904,7 @@ static int channel_handle_message(RedChannel *channel, SpiceDataHeader *message)
case SPICE_MSGC_DISCONNECTING:
break;
default:
- red_printf("invalid message type %u", message->type);
+ red_printf("invalid message type %u", type);
return FALSE;
}
return TRUE;
@@ -10146,7 +10144,7 @@ static int display_channel_handle_migrate_mark(DisplayChannel *channel)
return TRUE;
}
-static int display_channel_handle_migrate_data(DisplayChannel *channel, SpiceDataHeader *message)
+static int display_channel_handle_migrate_data(DisplayChannel *channel, size_t size, void *message)
{
DisplayChannelMigrateData *migrate_data;
int i;
@@ -10156,11 +10154,11 @@ static int display_channel_handle_migrate_data(DisplayChannel *channel, SpiceDat
return FALSE;
}
channel->expect_migrate_data = FALSE;
- if (message->size < sizeof(*migrate_data)) {
+ if (size < sizeof(*migrate_data)) {
red_printf("bad message size");
return FALSE;
}
- migrate_data = (DisplayChannelMigrateData *)(message + 1);
+ migrate_data = (DisplayChannelMigrateData *)message;
if (migrate_data->magic != DISPLAY_MIGRATE_DATA_MAGIC ||
migrate_data->version != DISPLAY_MIGRATE_DATA_VERSION) {
red_printf("invalid content");
@@ -10197,26 +10195,22 @@ static int display_channel_handle_migrate_data(DisplayChannel *channel, SpiceDat
return TRUE;
}
-static int display_channel_handle_message(RedChannel *channel, SpiceDataHeader *message)
+static int display_channel_handle_message(RedChannel *channel, size_t size, uint32_t type, void *message)
{
- switch (message->type) {
+ switch (type) {
case SPICE_MSGC_DISPLAY_INIT:
- if (message->size != sizeof(SpiceMsgcDisplayInit)) {
- red_printf("bad message size");
- return FALSE;
- }
if (!((DisplayChannel *)channel)->expect_init) {
red_printf("unexpected SPICE_MSGC_DISPLAY_INIT");
return FALSE;
}
((DisplayChannel *)channel)->expect_init = FALSE;
- return display_channel_init((DisplayChannel *)channel, (SpiceMsgcDisplayInit *)(message + 1));
+ return display_channel_init((DisplayChannel *)channel, (SpiceMsgcDisplayInit *)message);
case SPICE_MSGC_MIGRATE_FLUSH_MARK:
return display_channel_handle_migrate_mark((DisplayChannel *)channel);
case SPICE_MSGC_MIGRATE_DATA:
- return display_channel_handle_migrate_data((DisplayChannel *)channel, message);
+ return display_channel_handle_migrate_data((DisplayChannel *)channel, size, message);
default:
- return channel_handle_message(channel, message);
+ return channel_handle_message(channel, size, type, message);
}
}
@@ -10249,19 +10243,34 @@ static void red_receive(RedChannel *channel)
} else {
channel->recive_data.now += n;
for (;;) {
- SpiceDataHeader *message = channel->recive_data.message;
- n = channel->recive_data.now - (uint8_t *)message;
+ SpiceDataHeader *header = channel->recive_data.message;
+ uint8_t *data = (uint8_t *)(header+1);
+ size_t parsed_size;
+ uint8_t *parsed;
+
+ n = channel->recive_data.now - (uint8_t *)header;
if (n < sizeof(SpiceDataHeader) ||
- n < sizeof(SpiceDataHeader) + message->size) {
+ n < sizeof(SpiceDataHeader) + header->size) {
break;
}
- if (!channel->handle_message(channel, message)) {
+ parsed = channel->parser((void *)data, data + header->size, header->type,
+ SPICE_VERSION_MINOR, &parsed_size);
+
+ if (parsed == NULL) {
+ red_printf("failed to parse message type %d", header->type);
+ channel->disconnect(channel);
+ return;
+ }
+
+ if (!channel->handle_message(channel, parsed_size, header->type, parsed)) {
+ free(parsed);
channel->disconnect(channel);
return;
}
- channel->recive_data.message = (SpiceDataHeader *)((uint8_t *)message +
- sizeof(SpiceDataHeader) +
- message->size);
+ free(parsed);
+ channel->recive_data.message = (SpiceDataHeader *)((uint8_t *)header +
+ sizeof(SpiceDataHeader) +
+ header->size);
}
if (channel->recive_data.now == (uint8_t *)channel->recive_data.message) {
@@ -10276,7 +10285,8 @@ static void red_receive(RedChannel *channel)
}
}
-static RedChannel *__new_channel(RedWorker *worker, int size, RedsStreamContext *peer, int migrate,
+static RedChannel *__new_channel(RedWorker *worker, int size, uint32_t channel_id,
+ RedsStreamContext *peer, int migrate,
event_listener_action_proc handler,
disconnect_channel_proc disconnect,
hold_item_proc hold_item,
@@ -10306,6 +10316,7 @@ static RedChannel *__new_channel(RedWorker *worker, int size, RedsStreamContext
ASSERT(size >= sizeof(*channel));
channel = spice_malloc0(size);
channel->id = worker->id;
+ channel->parser = spice_get_client_channel_parser(channel_id, NULL);
channel->listener.refs = 1;
channel->listener.action = handler;
channel->disconnect = disconnect;
@@ -10408,7 +10419,8 @@ static void handle_new_display_channel(RedWorker *worker, RedsStreamContext *pee
red_disconnect_display((RedChannel *)worker->display_channel);
- if (!(display_channel = (DisplayChannel *)__new_channel(worker, sizeof(*display_channel), peer,
+ if (!(display_channel = (DisplayChannel *)__new_channel(worker, sizeof(*display_channel),
+ SPICE_CHANNEL_DISPLAY, peer,
migrate, handle_channel_events,
red_disconnect_display,
display_channel_hold_item,
@@ -10508,7 +10520,8 @@ static void red_connect_cursor(RedWorker *worker, RedsStreamContext *peer, int m
red_disconnect_cursor((RedChannel *)worker->cursor_channel);
- if (!(channel = (CursorChannel *)__new_channel(worker, sizeof(*channel), peer, migrate,
+ if (!(channel = (CursorChannel *)__new_channel(worker, sizeof(*channel),
+ SPICE_CHANNEL_CURSOR, peer, migrate,
handle_channel_events,
red_disconnect_cursor,
cursor_channel_hold_item,
diff --git a/server/reds.c b/server/reds.c
index fee1cbfe..f9d91dbc 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -52,6 +52,7 @@
#include "stat.h"
#include "ring.h"
#include "config.h"
+#include "demarshallers.h"
#include "marshaller.h"
#include "generated_marshallers.h"
#ifdef HAVE_SLIRP
@@ -113,11 +114,12 @@ static void openssl_init();
#define CAPS_LOCK_SCAN_CODE 0x3a
typedef struct IncomingHandler {
+ spice_parse_channel_func_t parser;
void *opaque;
int shut;
uint8_t buf[RECIVE_BUF_SIZE];
uint32_t end_pos;
- void (*handle_message)(void *opaque, SpiceDataHeader *message);
+ void (*handle_message)(void *opaque, size_t size, uint32_t type, void *message);
} IncomingHandler;
typedef struct OutgoingHandler {
@@ -792,9 +794,19 @@ static int handle_incoming(RedsStreamContext *peer, IncomingHandler *handler)
end = buf + pos;
while (buf + sizeof(SpiceDataHeader) <= end &&
buf + sizeof(SpiceDataHeader) + (header = (SpiceDataHeader *)buf)->size <= end) {
- buf += sizeof(SpiceDataHeader) + header->size;
- handler->handle_message(handler->opaque, header);
+ uint8_t *data = (uint8_t *)(header+1);
+ size_t parsed_size;
+ uint8_t *parsed;
+ buf += sizeof(SpiceDataHeader) + header->size;
+ parsed = handler->parser(data, data + header->size, header->type,
+ SPICE_VERSION_MINOR, &parsed_size);
+ if (parsed == NULL) {
+ red_printf("failed to parse message type %d", header->type);
+ return -1;
+ }
+ handler->handle_message(handler->opaque, parsed_size, header->type, parsed);
+ free(parsed);
if (handler->shut) {
return -1;
}
@@ -1586,9 +1598,9 @@ static void main_channel_recive_migrate_data(MainMigrateData *data, uint8_t *end
ASSERT(state->num_client_tokens + state->num_tokens == REDS_AGENT_WINDOW_SIZE);
}
-static void reds_main_handle_message(void *opaque, SpiceDataHeader *message)
+static void reds_main_handle_message(void *opaque, size_t size, uint32_t type, void *message)
{
- switch (message->type) {
+ switch (type) {
case SPICE_MSGC_MAIN_AGENT_START: {
SpiceMsgcMainAgentTokens *agent_start;
@@ -1596,7 +1608,7 @@ static void reds_main_handle_message(void *opaque, SpiceDataHeader *message)
if (!reds->peer) {
return;
}
- agent_start = (SpiceMsgcMainAgentTokens *)(message + 1);
+ agent_start = (SpiceMsgcMainAgentTokens *)message;
reds->agent_state.client_agent_started = TRUE;
reds->agent_state.send_tokens = agent_start->num_tokens;
read_from_vdi_port();
@@ -1624,7 +1636,7 @@ static void reds_main_handle_message(void *opaque, SpiceDataHeader *message)
break;
}
- if (message->size > SPICE_AGENT_MAX_DATA_SIZE) {
+ if (size > SPICE_AGENT_MAX_DATA_SIZE) {
red_printf("invalid agent message");
reds_disconnect();
break;
@@ -1638,9 +1650,9 @@ static void reds_main_handle_message(void *opaque, SpiceDataHeader *message)
ring_remove(ring_item);
buf = (VDAgentExtBuf *)ring_item;
buf->base.now = (uint8_t *)&buf->base.chunk_header.port;
- buf->base.write_len = message->size + sizeof(VDIChunkHeader);
- buf->base.chunk_header.size = message->size;
- memcpy(buf->buf, message + 1, message->size);
+ buf->base.write_len = size + sizeof(VDIChunkHeader);
+ buf->base.chunk_header.size = size;
+ memcpy(buf->buf, message, size);
ring_add(&reds->agent_state.write_queue, ring_item);
write_to_vdi_port();
break;
@@ -1653,7 +1665,7 @@ static void reds_main_handle_message(void *opaque, SpiceDataHeader *message)
break;
}
- token = (SpiceMsgcMainAgentTokens *)(message + 1);
+ token = (SpiceMsgcMainAgentTokens *)message;
reds->agent_state.send_tokens += token->num_tokens;
read_from_vdi_port();
break;
@@ -1674,7 +1686,7 @@ static void reds_main_handle_message(void *opaque, SpiceDataHeader *message)
}
break;
case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST: {
- switch (((SpiceMsgcMainMouseModeRequest *)(message + 1))->mode) {
+ switch (((SpiceMsgcMainMouseModeRequest *)message)->mode) {
case SPICE_MOUSE_MODE_CLIENT:
if (reds->is_client_mouse_allowed) {
reds_set_mouse_mode(SPICE_MOUSE_MODE_CLIENT);
@@ -1691,7 +1703,7 @@ static void reds_main_handle_message(void *opaque, SpiceDataHeader *message)
break;
}
case SPICE_MSGC_PONG: {
- SpiceMsgPing *ping = (SpiceMsgPing *)(message + 1);
+ SpiceMsgPing *ping = (SpiceMsgPing *)message;
uint64_t roundtrip;
struct timespec ts;
@@ -1743,15 +1755,15 @@ static void reds_main_handle_message(void *opaque, SpiceDataHeader *message)
main_channel_push_migrate_data_item();
break;
case SPICE_MSGC_MIGRATE_DATA:
- main_channel_recive_migrate_data((MainMigrateData *)(message + 1),
- (uint8_t *)(message + 1) + message->size);
+ main_channel_recive_migrate_data((MainMigrateData *)message,
+ ((uint8_t *)message) + size);
reds->mig_target = FALSE;
while (write_to_vdi_port() || read_from_vdi_port());
break;
case SPICE_MSGC_DISCONNECTING:
break;
default:
- red_printf("unexpected type %d", message->type);
+ red_printf("unexpected type %d", type);
}
}
@@ -2058,12 +2070,12 @@ static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
return sif->get_leds(sin);
}
-static void inputs_handle_input(void *opaque, SpiceDataHeader *header)
+static void inputs_handle_input(void *opaque, size_t size, uint32_t type, void *message)
{
InputsState *state = (InputsState *)opaque;
- uint8_t *buf = (uint8_t *)(header + 1);
+ uint8_t *buf = (uint8_t *)message;
- switch (header->type) {
+ switch (type) {
case SPICE_MSGC_INPUTS_KEY_DOWN: {
SpiceMsgcKeyDown *key_up = (SpiceMsgcKeyDown *)buf;
if (key_up->code == CAPS_LOCK_SCAN_CODE || key_up->code == NUM_LOCK_SCAN_CODE ||
@@ -2216,7 +2228,7 @@ static void inputs_handle_input(void *opaque, SpiceDataHeader *header)
case SPICE_MSGC_DISCONNECTING:
break;
default:
- red_printf("unexpected type %d", header->type);
+ red_printf("unexpected type %d", type);
}
}
@@ -2344,6 +2356,7 @@ static void inputs_link(Channel *channel, RedsStreamContext *peer, int migration
inputs_state->peer = peer;
inputs_state->end_pos = 0;
inputs_state->channel = channel;
+ inputs_state->in_handler.parser = spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL);
inputs_state->in_handler.opaque = inputs_state;
inputs_state->in_handler.handle_message = inputs_handle_input;
inputs_state->out_handler.length = 0;
@@ -3571,6 +3584,7 @@ static void do_spice_init(SpiceCoreInterface *core_interface)
reds->listen_socket = -1;
reds->secure_listen_socket = -1;
reds->peer = NULL;
+ reds->in_handler.parser = spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL);
reds->in_handler.handle_message = reds_main_handle_message;
ring_init(&reds->outgoing.pipe);
reds->outgoing.vec = reds->outgoing.vec_buf;
diff --git a/server/snd_worker.c b/server/snd_worker.c
index cd9af971..544da3bd 100644
--- a/server/snd_worker.c
+++ b/server/snd_worker.c
@@ -30,6 +30,7 @@
#include "snd_worker.h"
#include "marshaller.h"
#include "generated_marshallers.h"
+#include "demarshallers.h"
#define MAX_SEND_VEC 100
@@ -65,7 +66,7 @@ enum RecordCommand {
typedef struct SndChannel SndChannel;
typedef void (*send_messages_proc)(void *in_channel);
-typedef int (*handle_message_proc)(SndChannel *channel, SpiceDataHeader *message);
+typedef int (*handle_message_proc)(SndChannel *channel, size_t size, uint32_t type, void *message);
typedef void (*on_message_done_proc)(SndChannel *channel);
typedef void (*cleanup_channel_proc)(SndChannel *channel);
@@ -74,6 +75,7 @@ typedef struct SndWorker SndWorker;
struct SndChannel {
RedsStreamContext *peer;
SndWorker *worker;
+ spice_parse_channel_func_t parser;
int active;
int client_active;
@@ -266,12 +268,11 @@ static int snd_send_data(SndChannel *channel)
return TRUE;
}
-static int snd_record_handle_write(RecordChannel *record_channel, SpiceDataHeader *message)
+static int snd_record_handle_write(RecordChannel *record_channel, size_t size, void *message)
{
SpiceMsgcRecordPacket *packet;
uint32_t write_pos;
uint32_t* data;
- uint32_t size;
uint32_t len;
uint32_t now;
@@ -279,8 +280,8 @@ static int snd_record_handle_write(RecordChannel *record_channel, SpiceDataHeade
return FALSE;
}
- packet = (SpiceMsgcRecordPacket *)(message + 1);
- size = message->size - sizeof(*packet);
+ packet = (SpiceMsgcRecordPacket *)message;
+ size = size - sizeof(*packet);
if (record_channel->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1) {
int celt_err = celt051_decode(record_channel->celt_decoder, packet->data, size,
@@ -316,34 +317,34 @@ static int snd_record_handle_write(RecordChannel *record_channel, SpiceDataHeade
return TRUE;
}
-static int snd_playback_handle_message(SndChannel *channel, SpiceDataHeader *message)
+static int snd_playback_handle_message(SndChannel *channel, size_t size, uint32_t type, void *message)
{
if (!channel) {
return FALSE;
}
- switch (message->type) {
+ switch (type) {
case SPICE_MSGC_DISCONNECTING:
break;
default:
- red_printf("invalid message type %u", message->type);
+ red_printf("invalid message type %u", type);
return FALSE;
}
return TRUE;
}
-static int snd_record_handle_message(SndChannel *channel, SpiceDataHeader *message)
+static int snd_record_handle_message(SndChannel *channel, size_t size, uint32_t type, void *message)
{
RecordChannel *record_channel = (RecordChannel *)channel;
if (!channel) {
return FALSE;
}
- switch (message->type) {
+ switch (type) {
case SPICE_MSGC_RECORD_DATA:
- return snd_record_handle_write((RecordChannel *)channel, message);
+ return snd_record_handle_write((RecordChannel *)channel, size, message);
case SPICE_MSGC_RECORD_MODE: {
- SpiceMsgcRecordMode *mode = (SpiceMsgcRecordMode *)(message + 1);
+ SpiceMsgcRecordMode *mode = (SpiceMsgcRecordMode *)message;
record_channel->mode = mode->mode;
record_channel->mode_time = mode->time;
if (record_channel->mode != SPICE_AUDIO_DATA_MODE_CELT_0_5_1 &&
@@ -353,14 +354,14 @@ static int snd_record_handle_message(SndChannel *channel, SpiceDataHeader *messa
break;
}
case SPICE_MSGC_RECORD_START_MARK: {
- SpiceMsgcRecordStartMark *mark = (SpiceMsgcRecordStartMark *)(message + 1);
+ SpiceMsgcRecordStartMark *mark = (SpiceMsgcRecordStartMark *)message;
record_channel->start_time = mark->time;
break;
}
case SPICE_MSGC_DISCONNECTING:
break;
case SPICE_MSGC_MIGRATE_DATA: {
- RecordMigrateData* mig_data = (RecordMigrateData *)(message + 1);
+ RecordMigrateData* mig_data = (RecordMigrateData *)message;
if (mig_data->version != RECORD_MIG_VERSION) {
red_printf("invalid mig version");
break;
@@ -371,7 +372,7 @@ static int snd_record_handle_message(SndChannel *channel, SpiceDataHeader *messa
break;
}
default:
- red_printf("invalid message type %u", message->type);
+ red_printf("invalid message type %u", type);
return FALSE;
}
return TRUE;
@@ -410,18 +411,31 @@ static void snd_receive(void* data)
} else {
channel->recive_data.now += n;
for (;;) {
- SpiceDataHeader *message = channel->recive_data.message;
- n = channel->recive_data.now - (uint8_t *)message;
- if (n < sizeof(SpiceDataHeader) || n < sizeof(SpiceDataHeader) + message->size) {
+ SpiceDataHeader *header = channel->recive_data.message;
+ uint8_t *data = (uint8_t *)(header+1);
+ size_t parsed_size;
+ uint8_t *parsed;
+
+ n = channel->recive_data.now - (uint8_t *)header;
+ if (n < sizeof(SpiceDataHeader) || n < sizeof(SpiceDataHeader) + header->size) {
break;
}
- if (!channel->handle_message(channel, message)) {
+ parsed = channel->parser((void *)data, data + header->size, header->type,
+ SPICE_VERSION_MINOR, &parsed_size);
+ if (parsed == NULL) {
+ red_printf("failed to parse message type %d", header->type);
+ snd_disconnect_channel(channel);
+ return;
+ }
+ if (!channel->handle_message(channel, parsed_size, header->type, parsed)) {
+ free(parsed);
snd_disconnect_channel(channel);
return;
}
- channel->recive_data.message = (SpiceDataHeader *)((uint8_t *)message +
+ free(parsed);
+ channel->recive_data.message = (SpiceDataHeader *)((uint8_t *)header +
sizeof(SpiceDataHeader) +
- message->size);
+ header->size);
}
if (channel->recive_data.now == (uint8_t *)channel->recive_data.message) {
channel->recive_data.now = channel->recive_data.buf;
@@ -718,7 +732,8 @@ static void snd_record_send(void* data)
}
}
-static SndChannel *__new_channel(SndWorker *worker, int size, RedsStreamContext *peer,
+static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_id,
+ RedsStreamContext *peer,
int migrate, send_messages_proc send_messages,
handle_message_proc handle_message,
on_message_done_proc on_message_done,
@@ -758,6 +773,7 @@ static SndChannel *__new_channel(SndWorker *worker, int size, RedsStreamContext
ASSERT(size >= sizeof(*channel));
channel = spice_malloc0(size);
+ channel->parser = spice_get_client_channel_parser(channel_id, NULL);
channel->peer = peer;
channel->worker = worker;
channel->recive_data.message = (SpiceDataHeader *)channel->recive_data.buf;
@@ -941,6 +957,7 @@ static void snd_set_playback_peer(Channel *channel, RedsStreamContext *peer, int
if (!(playback_channel = (PlaybackChannel *)__new_channel(worker,
sizeof(*playback_channel),
+ SPICE_CHANNEL_PLAYBACK,
peer,
migration,
snd_playback_send,
@@ -1106,6 +1123,7 @@ static void snd_set_record_peer(Channel *channel, RedsStreamContext *peer, int m
if (!(record_channel = (RecordChannel *)__new_channel(worker,
sizeof(*record_channel),
+ SPICE_CHANNEL_RECORD,
peer,
migration,
snd_record_send,