Agent Protocol

!!UPDATE!!

For certain features to work SPICE requires that the guest os is running the SPICE agent (vdagent). This document describes how vdagent communicates with the SPICE server and client.

vdagent virtio serial port

All vdagent communications on the guest side run over a single pipe which gets presented to the guest os as a virtio serial port. Under Windows this virtio serial port has the following name:

\\\\.\\Global\\com.redhat.spice.0

Under Linux this virtio serial port has the following name:

/dev/virtio-ports/com.redhat.spice.0

Qemu will enable the virtio serial port when using the following params:

-device virtio-serial-pci,id=virtio-serial0,max_ports=16,bus=pci.0,addr=0x5 -chardev spicevmc,name=vdagent,id=vdagent -device virtserialport,nr=1,bus=virtio-serial0.0,chardev=vdagent,name=com.redhat.spice.0

vdagent data chunks / ports

The vdagent is connect through the virtio serial port (vdagent virtio port) with the SPICE server but it can receive messages from / send messages to both the SPICE server and the SPICE client. To make this possible all messages on the vdagent virtio port are prefixed with a VDIChunkHeader:

typedef struct SPICE_ATTR_PACKED VDIChunkHeader {
  uint32_t port;
  uint32_t size;
} VDIChunkHeader;

Where size is the size of the VDAgentMessage being send / received including the variable data part, IOW it is sizeof(VDAgentMessage) + variable_data_len, and port is one of:

enum {
    VDP_CLIENT_PORT = 1,
    VDP_SERVER_PORT,
};

When the vdagent receives messages port indicates where the message came from. When sending messages port indicates the intended receiver. When a message is a reply / ack of a received message port should have the same value as it had in the received message the message being send is a reply to.

When the SPICE server receives a message it removes the chunk header and then depending on the port in the chunk header, forwards it to the client, handles it itself, or if the port is not a valid value logs an error and drops the message.

Note currently there are no messages from the agent which are meant for the server so all messages sent by the agent with a port of VDP_SERVER_PORT get dropped silently.

vdagent message struct

Messages sent / received by the agent are encapsulated in the VDAgentMessage struct:

typedef struct SPICE_ATTR_PACKED VDAgentMessage {
  uint32_t protocol;
  uint32_t type;
  uint64_t opaque;
  uint32_t size;
  uint8_t data[0];
} VDAgentMessage;

Where protocol is always VD_AGENT_PROTOCOL.

Type is a value from the following enum:

enum {
  VD_AGENT_MOUSE_STATE = 1,
  VD_AGENT_MONITORS_CONFIG,
  VD_AGENT_REPLY,
  VD_AGENT_CLIPBOARD,
  VD_AGENT_DISPLAY_CONFIG,
  VD_AGENT_ANNOUNCE_CAPABILITIES,
  VD_AGENT_END_MESSAGE,
};

Opaque is a place holder for message types which only need to pass a single integer as message data, for message types which have more data it is always set to 0.

Size is the size of the variable length data. Note that the size of the complete message in the virtio port is sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + variable_data_len.

Data is the start of the variable length data, the contents of this depends on the message types, for most messages it is a message type specific struct such as VDAgentMouseState. Note that data is declared as a 0 sized array meaning that it does not take up any size in the struct, it is simply there to be able to easily determine the start address of the data.

vdagent messages

VD_AGENT_MOUSE_STATE

SPICE supports two mouse modes, server and client.

In server mode the the QEMU PS/2 mouse emulation is used for sending mouse state to the guest. Upon user click inside the SPICE client window, the client mouse is captured and the client sends mouse moves as delta coordinates.

In client mode mouse coordinates are sent as absolute values to the guest, this requires using either USB table emulation, or sending them to the vdagent which will them notify the guest os of the mouse position (and of button clicks).

The VD_AGENT_MOUSE_STATE message contains mouse state updates sent by the SPICE server when the mouse is in client mode. The variable data of these messages consists of the following structure:

typedef struct SPICE_ATTR_PACKED VDAgentMouseState {
  uint32_t x;
  uint32_t y;
  uint32_t buttons;
  uint8_t display_id;
} VDAgentMouseState;

Note these messages are sent by the SPICE server, not by the SPICE client as the server does all the mouse handling (like switching between client and server mode as the vdagent connects / disconnects).

VD_AGENT_MONITORS_CONFIG

This message gets sent by the client to the agent when the client is run in fullscreen auto configuration mode. This message contains information about the monitors attached to the client machine. Upon receiving this message the agent should reconfigure the output(s) of the qxl vga devices in the guest to match those in the message in as far as possible. For example if the client has more outputs then there are configured in the vm the outputs which are present should be configured to match what is in the message.

The variable data of these messages consists of the following structure:

typedef struct SPICE_ATTR_PACKED VDAgentMonitorsConfig {
  uint32_t num_of_monitors;
  uint32_t flags;
  VDAgentMonConfig monitors[0];
} VDAgentMonitorsConfig;

Followed by num_of_monitors times the following structure:

typedef struct SPICE_ATTR_PACKED VDAgentMonConfig {
  uint32_t height;
  uint32_t width;
  uint32_t depth;
  int32_t x;
  int32_t y;
} VDAgentMonConfig;

When the agent is done configuring the outputs it should send back in VD_AGENT_REPLY message with a type set to VD_AGENT_MONITORS_CONFIG and error set to VD_AGENT_SUCCESS or VD_AGENT_ERROR to indicate success resp. error in configuring the outputs.

VD_AGENT_REPLY

This message gets sent by the vdagent to the SPICE client to signal it is done handling a VD_AGENT_MONITORS_CONFIG or VD_AGENT_DISPLAY_CONFIG, and if it succeeded or not:

typedef struct SPICE_ATTR_PACKED VDAgentReply {
  uint32_t type;
  uint32_t error;
} VDAgentReply;

enum {
  VD_AGENT_SUCCESS = 1,
  VD_AGENT_ERROR,
};

Clipboard

SPICE allows sharing clipboard data between the SPICE client host, and the guest via its agent.

To do so, the guest agent and the client play a symmetric role: they can both claim ownership (GRAB), RELEASE ownership, REQUEST clipboard data and send CLIPBOARD data. For example, the GRAB message is sent after receiving a system notification of clipboard data available after a Copy operation in some application. When the clipboard is emptied, the grab must be RELEASEd. The other side can REQUEST the data while the GRAB is active, and should expect a CLIPBOARD reply with the data.

The data is described with a type. The SPICE clipboard type are rather self-descriptive:

enum {
  VD_AGENT_CLIPBOARD_NONE = 0,
  VD_AGENT_CLIPBOARD_UTF8_TEXT,
  VD_AGENT_CLIPBOARD_IMAGE_PNG,  /* All clients with image support should support this one */
  VD_AGENT_CLIPBOARD_IMAGE_BMP,  /* optional */
  VD_AGENT_CLIPBOARD_IMAGE_TIFF, /* optional */
  VD_AGENT_CLIPBOARD_IMAGE_JPG,  /* optional */
};

If both side implement VD_AGENT_CAP_CLIPBOARD_SELECTION capability, clipboard messages are prepended with a uint8_t indicating which clipboard selection to operate. Please refer to VD_AGENT_CAP_CLIPBOARD_SELECTION documentation.

VD_AGENT_CLIPBOARD

struct VDAgentClipboard {
if VD_AGENT_CAP_CLIPBOARD_SELECTION capability
  uint8_t selection;
  uint8_t __reserved[3];
endif
  uint32_t type;
  uint8_t data[0];
};

The VD_AGENT_CLIPBOARD is used to send clipboard data. The data shouldn't be sent unless it was requested with VD_AGENT_CLIPBOARD_REQUEST before to avoid wasting bandwidth. It is quite common that the clipboard data to be transfered is large. In that case, it is to be expected that the message is split across several VD_AGENT_MESSAGE.

VD_AGENT_CLIPBOARD_REQUEST

struct VDAgentClipboardRequest {
if VD_AGENT_CAP_CLIPBOARD_SELECTION capability
  uint8_t selection;
  uint8_t __reserved[3];
endif
  uint32_t type;
};

Request clipboard data with the specified type.

VD_AGENT_CLIPBOARD_GRAB

struct VDAgentClipboardGrab {
if VD_AGENT_CAP_CLIPBOARD_SELECTION capability
  uint8_t selection;
  uint8_t __reserved[3];
endif
  uint32_t types[0];
};

Grab clipboard. The REQUEST of data should succeed with any of the GRAB types.

VD_AGENT_CLIPBOARD_RELEASE

struct VDAgentClipboardRelease {
if VD_AGENT_CAP_CLIPBOARD_SELECTION capability
  uint8_t selection;
  uint8_t __reserved[3];
endif
};

Release the clipboard (example: when clipboard becomes empty).

Important:

If a GRAB message has been sent and is currently active, then a successive GRAB message is received from the peer, no RELEASE message should be sent to the peer for the previous active grab. It has been implicitly released by the peer. Sending an extra RELEASE message would only confuse the peer.

VD_AGENT_DISPLAY_CONFIG

This message gets sent by the SPICE client to the vdagent to notify it of any special performance related settings. The client can ask the vdagent to disable various features of the guest os like font anti aliasing to improve performance. vdagent should do a best effort here, esp. as most settings are rather Windows centric and should return a success status using VD_AGENT_REPLY unless something really went wrong.

typedef struct SPICE_ATTR_PACKED VDAgentDisplayConfig {
  uint32_t flags;
  uint32_t depth;
} VDAgentDisplayConfig;

enum {
  VD_AGENT_DISPLAY_CONFIG_FLAG_DISABLE_WALLPAPER = (1 << 0),
  VD_AGENT_DISPLAY_CONFIG_FLAG_DISABLE_FONT_SMOOTH = (1 << 1),
  VD_AGENT_DISPLAY_CONFIG_FLAG_DISABLE_ANIMATION = (1 << 2),
  VD_AGENT_DISPLAY_CONFIG_FLAG_SET_COLOR_DEPTH = (1 << 3),
};

VD_AGENT_ANNOUNCE_CAPABILITIES

This message can (and should) be send by both the client and the vdagent, it announces which messages / capabilities (one capability can encompass multiple messages) the sending side may send and/or knows how to handle when received. The purpose of this message is to allow different client and vdagent versions to work together:

typedef struct SPICE_ATTR_PACKED VDAgentAnnounceCapabilities {
  uint32_t  request;
  uint32_t caps[0];
} VDAgentAnnounceCapabilities;

The request field is a boolean which indicates if the receiver of the message should send back an VD_AGENT_ANNOUNCE_CAPABILITIES message as the sender wants to know its capabilities too. It should be true when initiating a capabilities exchange, and set to false when sending an announce capabilities as a reply to a received one.

The caps member of the struct is the beginning of a variable length array holding the capabilities bit, the length of this array can be determined using the VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE macro on the VDAgentMessage msg size member. The indexes for the different capabilities are in an enum defining VD_AGENT_CAP constants and there are VD_AGENT_HAS_CAPABILITY and VD_AGENT_SET_CAPABILITY macros to test / set the capability bits in the array.

One should never send messages with a type associated with a capability which is not announced as supported by the other side. Older versions do not support announce capabilities, so until an announce capabilities message has been received the following capabilities (which are supported by all versions) should be assumed:

VD_AGENT_CAP_MOUSE_STATE
VD_AGENT_CAP_MONITORS_CONFIG
VD_AGENT_CAP_REPLY

VD_AGENT_CAP_CLIPBOARD_SELECTION

When both client and servers have the selection capability, the VDAgentClipboard* messages MUST be prepended with a uint8_t indicating which clipboard selection to operate + 3 bytes stuffing for alignment that could be used for future capabilities or extensions.

A few clipboard selection are defined according to X11/Gtk scheme:

  • VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD: the default clipboard, implemented by most OS to deal with explicit Copy/Paste operations.
  • VD_AGENT_CLIPBOARD_SELECTION_PRIMARY: the PRIMARY clipboard, used for mouse selections.
  • VD_AGENT_CLIPBOARD_SELECTION_SECONDARY: the SECONDARY clipboard.