[client] spice: protocol updates for performance and agent support

Note: agent support is not complete at this point due to lack of documentation.
This commit is contained in:
Geoffrey McRae 2019-02-22 03:03:11 +11:00
parent 5bd748680f
commit 3df4bb3a54
3 changed files with 270 additions and 71 deletions

View File

@ -132,4 +132,10 @@ typedef struct SpiceMsgcMousePress
SpiceMsgcMousePress, SpiceMsgcMousePress,
SpiceMsgcMouseRelease; SpiceMsgcMouseRelease;
// spice is missing these defines, the offical reference library incorrectly uses the VD defines
#define COMMON_CAPS_BYTES (((SPICE_COMMON_CAP_MINI_HEADER + 32) / 8) & ~3)
#define COMMON_SET_CAPABILITY(caps, index) \
{ (caps)[(index) / 32] |= (1 << ((index) % 32)); }
#pragma pack(pop) #pragma pack(pop)

View File

@ -17,6 +17,9 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
//#define DEBUG_SPICE
#include "spice.h" #include "spice.h"
#include "utils.h" #include "utils.h"
#include "debug.h" #include "debug.h"
@ -26,6 +29,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
@ -36,6 +40,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <arpa/inet.h> #include <arpa/inet.h>
#include <spice/protocol.h> #include <spice/protocol.h>
#include <spice/vd_agent.h>
#include "messages.h" #include "messages.h"
#include "rsa.h" #include "rsa.h"
@ -52,6 +57,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#define DEBUG_KEYBOARD(fmt, args...) do {} while(0) #define DEBUG_KEYBOARD(fmt, args...) do {} while(0)
#endif #endif
#define SPICE_AGENT_TOKENS_MAX 10
// ============================================================================ // ============================================================================
@ -59,6 +66,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
struct SpiceChannel struct SpiceChannel
{ {
bool connected; bool connected;
bool ready;
bool initDone; bool initDone;
uint8_t channelType; uint8_t channelType;
int socket; int socket;
@ -122,18 +130,23 @@ void spice_disconnect_channel(struct SpiceChannel * channel);
bool spice_process_ack(struct SpiceChannel * channel); bool spice_process_ack(struct SpiceChannel * channel);
bool spice_on_common_read (struct SpiceChannel * channel, SpiceDataHeader * header, bool * handled); bool spice_on_common_read (struct SpiceChannel * channel, SpiceMiniDataHeader * header, bool * handled);
bool spice_on_main_channel_read (); bool spice_on_main_channel_read ();
bool spice_on_inputs_channel_read(); bool spice_on_inputs_channel_read();
bool spice_agent_connect(); bool spice_agent_process ();
void spice_agent_disconnect(); bool spice_agent_connect (bool startup);
bool spice_agent_send_caps(bool request);
bool spice_read (const struct SpiceChannel * channel, void * buffer, const ssize_t size); // thread safe read/write methods
ssize_t spice_write (const struct SpiceChannel * channel, const void * buffer, const ssize_t size); bool spice_write_msg (struct SpiceChannel * channel, uint32_t type, const void * buffer, const ssize_t size);
bool spice_write_msg(struct SpiceChannel * channel, uint32_t type, const void * buffer, const ssize_t size); bool spice_agent_write_msg (uint32_t type, const void * buffer, const ssize_t size);
bool spice_discard (const struct SpiceChannel * channel, ssize_t size);
// non thread safe read/write methods (nl = non-locking)
bool spice_read_nl (const struct SpiceChannel * channel, void * buffer, const ssize_t size);
ssize_t spice_write_nl (const struct SpiceChannel * channel, const void * buffer, const ssize_t size);
bool spice_discard_nl (const struct SpiceChannel * channel, ssize_t size);
bool spice_write_msg_nl( struct SpiceChannel * channel, uint32_t type, const void * buffer, const ssize_t size);
// ============================================================================ // ============================================================================
@ -264,20 +277,19 @@ bool spice_process_ack(struct SpiceChannel * channel)
// ============================================================================ // ============================================================================
bool spice_on_common_read(struct SpiceChannel * channel, SpiceDataHeader * header, bool * handled) bool spice_on_common_read(struct SpiceChannel * channel, SpiceMiniDataHeader * header, bool * handled)
{ {
*handled = false; *handled = false;
if (!spice_read(channel, header, sizeof(SpiceDataHeader))) if (!spice_read_nl(channel, header, sizeof(SpiceMiniDataHeader)))
{ {
DEBUG_ERROR("read failure"); DEBUG_ERROR("read failure");
return false; return false;
} }
#if 0 //#if 0
printf("socket: %d, serial: %6u, type: %2u, size %6u, sub_list %4u\n", DEBUG_PROTO("socket: %d, type: %2u, size %6u",
channel->socket, channel->socket, header->type, header->size);
header->serial, header->type, header->size, header->sub_list); //#endif
#endif
if (!channel->initDone) if (!channel->initDone)
return true; return true;
@ -300,7 +312,7 @@ bool spice_on_common_read(struct SpiceChannel * channel, SpiceDataHeader * heade
*handled = true; *handled = true;
SpiceMsgSetAck in; SpiceMsgSetAck in;
if (!spice_read(channel, &in, sizeof(in))) if (!spice_read_nl(channel, &in, sizeof(in)))
return false; return false;
channel->ackFrequency = in.window; channel->ackFrequency = in.window;
@ -319,14 +331,17 @@ bool spice_on_common_read(struct SpiceChannel * channel, SpiceDataHeader * heade
*handled = true; *handled = true;
SpiceMsgPing in; SpiceMsgPing in;
if (!spice_read(channel, &in, sizeof(in))) if (!spice_read_nl(channel, &in, sizeof(in)))
return false; return false;
if (!spice_discard(channel, header->size - sizeof(in))) const int discard = header->size - sizeof(in);
if (!spice_discard_nl(channel, discard))
{ {
DEBUG_ERROR("failed discarding enough bytes from the ping packet"); DEBUG_ERROR("failed discarding enough bytes (%d) from the ping packet", discard);
return false; return false;
} }
else
DEBUG_PROTO("discard %d", discard);
SpiceMsgcPong out; SpiceMsgcPong out;
out.id = in.id; out.id = in.id;
@ -349,11 +364,11 @@ bool spice_on_common_read(struct SpiceChannel * channel, SpiceDataHeader * heade
{ {
DEBUG_PROTO("SPICE_MSG_NOTIFY"); DEBUG_PROTO("SPICE_MSG_NOTIFY");
SpiceMsgNotify in; SpiceMsgNotify in;
if (!spice_read(channel, &in, sizeof(in))) if (!spice_read_nl(channel, &in, sizeof(in)))
return false; return false;
char msg[in.message_len+1]; char msg[in.message_len+1];
if (!spice_read(channel, msg, in.message_len+1)) if (!spice_read_nl(channel, msg, in.message_len+1))
return false; return false;
DEBUG_INFO("notify message: %s", msg); DEBUG_INFO("notify message: %s", msg);
@ -371,7 +386,7 @@ bool spice_on_main_channel_read()
{ {
struct SpiceChannel *channel = &spice.scMain; struct SpiceChannel *channel = &spice.scMain;
SpiceDataHeader header; SpiceMiniDataHeader header;
bool handled; bool handled;
if (!spice_on_common_read(channel, &header, &handled)) if (!spice_on_common_read(channel, &header, &handled))
@ -396,7 +411,7 @@ bool spice_on_main_channel_read()
channel->initDone = true; channel->initDone = true;
SpiceMsgMainInit msg; SpiceMsgMainInit msg;
if (!spice_read(channel, &msg, sizeof(msg))) if (!spice_read_nl(channel, &msg, sizeof(msg)))
{ {
spice_disconnect(); spice_disconnect();
return false; return false;
@ -405,13 +420,14 @@ bool spice_on_main_channel_read()
spice.sessionID = msg.session_id; spice.sessionID = msg.session_id;
spice.agentTokens = msg.agent_tokens; spice.agentTokens = msg.agent_tokens;
if (msg.agent_connected) spice.hasAgent = msg.agent_connected;
if (spice.hasAgent && !spice_agent_connect(true))
{ {
spice.hasAgent = true; spice_disconnect();
spice_agent_connect(); DEBUG_ERROR("failed to connect to spice agent");
return false;
} }
else
spice.hasAgent = false;
if (msg.current_mouse_mode != SPICE_MOUSE_MODE_CLIENT && !spice_mouse_mode(false)) if (msg.current_mouse_mode != SPICE_MOUSE_MODE_CLIENT && !spice_mouse_mode(false))
{ {
@ -434,7 +450,7 @@ bool spice_on_main_channel_read()
DEBUG_PROTO("SPICE_MSG_MAIN_CHANNELS_LIST"); DEBUG_PROTO("SPICE_MSG_MAIN_CHANNELS_LIST");
SpiceMainChannelsList msg; SpiceMainChannelsList msg;
if (!spice_read(channel, &msg, sizeof(msg))) if (!spice_read_nl(channel, &msg, sizeof(msg)))
{ {
DEBUG_ERROR("Failed to read channel list msg"); DEBUG_ERROR("Failed to read channel list msg");
spice_disconnect(); spice_disconnect();
@ -443,7 +459,7 @@ bool spice_on_main_channel_read()
// documentation doesn't state that the array is null terminated but it seems that it is // documentation doesn't state that the array is null terminated but it seems that it is
SpiceChannelID channels[msg.num_of_channels]; SpiceChannelID channels[msg.num_of_channels];
if (!spice_read(channel, &channels, msg.num_of_channels * sizeof(SpiceChannelID))) if (!spice_read_nl(channel, &channels, msg.num_of_channels * sizeof(SpiceChannelID)))
{ {
DEBUG_ERROR("Failed to read channel list vector"); DEBUG_ERROR("Failed to read channel list vector");
spice_disconnect(); spice_disconnect();
@ -477,14 +493,19 @@ bool spice_on_main_channel_read()
if (header.type == SPICE_MSG_MAIN_AGENT_CONNECTED) if (header.type == SPICE_MSG_MAIN_AGENT_CONNECTED)
{ {
spice.hasAgent = true; spice.hasAgent = true;
spice_agent_connect(); if (!spice_agent_connect(false))
{
DEBUG_ERROR("failed to connect to spice agent");
spice_disconnect();
return false;
}
return true; return true;
} }
if (header.type == SPICE_MSG_MAIN_AGENT_DISCONNECTED) if (header.type == SPICE_MSG_MAIN_AGENT_DISCONNECTED)
{ {
uint32_t error; uint32_t error;
if (!spice_read(channel, &error, sizeof(error))) if (!spice_read_nl(channel, &error, sizeof(error)))
{ {
DEBUG_ERROR("failed to read agent disconnect error code"); DEBUG_ERROR("failed to read agent disconnect error code");
spice_disconnect(); spice_disconnect();
@ -493,12 +514,22 @@ bool spice_on_main_channel_read()
DEBUG_INFO("Spice agent disconnected, error: %u", error); DEBUG_INFO("Spice agent disconnected, error: %u", error);
spice.hasAgent = false; spice.hasAgent = false;
spice_agent_disconnect(); return true;
}
if (header.type == SPICE_MSG_MAIN_AGENT_DATA && spice.hasAgent)
{
if (!spice_agent_process())
{
DEBUG_ERROR("failed to process spice agent message");
spice_disconnect();
return false;
}
return true; return true;
} }
DEBUG_WARN("main channel unhandled message type %u", header.type); DEBUG_WARN("main channel unhandled message type %u", header.type);
spice_discard(channel, header.size); spice_discard_nl(channel, header.size);
return true; return true;
} }
@ -508,7 +539,7 @@ bool spice_on_inputs_channel_read()
{ {
struct SpiceChannel *channel = &spice.scInputs; struct SpiceChannel *channel = &spice.scInputs;
SpiceDataHeader header; SpiceMiniDataHeader header;
bool handled; bool handled;
if (!spice_on_common_read(channel, &header, &handled)) if (!spice_on_common_read(channel, &header, &handled))
@ -535,7 +566,7 @@ bool spice_on_inputs_channel_read()
channel->initDone = true; channel->initDone = true;
SpiceMsgInputsInit in; SpiceMsgInputsInit in;
if (!spice_read(channel, &in, sizeof(in))) if (!spice_read_nl(channel, &in, sizeof(in)))
return false; return false;
return true; return true;
@ -545,7 +576,7 @@ bool spice_on_inputs_channel_read()
{ {
DEBUG_PROTO("SPICE_MSG_INPUTS_KEY_MODIFIERS"); DEBUG_PROTO("SPICE_MSG_INPUTS_KEY_MODIFIERS");
SpiceMsgInputsInit in; SpiceMsgInputsInit in;
if (!spice_read(channel, &in, sizeof(in))) if (!spice_read_nl(channel, &in, sizeof(in)))
return false; return false;
spice.kb.modifiers = in.modifiers; spice.kb.modifiers = in.modifiers;
@ -566,7 +597,7 @@ bool spice_on_inputs_channel_read()
} }
DEBUG_WARN("inputs channel unhandled message type %u", header.type); DEBUG_WARN("inputs channel unhandled message type %u", header.type);
spice_discard(channel, header.size); spice_discard_nl(channel, header.size);
return true; return true;
} }
@ -619,12 +650,17 @@ bool spice_connect_channel(struct SpiceChannel * channel)
} }
channel->connected = true; channel->connected = true;
uint32_t supportCaps[COMMON_CAPS_BYTES / sizeof(uint32_t)];
COMMON_SET_CAPABILITY(supportCaps, SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION);
COMMON_SET_CAPABILITY(supportCaps, SPICE_COMMON_CAP_AUTH_SPICE );
COMMON_SET_CAPABILITY(supportCaps, SPICE_COMMON_CAP_MINI_HEADER );
SpiceLinkHeader header = SpiceLinkHeader header =
{ {
.magic = SPICE_MAGIC , .magic = SPICE_MAGIC ,
.major_version = SPICE_VERSION_MAJOR, .major_version = SPICE_VERSION_MAJOR,
.minor_version = SPICE_VERSION_MINOR, .minor_version = SPICE_VERSION_MINOR,
.size = sizeof(SpiceLinkMess) .size = sizeof(SpiceLinkMess) + sizeof(supportCaps)
}; };
SpiceLinkMess message = SpiceLinkMess message =
@ -632,15 +668,23 @@ bool spice_connect_channel(struct SpiceChannel * channel)
.connection_id = spice.sessionID, .connection_id = spice.sessionID,
.channel_type = channel->channelType, .channel_type = channel->channelType,
.channel_id = spice.channelID, .channel_id = spice.channelID,
.num_common_caps = 0, .num_common_caps = sizeof(supportCaps) / sizeof(uint32_t),
.num_channel_caps = 0, .num_channel_caps = 0,
.caps_offset = sizeof(SpiceLinkMess) .caps_offset = sizeof(SpiceLinkMess)
}; };
spice_write(channel, &header , sizeof(header )); if (
spice_write(channel, &message, sizeof(message)); !spice_write_nl(channel, &header , sizeof(header )) ||
!spice_write_nl(channel, &message , sizeof(message )) ||
!spice_write_nl(channel, &supportCaps, sizeof(supportCaps))
)
{
DEBUG_ERROR("failed to write the initial payload");
spice_disconnect_channel(channel);
return false;
}
if (!spice_read(channel, &header, sizeof(header))) if (!spice_read_nl(channel, &header, sizeof(header)))
{ {
DEBUG_ERROR("failed to read SpiceLinkHeader"); DEBUG_ERROR("failed to read SpiceLinkHeader");
spice_disconnect_channel(channel); spice_disconnect_channel(channel);
@ -662,7 +706,7 @@ bool spice_connect_channel(struct SpiceChannel * channel)
} }
SpiceLinkReply reply; SpiceLinkReply reply;
if (!spice_read(channel, &reply, sizeof(reply))) if (!spice_read_nl(channel, &reply, sizeof(reply)))
{ {
DEBUG_ERROR("failed to read SpiceLinkReply"); DEBUG_ERROR("failed to read SpiceLinkReply");
spice_disconnect_channel(channel); spice_disconnect_channel(channel);
@ -678,17 +722,34 @@ bool spice_connect_channel(struct SpiceChannel * channel)
uint32_t capsCommon [reply.num_common_caps ]; uint32_t capsCommon [reply.num_common_caps ];
uint32_t capsChannel[reply.num_channel_caps]; uint32_t capsChannel[reply.num_channel_caps];
spice_read(channel, &capsCommon , sizeof(capsCommon )); if (
spice_read(channel, &capsChannel, sizeof(capsChannel)); !spice_read_nl(channel, &capsCommon , sizeof(capsCommon)) ||
!spice_read_nl(channel, &capsChannel, sizeof(capsChannel))
struct spice_password pass; )
if (!spice_rsa_encrypt_password(reply.pub_key, spice.password, &pass))
{ {
DEBUG_ERROR("failed to read the capabilities");
spice_disconnect_channel(channel); spice_disconnect_channel(channel);
return false; return false;
} }
if (!spice_write(channel, pass.data, pass.size)) SpiceLinkAuthMechanism auth;
auth.auth_mechanism = SPICE_COMMON_CAP_AUTH_SPICE;
if (!spice_write_nl(channel, &auth, sizeof(auth)))
{
DEBUG_ERROR("failed to write the auth mechanism");
spice_disconnect_channel(channel);
return false;
}
struct spice_password pass;
if (!spice_rsa_encrypt_password(reply.pub_key, spice.password, &pass))
{
DEBUG_ERROR("failed to encrypt the password");
spice_disconnect_channel(channel);
return false;
}
if (!spice_write_nl(channel, pass.data, pass.size))
{ {
spice_rsa_free_password(&pass); spice_rsa_free_password(&pass);
DEBUG_ERROR("failed to write encrypted data"); DEBUG_ERROR("failed to write encrypted data");
@ -699,7 +760,7 @@ bool spice_connect_channel(struct SpiceChannel * channel)
spice_rsa_free_password(&pass); spice_rsa_free_password(&pass);
uint32_t linkResult; uint32_t linkResult;
if (!spice_read(channel, &linkResult, sizeof(linkResult))) if (!spice_read_nl(channel, &linkResult, sizeof(linkResult)))
{ {
DEBUG_ERROR("failed to read SpiceLinkResult"); DEBUG_ERROR("failed to read SpiceLinkResult");
spice_disconnect_channel(channel); spice_disconnect_channel(channel);
@ -713,6 +774,7 @@ bool spice_connect_channel(struct SpiceChannel * channel)
return false; return false;
} }
channel->ready = true;
return true; return true;
} }
@ -738,16 +800,21 @@ void spice_disconnect_channel(struct SpiceChannel * channel)
// ============================================================================ // ============================================================================
bool spice_agent_connect() bool spice_agent_connect(bool startup)
{ {
return true;
DEBUG_INFO("Spice agent available, sending start"); DEBUG_INFO("Spice agent available, sending start");
if (!spice_write_msg(&spice.scMain, SPICE_MSGC_MAIN_AGENT_START, &spice.agentTokens, sizeof(spice.agentTokens))) const uint32_t tokens = SPICE_AGENT_TOKENS_MAX;
if (!spice_write_msg(&spice.scMain, SPICE_MSGC_MAIN_AGENT_START, &tokens, sizeof(tokens)))
{ {
DEBUG_ERROR("failed to send agent start message"); DEBUG_ERROR("failed to send agent start message");
return false; return false;
} }
if (startup && !spice_agent_send_caps(startup))
return false;
//todo, agent setup //todo, agent setup
return true; return true;
@ -755,14 +822,116 @@ bool spice_agent_connect()
// ============================================================================ // ============================================================================
void spice_agent_disconnect() bool spice_agent_process()
{ {
// VDAgentMessage msg;
if (!spice_read_nl(&spice.scMain, &msg, sizeof(msg)))
{
DEBUG_ERROR("failed to read spice agent message");
return false;
}
if (msg.protocol != VD_AGENT_PROTOCOL)
{
DEBUG_ERROR("invalid or unknown spice agent protocol");
return false;
}
if (msg.type == VD_AGENT_ANNOUNCE_CAPABILITIES)
{
VDAgentAnnounceCapabilities *caps = (VDAgentAnnounceCapabilities *)malloc(msg.size);
memset(caps, 0, msg.size);
if (!spice_read_nl(&spice.scMain, caps, msg.size))
{
DEBUG_ERROR("failed to read agent message payload");
free(caps);
return false;
}
const int capsSize = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(msg.size);
if (VD_AGENT_HAS_CAPABILITY(caps->caps, capsSize, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))
{
DEBUG_INFO("clipboard capability detected");
}
else
DEBUG_INFO("no clipboard capability detected");
if (caps->request && !spice_agent_send_caps(false))
{
free(caps);
return false;
}
free(caps);
return true;
}
DEBUG_WARN("unknown agent message type %d", msg.type);
spice_discard_nl(&spice.scMain, msg.size);
return true;
}
// ============================================================================
bool spice_agent_send_caps(bool request)
{
const ssize_t capsSize = (sizeof(VDAgentAnnounceCapabilities) + VD_AGENT_CAPS_BYTES);
VDAgentAnnounceCapabilities *caps = (VDAgentAnnounceCapabilities *)malloc(capsSize);
memset(caps, 0, capsSize);
caps->request = request ? 1 : 0;
VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
if (!spice_agent_write_msg(VD_AGENT_ANNOUNCE_CAPABILITIES, caps, capsSize))
{
DEBUG_ERROR("failed to send agent capabilities");
free(caps);
return false;
}
free(caps);
return true;
} }
// ============================================================================ // ============================================================================
ssize_t spice_write(const struct SpiceChannel * channel, const void * buffer, const ssize_t size) bool spice_agent_write_msg(uint32_t type, const void * buffer, const ssize_t size)
{
DEBUG_PROTO("agent_write: t=%d, b=%ld", type, size);
VDAgentMessage msg;
msg.protocol = VD_AGENT_PROTOCOL;
msg.type = type;
msg.opaque = 0;
msg.size = size;
LG_LOCK(spice.scMain.lock);
if (!spice_write_msg_nl(&spice.scMain, SPICE_MSGC_MAIN_AGENT_DATA, &msg, sizeof(msg)))
{
LG_UNLOCK(spice.scMain.lock);
DEBUG_ERROR("failed to write agent data header");
return false;
}
if (spice_write_nl(&spice.scMain, buffer, size) != size)
{
LG_UNLOCK(spice.scMain.lock);
DEBUG_ERROR("failed to write agent data payload");
return false;
}
LG_UNLOCK(spice.scMain.lock);
return true;
}
// ============================================================================
ssize_t spice_write_nl(const struct SpiceChannel * channel, const void * buffer, const ssize_t size)
{ {
if (!channel->connected) if (!channel->connected)
{ {
@ -785,45 +954,60 @@ ssize_t spice_write(const struct SpiceChannel * channel, const void * buffer, co
// ============================================================================ // ============================================================================
bool spice_write_msg(struct SpiceChannel * channel, uint32_t type, const void * buffer, const ssize_t size) inline bool spice_write_msg(struct SpiceChannel * channel, uint32_t type, const void * buffer, const ssize_t size)
{ {
bool result;
LG_LOCK(channel->lock); LG_LOCK(channel->lock);
SpiceDataHeader header; result = spice_write_msg_nl(channel, type, buffer, size);
header.serial = channel->serial++;
LG_UNLOCK(channel->lock);
return result;
}
// ============================================================================
bool spice_write_msg_nl(struct SpiceChannel * channel, uint32_t type, const void * buffer, const ssize_t size)
{
if (!channel->ready)
{
DEBUG_ERROR("channel not ready");
return false;
}
DEBUG_PROTO("write: t=%u, b=%ld", type, size);
SpiceMiniDataHeader header;
++channel->serial;
header.type = type; header.type = type;
header.size = size; header.size = size;
header.sub_list = 0;
if (spice_write(channel, &header, sizeof(header)) != sizeof(header)) if (spice_write_nl(channel, &header, sizeof(header)) != sizeof(header))
{ {
DEBUG_ERROR("failed to write message header"); DEBUG_ERROR("failed to write message header");
LG_UNLOCK(channel->lock);
return false; return false;
} }
if (buffer && size) if (buffer && size)
{ {
if (spice_write(channel, buffer, size) != size) if (spice_write_nl(channel, buffer, size) != size)
{ {
DEBUG_ERROR("failed to write message body"); DEBUG_ERROR("failed to write message body");
LG_UNLOCK(channel->lock);
return false; return false;
} }
} }
LG_UNLOCK(channel->lock);
return true; return true;
} }
// ============================================================================ // ============================================================================
bool spice_read(const struct SpiceChannel * channel, void * buffer, const ssize_t size) bool spice_read_nl(const struct SpiceChannel * channel, void * buffer, const ssize_t size)
{ {
if (!channel->connected) if (!channel->connected)
{ {
DEBUG_ERROR("not connected"); DEBUG_ERROR("not connected");
return false; return -1;
} }
if (!buffer) if (!buffer)
@ -844,17 +1028,25 @@ bool spice_read(const struct SpiceChannel * channel, void * buffer, const ssize_
// ============================================================================ // ============================================================================
bool spice_discard(const struct SpiceChannel * channel, ssize_t size) bool spice_discard_nl(const struct SpiceChannel * channel, ssize_t size)
{ {
while(size) void *c = malloc(8192);
ssize_t left = size;
while(left)
{ {
char c[8192]; size_t len = read(channel->socket, c, left > 8192 ? 8192 : left);
size_t len = read(channel->socket, c, size > sizeof(c) ? sizeof(c) : size);
if (len <= 0) if (len <= 0)
{
if (len == 0)
DEBUG_ERROR("remote end closed connection after %ld byte(s)", size - left);
free(c);
return false; return false;
size -= len;
} }
left -= len;
}
free(c);
return true; return true;
} }

View File

@ -24,6 +24,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
bool spice_connect(const char * host, const unsigned short port, const char * password); bool spice_connect(const char * host, const unsigned short port, const char * password);
void spice_disconnect(); void spice_disconnect();
bool spice_process(); bool spice_process();
bool spice_agent_process();
bool spice_ready(); bool spice_ready();
bool spice_key_down (uint32_t code); bool spice_key_down (uint32_t code);