Commit 1069e52a authored by Liang Guo's avatar Liang Guo

Imported Upstream version 0.6.3

parents
spice-vdagent
spice-vdagentd
*.o
*~
This diff is collapsed.
spice-agent-0.6.3
-----------------
* Initial release, starting with version nr 0.6.3, to indicate that it
more or less supports all parts of the cdagent protocol in spice-protocol
and spice 0.6.3
VERSION = 0.6.3
DESTDIR ?=
sbindir ?= /sbin
bindir ?= /usr/bin
initdir ?= /etc/rc.d/init.d
xdgautostartdir ?= /etc/xdg/autostart
gdmautostartdir ?= /usr/share/gdm/autostart/LoginWindow
socketdir ?= /var/run/spice-vdagentd
CFLAGS ?= -O2 -g -Wall
CPPFLAGS = $(shell pkg-config --cflags spice-protocol)
CPPFLAGS += $(shell pkg-config --cflags dbus-1)
CPPFLAGS += -D_GNU_SOURCE
TARGETS := spice-vdagentd spice-vdagent
build: $(TARGETS)
install: build
install -d $(DESTDIR)$(bindir)
install -d $(DESTDIR)$(sbindir)
install -d $(DESTDIR)$(socketdir)
install -p -m 755 spice-vdagent $(DESTDIR)$(bindir)
install -p -m 755 spice-vdagentd $(DESTDIR)$(sbindir)
install -d $(DESTDIR)$(initdir)
install -p -m 755 spice-vdagentd.sh $(DESTDIR)$(initdir)/spice-vdagentd
install -d $(DESTDIR)$(xdgautostartdir)
install -d $(DESTDIR)$(gdmautostartdir)
desktop-file-install --dir=$(DESTDIR)$(xdgautostartdir) \
spice-vdagent.desktop
desktop-file-install --dir=$(DESTDIR)$(gdmautostartdir) \
spice-vdagent.desktop
clean:
rm -f $(TARGETS) *.o *~
spice-vdagentd: vdagentd.o vdagentd-uinput.o udscs.o vdagent-virtio-port.o console-kit.o
$(CC) -o $@ $^ $(shell pkg-config --libs dbus-1)
spice-vdagent: vdagent.o vdagent-x11.o udscs.o
$(CC) -o $@ $^ -lX11 -lXrandr -lXfixes
tag:
@git tag -a -m "Tag as spice-vdagent-$(VERSION)" spice-vdagent-$(VERSION)
@echo "Tagged as spice-vdagent-$(VERSION)"
archive-no-tag:
@git archive --format=tar --prefix=spice-vdagent-$(VERSION)/ spice-vdagent-$(VERSION) > spice-vdagent-$(VERSION).tar
@bzip2 -f spice-vdagent-$(VERSION).tar
archive: clean tag archive-no-tag
Spice agent for Linux
=====================
The spice agent for Linux consists of 2 parts, a daemon spice-vdagentd and
a per X-session process spice-vdagent. The daemon gets automatically started
in Spice guests through a udev rule. The per X-session gets automatically
started in desktop environments which honor /etc/xdg/autostart, and under
gdm.
The main daemon needs to know which X-session daemon is the currently
active X-session (think switch user functionality) for this console kit is
used. spice-vdagent will not work without console kit.
Features:
* Client mouse mode (no need to grab mouse by client, no mouse lag)
this is handled by the daemon by feeding mouse events into the kernel
via uinput. This will only work if the active X-session is running a
spice-vdagent process so that its resolution can be determined.
* Automatic adjustment of the X-session resolution to the client resolution
* Support of copy and paste (text and images) between the active X-session
and the client
Not handled: multiple displays.
Enjoy,
Gerd & Hans
--
Gerd Hoffmann <kraxel@redhat.com>
Hans de Goede <hdegoede@redhat.com>
-patch xorg driver to allow adding "random" resolutions
-make vdagent xorg client try to add "random" resolutions
Maybe:
-Add support for selections with a target of STRING, which are
ISO Latin-1 strings, which we could support through iconv
This diff is collapsed.
/* console-kit.h vdagentd ConsoleKit integration code - header
Copyright 2010 Red Hat, Inc.
Red Hat Authors:
Hans de Goede <hdegoede@redhat.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CONSOLE_KIT_H
#define __CONSOLE_KIT_H
#include <stdio.h>
#include <stdint.h>
struct console_kit;
struct console_kit *console_kit_create(FILE *errfile);
void console_kit_destroy(struct console_kit *ck);
int console_kit_get_fd(struct console_kit *ck);
const char *console_kit_get_active_session(struct console_kit *ck);
/* Note result must be free()-ed by caller */
char *console_kit_session_for_pid(struct console_kit *ck, uint32_t pid);
#endif
[Desktop Entry]
Name=Spice vdagent
Comment=Agent for Spice guests
Exec=/usr/bin/spice-vdagent
Terminal=false
Type=Application
Categories=
X-GNOME-Autostart-Phase=Initialization
X-GNOME-AutoRestart=true
#!/bin/sh
#
# spice-vdagentd Agent daemon for Spice guests
#
# chkconfig: 345 70 30
# description: Together with a per X-session agent process the spice agent \
# daemon enhances the spice guest user experience with client \
# mouse mode, guest <-> client copy and paste support and more.
### BEGIN INIT INFO
# Provides: spice-vdagentd
# Required-Start: $local_fs messagebus
# Required-Stop: $local_fs messagebus
# Should-Start: $local_fs messagebus
# Should-Stop: $local_fs messagebus
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Agent daemon for Spice guests
# Description: Together with a per X-session agent process the spice agent
# daemon enhances the spice guest user experience with client
# mouse mode, guest <-> client copy and paste support and more.
### END INIT INFO
# Source function library.
. /etc/rc.d/init.d/functions
exec="/sbin/spice-vdagentd"
prog="spice-vdagentd"
port="/dev/virtio-ports/com.redhat.spice.0"
[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
lockfile=/var/lock/subsys/$prog
start() {
[ -x $exec ] || exit 5
[ -c $port ] || exit 6
# In case the previous running vdagentd crashed
rm -f /var/run/spice-vdagentd/spice-vdagent-sock
echo -n $"Starting $prog: "
daemon $exec $SPICE_VDAGENTD_EXTRA_ARGS
retval=$?
echo
[ $retval -eq 0 ] && touch $lockfile
return $retval
}
stop() {
echo -n $"Stopping $prog: "
killproc $prog
retval=$?
echo
[ $retval -eq 0 ] && rm -f $lockfile
return $retval
}
restart() {
stop
start
}
reload() {
restart
}
force_reload() {
restart
}
rh_status() {
# run checks to determine if the service is running or use generic status
status $prog
}
rh_status_q() {
rh_status >/dev/null 2>&1
}
case "$1" in
start)
rh_status_q && exit 0
$1
;;
stop)
rh_status_q || exit 0
$1
;;
restart)
$1
;;
reload)
rh_status_q || exit 7
$1
;;
force-reload)
force_reload
;;
status)
rh_status
;;
condrestart|try-restart)
rh_status_q || exit 0
restart
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
exit 2
esac
exit $?
This diff is collapsed.
/* udscs.h Unix Domain Socket Client Server framework header file
Copyright 2010 Red Hat, Inc.
Red Hat Authors:
Hans de Goede <hdegoede@redhat.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __UDSCS_H
#define __UDSCS_H
#include <stdio.h>
#include <stdint.h>
#include <sys/select.h>
#include <sys/socket.h>
struct udscs_connection;
struct udscs_server;
struct udscs_message_header {
uint32_t type;
uint32_t opaque;
uint32_t size;
};
/* Callbacks with this type will be called when a new connection to a
server is accepted. */
typedef void (*udscs_connect_callback)(struct udscs_connection *conn);
/* Callbacks with this type will be called when a complete message has been
received. The callback may call udscs_destroy_connection, in which case
*connp must be made NULL (which udscs_destroy_connection takes care of) */
typedef void (*udscs_read_callback)(struct udscs_connection **connp,
struct udscs_message_header *header, const uint8_t *data);
/* Callback type for udscs_server_for_all_clients. Clients can be disconnected
from this callback just like with a read callback. */
typedef int (*udscs_for_all_clients_callback)(struct udscs_connection **connp,
void *priv);
/* Callbacks with this type will be called when the connection is disconnected.
Note:
1) udscs will destroy the connection in question itself after
this callback has completed!
2) This callback is always called, even if the disconnect is initiated
by the udscs user through returning -1 from a read callback, or
by explictly calling udscs_destroy_connection */
typedef void (*udscs_disconnect_callback)(struct udscs_connection *conn);
/* Create a unix domain socket named name and start listening on it. */
struct udscs_server *udscs_create_server(const char *socketname,
udscs_connect_callback connect_callback,
udscs_read_callback read_callback,
udscs_disconnect_callback disconnect_callback,
const char * const type_to_string[], int no_types,
FILE *logfile, FILE *errfile);
void udscs_destroy_server(struct udscs_server *server);
/* Connect to a unix domain socket named name. */
struct udscs_connection *udscs_connect(const char *socketname,
udscs_read_callback read_callback,
udscs_disconnect_callback disconnect_callback,
const char * const type_to_string[], int no_types,
FILE *logfile, FILE *errfile);
/* The contents of connp will be made NULL */
void udscs_destroy_connection(struct udscs_connection **connp);
/* Given an udscs server or client fill the fd_sets pointed to by readfds and
writefds for select() usage.
Return value: value of the highest fd + 1 */
int udscs_server_fill_fds(struct udscs_server *server, fd_set *readfds,
fd_set *writefds);
int udscs_client_fill_fds(struct udscs_connection *conn, fd_set *readfds,
fd_set *writefds);
/* Handle any events flagged by select for the given udscs server or client. */
void udscs_server_handle_fds(struct udscs_server *server, fd_set *readfds,
fd_set *writefds);
/* Note the connection may be destroyed (when disconnected) by this call
in this case the disconnect calllback will get called before the
destruction and the contents of connp will be made NULL */
void udscs_client_handle_fds(struct udscs_connection **connp, fd_set *readfds,
fd_set *writefds);
/* Queue a message for delivery to the client connected through conn.
Returns 0 on success -1 on error (only happens when malloc fails) */
int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t opaque,
const uint8_t *data, uint32_t size);
/* Like udscs_write, but then send the message to all clients connected to
the server */
int udscs_server_write_all(struct udscs_server *server,
uint32_t type, uint32_t opaque,
const uint8_t *data, uint32_t size);
/* Call func for all clients connected to the server, passing through
priv to all func calls. Returns the total of the return values from all
calls to func */
int udscs_server_for_all_clients(struct udscs_server *server,
udscs_for_all_clients_callback func, void *priv);
struct ucred udscs_get_peer_cred(struct udscs_connection *conn);
/* For server use, to associate per connection data with a connection */
void udscs_set_user_data(struct udscs_connection *conn, void *data);
void *udscs_get_user_data(struct udscs_connection *conn);
#endif
/* vdagent-virtio-port.c virtio port communication code
Copyright 2010 Red Hat, Inc.
Red Hat Authors:
Hans de Goede <hdegoede@redhat.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#include "vdagent-virtio-port.h"
struct vdagent_virtio_port_buf {
uint8_t *buf;
size_t pos;
size_t size;
struct vdagent_virtio_port_buf *next;
};
struct vdagent_virtio_port {
int fd;
FILE *errfile;
/* Read stuff, single buffer, separate header and data buffer */
int chunk_header_read;
int chunk_data_pos;
int message_header_read;
int message_data_pos;
VDIChunkHeader chunk_header;
VDAgentMessage message_header;
uint8_t chunk_data[VD_AGENT_MAX_DATA_SIZE];
uint8_t *message_data;
/* Writes are stored in a linked list of buffers, with both the header
+ data for a single message in 1 buffer. */
struct vdagent_virtio_port_buf *write_buf;
/* Callbacks */
vdagent_virtio_port_read_callback read_callback;
vdagent_virtio_port_disconnect_callback disconnect_callback;
};
static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **portp);
static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **portp);
struct vdagent_virtio_port *vdagent_virtio_port_create(const char *portname,
vdagent_virtio_port_read_callback read_callback,
vdagent_virtio_port_disconnect_callback disconnect_callback,
FILE *errfile)
{
struct vdagent_virtio_port *port;
port = calloc(1, sizeof(*port));
if (!port)
return 0;
port->errfile = errfile;
port->fd = open(portname, O_RDWR);
if (port->fd == -1) {
fprintf(port->errfile, "open %s: %s\n", portname, strerror(errno));
free(port);
return NULL;
}
port->read_callback = read_callback;
port->disconnect_callback = disconnect_callback;
return port;
}
void vdagent_virtio_port_destroy(struct vdagent_virtio_port **portp)
{
struct vdagent_virtio_port_buf *wbuf, *next_wbuf;
struct vdagent_virtio_port *port = *portp;
if (!port)
return;
if (port->disconnect_callback)
port->disconnect_callback(port);
wbuf = port->write_buf;
while (wbuf) {
next_wbuf = wbuf->next;
free(wbuf->buf);
free(wbuf);
wbuf = next_wbuf;
}
free(port->message_data);
close(port->fd);
free(port);
*portp = NULL;
}
int vdagent_virtio_port_fill_fds(struct vdagent_virtio_port *port,
fd_set *readfds, fd_set *writefds)
{
if (!port)
return -1;
FD_SET(port->fd, readfds);
if (port->write_buf)
FD_SET(port->fd, writefds);
return port->fd + 1;
}
void vdagent_virtio_port_handle_fds(struct vdagent_virtio_port **portp,
fd_set *readfds, fd_set *writefds)
{
if (!*portp)
return;
if (FD_ISSET((*portp)->fd, readfds))
vdagent_virtio_port_do_read(portp);
if (*portp && FD_ISSET((*portp)->fd, writefds))
vdagent_virtio_port_do_write(portp);
}
int vdagent_virtio_port_write(
struct vdagent_virtio_port *port,
uint32_t port_nr,
uint32_t message_type,
uint32_t message_opaque,
const uint8_t *data,
uint32_t data_size)
{
struct vdagent_virtio_port_buf *wbuf, *new_wbuf;
VDIChunkHeader chunk_header;
VDAgentMessage message_header;
new_wbuf = malloc(sizeof(*new_wbuf));
if (!new_wbuf)
return -1;
new_wbuf->pos = 0;
new_wbuf->size = sizeof(chunk_header) + sizeof(message_header) + data_size;
new_wbuf->next = NULL;
new_wbuf->buf = malloc(new_wbuf->size);
if (!new_wbuf->buf) {
free(new_wbuf);
return -1;
}
chunk_header.port = port_nr;
chunk_header.size = sizeof(message_header) + data_size;
message_header.protocol = VD_AGENT_PROTOCOL;
message_header.type = message_type;
message_header.opaque = message_opaque;
message_header.size = data_size;
memcpy(new_wbuf->buf, &chunk_header, sizeof(chunk_header));
memcpy(new_wbuf->buf + sizeof(chunk_header), &message_header,
sizeof(message_header));
memcpy(new_wbuf->buf + sizeof(chunk_header) + sizeof(message_header),
data, data_size);
if (!port->write_buf) {
port->write_buf = new_wbuf;
return 0;
}
/* maybe we should limit the write_buf stack depth ? */
wbuf = port->write_buf;
while (wbuf->next)
wbuf = wbuf->next;
wbuf->next = new_wbuf;
return 0;
}
void vdagent_virtio_port_flush(struct vdagent_virtio_port **portp)
{
while (*portp && (*portp)->write_buf)
vdagent_virtio_port_do_write(portp);
}
static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **portp)
{
int avail, read, pos = 0;
struct vdagent_virtio_port *port = *portp;
if (port->message_header_read < sizeof(port->message_header)) {
read = sizeof(port->message_header) - port->message_header_read;
memcpy((uint8_t *)&port->message_header + port->message_header_read,
port->chunk_data, read);
port->message_header_read += read;
if (port->message_header_read == sizeof(port->message_header) &&
port->message_header.size) {
port->message_data = malloc(port->message_header.size);
if (!port->message_data) {
fprintf(port->errfile, "out of memory, disconnecting virtio\n");
vdagent_virtio_port_destroy(portp);
return;
}
}
pos = read;
}
if (port->message_header_read == sizeof(port->message_header)) {
read = port->message_header.size - port->message_data_pos;
avail = port->chunk_header.size - pos;
if (avail > read) {
fprintf(port->errfile, "chunk larger then message, lost sync?\n");
vdagent_virtio_port_destroy(portp);
return;
}
if (avail < read)
read = avail;
if (read) {
memcpy(port->message_data + port->message_data_pos,
port->chunk_data + pos, read);
port->message_data_pos += read;
}
if (port->message_data_pos == port->message_header.size) {
if (port->read_callback) {
int r = port->read_callback(port, &port->chunk_header,
&port->message_header, port->message_data);
if (r == -1) {
vdagent_virtio_port_destroy(portp);
return;
}
}
port->message_header_read = 0;
port->message_data_pos = 0;
free(port->message_data);
port->message_data = NULL;
}
}
}
static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **portp)
{
ssize_t n;
size_t to_read;
uint8_t *dest;
struct vdagent_virtio_port *port = *portp;
if (port->chunk_header_read < sizeof(port->chunk_header)) {
to_read = sizeof(port->chunk_header) - port->chunk_header_read;
dest = (uint8_t *)&port->chunk_header + port->chunk_header_read;
} else {
to_read = port->chunk_header.size - port->chunk_data_pos;
dest = port->chunk_data + port->chunk_data_pos;
}
n = read(port->fd, dest, to_read);
if (n < 0) {
if (errno == EINTR)
return;
fprintf(port->errfile, "reading from vdagent virtio port: %s\n",
strerror(errno));
}
if (n <= 0) {
vdagent_virtio_port_destroy(portp);
return;
}
if (port->chunk_header_read < sizeof(port->chunk_header)) {
port->chunk_header_read += n;
if (port->chunk_header_read == sizeof(port->chunk_header)) {
if (port->chunk_header.size > VD_AGENT_MAX_DATA_SIZE) {
fprintf(port->errfile, "chunk size too large\n");
vdagent_virtio_port_destroy(portp);
return;
}
}
} else {
port->chunk_data_pos += n;
if (port->chunk_data_pos == port->chunk_header.size) {
vdagent_virtio_port_do_chunk(portp);
port->chunk_header_read = 0;
port->chunk_data_pos = 0;
}
}
}
static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **portp)
{
ssize_t n;
size_t to_write;
struct vdagent_virtio_port *port = *portp;
struct vdagent_virtio_port_buf* wbuf = port->write_buf;
if (!wbuf) {
fprintf(port->errfile,
"do_write called on a port without a write buf ?!\n");
return;
}
to_write = wbuf->size - wbuf->pos;
n = write(port->fd, wbuf->buf + wbuf->pos, to_write);
if (n < 0) {
if (errno == EINTR)
return;
fprintf(port->errfile, "writing to vdagent virtio port: %s\n",
strerror(errno));
vdagent_virtio_port_destroy(portp);
return;
}
wbuf->pos += n;
if (wbuf->pos == wbuf->size) {
port->write_buf = wbuf->next;
free(wbuf->buf);
free(wbuf);
}
}
/* vdagent-virtio-port.h virtio port communication header
Copyright 2010 Red Hat, Inc.
Red Hat Authors:
Hans de Goede <hdegoede@redhat.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __VIRTIO_PORT_H
#define __VIRTIO_PORT_H