[Spice-commits] 6 commits - configure.ac server/Makefile.am server/reds.c server/reds.h server/reds-private.h server/reds_websockets.c server/reds_websockets.h server/red_worker.c server/spice.h server/spice-server.syms server/tests spice-common
Alon Levy
alon at kemper.freedesktop.org
Sun Nov 4 03:40:57 PST 2012
configure.ac | 20 ++
server/Makefile.am | 9 +
server/red_worker.c | 129 ++++++++++++----
server/reds-private.h | 47 +++++
server/reds.c | 79 ++++++---
server/reds.h | 17 ++
server/reds_websockets.c | 310 +++++++++++++++++++++++++++++++++++++++
server/reds_websockets.h | 9 +
server/spice-server.syms | 5
server/spice.h | 9 +
server/tests/test_display_base.c | 4
spice-common | 2
12 files changed, 571 insertions(+), 69 deletions(-)
New commits:
commit b7bdd28343b1b9d7132bcfdeef923cd7612f7b46
Author: Alon Levy <alevy at redhat.com>
Date: Sun Nov 4 13:40:48 2012 +0200
update spice-common (was broken)
diff --git a/spice-common b/spice-common
index 20a39e0..be6392a 160000
--- a/spice-common
+++ b/spice-common
@@ -1 +1 @@
-Subproject commit 20a39e0c1fcd61c07264cef7974b774d1afea8b6
+Subproject commit be6392a5ad338e3f55a279adafaa30bc64e03e4a
commit 4ca54e596f81ae8ea914b83d3a6bd9df55cd777f
Author: Alon Levy <alevy at redhat.com>
Date: Thu Nov 1 12:30:25 2012 +0200
server/red_worker: don't call set_client_capabilities if vm is stopped
We try to inject an interrupt to the vm in this case, which we cannot do
if it is stopped. Instead log this and update when vm restarts.
RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=870972
(that bz is on qemu, it will be cloned or just changed, not
sure yet)
diff --git a/server/red_worker.c b/server/red_worker.c
index ec1c284..9edd5d4 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -997,6 +997,7 @@ typedef struct RedWorker {
#endif
int driver_has_monitors_config;
+ int set_client_capabilities_pending;
} RedWorker;
typedef enum {
@@ -10327,6 +10328,48 @@ static void display_channel_create(RedWorker *worker, int migrate)
stat_compress_init(&display_channel->jpeg_alpha_stat, jpeg_alpha_stat_name);
}
+static void guest_set_client_capabilities(RedWorker *worker)
+{
+ int i;
+ DisplayChannelClient *dcc;
+ RedChannelClient *rcc;
+ RingItem *link;
+ uint8_t caps[58] = { 0 };
+ int caps_available[] = {
+ SPICE_DISPLAY_CAP_SIZED_STREAM,
+ SPICE_DISPLAY_CAP_MONITORS_CONFIG,
+ SPICE_DISPLAY_CAP_COMPOSITE,
+ SPICE_DISPLAY_CAP_A8_SURFACE,
+ };
+
+#define SET_CAP(a,c) \
+ ((a)[(c) / 8] |= (1 << ((c) % 8)))
+
+#define CLEAR_CAP(a,c) \
+ ((a)[(c) / 8] &= ~(1 << ((c) % 8)))
+
+ if (!worker->running) {
+ worker->set_client_capabilities_pending = 1;
+ return;
+ }
+ if (worker->display_channel->common.base.clients_num == 0) {
+ worker->qxl->st->qif->set_client_capabilities(worker->qxl, FALSE, caps);
+ } else {
+ // Take least common denominator
+ for (i = 0 ; i < sizeof(caps_available) / sizeof(caps_available[0]); ++i) {
+ SET_CAP(caps, caps_available[i]);
+ }
+ DCC_FOREACH(link, dcc, &worker->display_channel->common.base) {
+ rcc = (RedChannelClient *)dcc;
+ for (i = 0 ; i < sizeof(caps_available) / sizeof(caps_available[0]); ++i) {
+ if (!red_channel_client_test_remote_cap(rcc, caps_available[i]))
+ CLEAR_CAP(caps, caps_available[i]);
+ }
+ }
+ worker->qxl->st->qif->set_client_capabilities(worker->qxl, TRUE, caps);
+ }
+ worker->set_client_capabilities_pending = 0;
+}
static void handle_new_display_channel(RedWorker *worker, RedClient *client, RedsStream *stream,
int migrate,
@@ -10384,22 +10427,7 @@ static void handle_new_display_channel(RedWorker *worker, RedClient *client, Red
if (worker->qxl->st->qif->base.major_version == 3 &&
worker->qxl->st->qif->base.minor_version >= 2 &&
worker->qxl->st->qif->set_client_capabilities) {
- RedChannelClient *rcc = (RedChannelClient *)dcc;
- uint8_t caps[58] = { 0 };
-
-#define SET_CAP(a,c) \
- ((a)[(c) / 8] |= (1 << ((c) % 8)))
-
- if (red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_SIZED_STREAM))
- SET_CAP(caps, SPICE_DISPLAY_CAP_SIZED_STREAM);
- if (red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_MONITORS_CONFIG))
- SET_CAP(caps, SPICE_DISPLAY_CAP_MONITORS_CONFIG);
- if (red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_COMPOSITE))
- SET_CAP(caps, SPICE_DISPLAY_CAP_COMPOSITE);
- if (red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_A8_SURFACE))
- SET_CAP(caps, SPICE_DISPLAY_CAP_A8_SURFACE);
-
- worker->qxl->st->qif->set_client_capabilities(worker->qxl, TRUE, caps);
+ guest_set_client_capabilities(worker);
}
// todo: tune level according to bandwidth
@@ -11146,6 +11174,7 @@ void handle_dev_start(void *opaque, void *payload)
worker->display_channel->common.during_target_migrate = FALSE;
}
worker->running = TRUE;
+ guest_set_client_capabilities(worker);
}
void handle_dev_wakeup(void *opaque, void *payload)
@@ -11267,8 +11296,7 @@ void handle_dev_display_disconnect(void *opaque, void *payload)
if (worker->qxl->st->qif->base.major_version == 3 &&
worker->qxl->st->qif->base.minor_version >= 2 &&
worker->qxl->st->qif->set_client_capabilities) {
- uint8_t caps[58] = { 0 };
- worker->qxl->st->qif->set_client_capabilities(worker->qxl, FALSE, caps);
+ guest_set_client_capabilities(worker);
}
red_channel_client_disconnect(rcc);
commit 3f71ed962f4433056c849c85f3395f922159489c
Author: Alon Levy <alevy at redhat.com>
Date: Thu Oct 25 12:31:34 2012 +0200
server/red_worker: wip: VALIDATE_SURFACE macros, remove asserts (but too late - should be done earlier)
diff --git a/server/red_worker.c b/server/red_worker.c
index 31d1341..ec1c284 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -128,6 +128,29 @@
typedef int64_t red_time_t;
+#define VALIDATE_SURFACE_RET(worker, surface_id) \
+ if (!validate_surface(worker, surface_id)) { \
+ rendering_incorrect(__func__); \
+ return; \
+ }
+
+#define VALIDATE_SURFACE_RETVAL(worker, surface_id, ret) \
+ if (!validate_surface(worker, surface_id)) { \
+ rendering_incorrect(__func__); \
+ return ret; \
+ }
+
+#define VALIDATE_SURFACE_BREAK(worker, surface_id) \
+ if (!validate_surface(worker, surface_id)) { \
+ rendering_incorrect(__func__); \
+ break; \
+ }
+
+static void rendering_incorrect(const char *msg)
+{
+ spice_warning("rendering incorrect from now on: %s", msg);
+}
+
static inline red_time_t timespec_to_red_time(struct timespec *time)
{
return time->tv_sec * (1000 * 1000 * 1000) + time->tv_nsec;
@@ -1231,7 +1254,7 @@ static inline void __validate_surface(RedWorker *worker, uint32_t surface_id)
spice_warn_if(surface_id >= worker->n_surfaces);
}
-static inline void validate_surface(RedWorker *worker, uint32_t surface_id)
+static inline int validate_surface(RedWorker *worker, uint32_t surface_id)
{
spice_warn_if(surface_id >= worker->n_surfaces);
if (!worker->surfaces[surface_id].context.canvas) {
@@ -1239,7 +1262,9 @@ static inline void validate_surface(RedWorker *worker, uint32_t surface_id)
&(worker->surfaces[surface_id].context.canvas), surface_id);
spice_warning("failed on %d", surface_id);
spice_warn_if(!worker->surfaces[surface_id].context.canvas);
+ return 0;
}
+ return 1;
}
static const char *draw_type_to_str(uint8_t type)
@@ -3782,7 +3807,8 @@ static void free_one_drawable(RedWorker *worker, int force_glz_free)
}
static Drawable *get_drawable(RedWorker *worker, uint8_t effect, RedDrawable *red_drawable,
- uint32_t group_id) {
+ uint32_t group_id)
+{
Drawable *drawable;
struct timespec time;
int x;
@@ -3812,11 +3838,11 @@ static Drawable *get_drawable(RedWorker *worker, uint8_t effect, RedDrawable *re
drawable->group_id = group_id;
drawable->surface_id = red_drawable->surface_id;
- validate_surface(worker, drawable->surface_id);
+ VALIDATE_SURFACE_RETVAL(worker, drawable->surface_id, NULL)
for (x = 0; x < 3; ++x) {
drawable->surfaces_dest[x] = red_drawable->surfaces_dest[x];
if (drawable->surfaces_dest[x] != -1) {
- validate_surface(worker, drawable->surfaces_dest[x]);
+ VALIDATE_SURFACE_RETVAL(worker, drawable->surfaces_dest[x], NULL)
}
}
ring_init(&drawable->pipes);
@@ -3903,7 +3929,10 @@ static inline void red_process_drawable(RedWorker *worker, RedDrawable *drawable
int surface_id;
Drawable *item = get_drawable(worker, drawable->effect, drawable, group_id);
- spice_assert(item);
+ if (!item) {
+ rendering_incorrect("failed to get_drawable");
+ return;
+ }
surface_id = item->surface_id;
@@ -4022,7 +4051,7 @@ static SpiceCanvas *image_surfaces_get(SpiceImageSurfaces *surfaces,
RedWorker *worker;
worker = SPICE_CONTAINEROF(surfaces, RedWorker, image_surfaces);
- validate_surface(worker, surface_id);
+ VALIDATE_SURFACE_RETVAL(worker, surface_id, NULL);
return worker->surfaces[surface_id].context.canvas;
}
@@ -4913,7 +4942,10 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *
&update, ext_cmd.cmd.data)) {
break;
}
- validate_surface(worker, update.surface_id);
+ if (!validate_surface(worker, update.surface_id)) {
+ rendering_incorrect("QXL_CMD_UPDATE");
+ break;
+ }
red_update_area(worker, &update.area, update.surface_id);
worker->qxl->st->qif->notify_update(worker->qxl, update.update_id);
release_info_ext.group_id = ext_cmd.group_id;
@@ -6509,7 +6541,10 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m,
RedSurface *surface;
surface_id = simage->u.surface.surface_id;
- validate_surface(worker, surface_id);
+ if (!validate_surface(worker, surface_id)) {
+ rendering_incorrect("SPICE_IMAGE_TYPE_SURFACE");
+ return FILL_BITS_TYPE_SURFACE;
+ }
surface = &worker->surfaces[surface_id];
image.descriptor.type = SPICE_IMAGE_TYPE_SURFACE;
@@ -6668,7 +6703,7 @@ static int is_surface_area_lossy(DisplayChannelClient *dcc, uint32_t surface_id,
QRegion lossy_region;
RedWorker *worker = dcc->common.worker;
- validate_surface(worker, surface_id);
+ VALIDATE_SURFACE_RETVAL(worker, surface_id, FALSE);
surface = &worker->surfaces[surface_id];
surface_lossy_region = &dcc->surface_client_lossy_region[surface_id];
@@ -10671,7 +10706,7 @@ void handle_dev_update_async(void *opaque, void *payload)
spice_assert(worker->running);
- validate_surface(worker, surface_id);
+ VALIDATE_SURFACE_RET(worker, surface_id);
red_update_area(worker, &rect, surface_id);
if (!worker->qxl->st->qif->update_area_complete) {
return;
@@ -10707,8 +10742,11 @@ void handle_dev_update(void *opaque, void *payload)
spice_assert(worker->running);
- validate_surface(worker, surface_id);
- red_update_area(worker, rect, surface_id);
+ if (validate_surface(worker, surface_id)) {
+ red_update_area(worker, rect, surface_id);
+ } else {
+ rendering_incorrect(__func__);
+ }
free(rect);
surface_dirty_region_to_rects(surface, qxl_dirty_rects, num_dirty_rects,
commit 65b6c56cf8a474e692feb82f5f47fe20082b2be1
Author: Alon Levy <alevy at redhat.com>
Date: Fri Oct 19 13:15:56 2012 +0200
release 0.12.1
diff --git a/configure.ac b/configure.ac
index 06c8678..5a6d136 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,9 +13,9 @@ AC_PREREQ([2.57])
# 4. Follow the libtool manual for the so version:
# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
-m4_define([SPICE_CURRENT], [6])
+m4_define([SPICE_CURRENT], [7])
m4_define([SPICE_REVISION], [0])
-m4_define([SPICE_AGE], [5])
+m4_define([SPICE_AGE], [6])
# Note on the library name on linux (SONAME) produced by libtool (for reference, gleaned
# from looking at libtool 2.4.2)
diff --git a/server/spice.h b/server/spice.h
index 7ca5033..f42c4a8 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -23,7 +23,7 @@
#include <spice/qxl_dev.h>
#include <spice/vd_agent.h>
-#define SPICE_SERVER_VERSION 0x000c00 /* release 0.12.0 */
+#define SPICE_SERVER_VERSION 0x000c01 /* release 0.12.1 */
/* interface base type */
commit 63bb37276e028ab1b1c156c9e7907bf22b6d5952
Author: Alon Levy <alevy at redhat.com>
Date: Fri Oct 19 11:27:57 2012 +0200
server: add websockets support via libwebsockets
New API: spice_server_set_ws_ports
This adds an optional dependency on libwebsockets. You need to get my
patched 0.0.3 version here:
git://people.freedesktop.org/~alon/libwebsockets
There is no qemu patches yet, to test change in reds.c the default value
of spice_ws_port to 5959 (for the default of spice-html5).
For testing there is an online client at
http://spice-space.org/spice-html5/spice.html
Known issues:
1. The tester (server/tests/test_display_no_ssl) gets into dropping all
data after a few seconds, I think it's an issue with the implemented
watches, but haven't figured it out.
2. libwebsocket's read interface is inverted to what our code expects,
i.e. there is no libwebsocket_read, so there is an additional copy
involved (see RedsWebSocket). This can be fixed.
3. Listening on a separate port. Since the headers are different, we
could listen on the same port (first three bytes RED/GET). I don't know
if we want to?
Todos:
1. SSL not implemented yet. Needs some thought as to how.
2. Serve spice-html5 when accessed as a http server. Nice to have.
diff --git a/configure.ac b/configure.ac
index 83d4c95..06c8678 100644
--- a/configure.ac
+++ b/configure.ac
@@ -160,6 +160,14 @@ AC_ARG_ENABLE(automated_tests,
AS_IF([test x"$enable_automated_tests" != "xno"], [enable_automated_tests="yes"])
AM_CONDITIONAL(SUPPORT_AUTOMATED_TESTS, test "x$enable_automated_tests" != "xno")
+AC_ARG_ENABLE(libwebsockets,
+[ --enable-libwebsockets Enable websockets server support (no need for proxy)],,
+[enable_libwebsockets="no"])
+AS_IF([test x"enable_libwebsockets" != "xyes"], [enable_websockets="no"])
+AM_CONDITIONAL(SUPPORT_LIBWEBSOCKETS, test "x$enable_websockets" = "xyes")
+if test "x$enable_libwebsockets" = "xyes"; then
+ AC_DEFINE([USE_LIBWEBSOCKETS], [1], [Define if supporting websocket connections])
+fi
dnl =========================================================================
dnl Check deps
@@ -237,6 +245,12 @@ if test "x$enable_smartcard" = "xyes"; then
SPICE_REQUIRES+=" libcacard >= 0.1.2"
fi
+if test "x$enable_libwebsockets" = "xyes"; then
+ PKG_CHECK_MODULES(LIBWEBSOCKETS, libwebsockets >= 0.0.3)
+ AC_SUBST(LIBWEBSOCKETS_LIBS)
+ AC_SUBST(LIBWEBSOCKETS_CFLAGS)
+ SPICE_REQUIRES+=" libwebsockets >= 0.0.3"
+fi
PKG_CHECK_MODULES(PIXMAN, pixman-1 >= 0.17.7)
AC_SUBST(PIXMAN_CFLAGS)
@@ -536,6 +550,8 @@ echo "
SASL support: ${enable_sasl}
Automated tests: ${enable_automated_tests}
+
+ libwebsockets: ${enable_libwebsockets}
"
if test $os_win32 == "yes" ; then
diff --git a/server/Makefile.am b/server/Makefile.am
index b62d98c..4c01c31 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -10,6 +10,7 @@ AM_CPPFLAGS = \
$(SLIRP_CFLAGS) \
$(SMARTCARD_CFLAGS) \
$(SSL_CFLAGS) \
+ $(LIBWEBSOCKETS_CFLAGS) \
$(VISIBILITY_HIDDEN_CFLAGS) \
$(WARN_CFLAGS) \
$(NULL)
@@ -38,6 +39,7 @@ libspice_server_la_LIBADD = \
$(SLIRP_LIBS) \
$(SSL_LIBS) \
$(Z_LIBS) \
+ $(LIBWEBSOCKETS_LIBS) \
$(NULL)
libspice_server_la_SOURCES = \
@@ -93,6 +95,13 @@ libspice_server_la_SOURCES = \
zlib_encoder.h \
$(NULL)
+if SUPPORT_WEBSOCKETS
+libspice_server_la_SOURCES += \
+ reds_websockets.c \
+ reds_websockets.h \
+ $(NULL)
+endif
+
if SUPPORT_TUNNEL
libspice_server_la_SOURCES += \
red_tunnel_worker.c \
diff --git a/server/reds-private.h b/server/reds-private.h
index 3db6565..a5903b3 100644
--- a/server/reds-private.h
+++ b/server/reds-private.h
@@ -4,6 +4,16 @@
#include <time.h>
#include <spice/protocol.h>
+#include <spice/stats.h>
+
+#if USE_LIBWEBSOCKETS
+#include <libwebsockets.h>
+#endif
+
+#include "reds.h"
+#include "char_device.h"
+#include "agent-msg-filter.h"
+#include "main_channel.h"
#define MIGRATE_TIMEOUT (1000 * 10) /* 10sec */
#define MM_TIMER_GRANULARITY_MS (1000 / 30)
@@ -34,10 +44,6 @@ typedef struct VDIReadBuf {
uint8_t data[SPICE_AGENT_MAX_DATA_SIZE];
} VDIReadBuf;
-static VDIReadBuf *vdi_port_read_buf_get(void);
-static VDIReadBuf *vdi_port_read_buf_ref(VDIReadBuf *buf);
-static void vdi_port_read_buf_unref(VDIReadBuf *buf);
-
enum {
VDI_PORT_READ_STATE_READ_HEADER,
VDI_PORT_READ_STATE_GET_BUFF,
@@ -125,9 +131,19 @@ typedef struct RedsClientMonitorsConfig {
int buffer_pos;
} RedsClientMonitorsConfig;
+#ifdef USE_LIBWEBSOCKETS
+#define REDS_MAX_WEBSOCKETS 32
+#endif
+
typedef struct RedsState {
int listen_socket;
int secure_listen_socket;
+#ifdef USE_LIBWEBSOCKETS
+ struct libwebsocket_context *ws_context;
+ RedsWebSocket ws[REDS_MAX_WEBSOCKETS];
+ int ws_in_service_fd;
+ int ws_count;
+#endif
SpiceWatch *listen_watch;
SpiceWatch *secure_listen_watch;
VDIPortState agent_state;
@@ -179,4 +195,27 @@ typedef struct RedsState {
RedsClientMonitorsConfig client_monitors_config;
} RedsState;
+typedef struct AsyncRead {
+ RedsStream *stream;
+ void *opaque;
+ uint8_t *now;
+ uint8_t *end;
+ void (*done)(void *opaque);
+ void (*error)(void *opaque, int err);
+} AsyncRead;
+
+typedef struct RedLinkInfo {
+ RedsStream *stream;
+ AsyncRead asyc_read;
+ SpiceLinkHeader link_header;
+ SpiceLinkMess *link_mess;
+ int mess_pos;
+ TicketInfo tiTicketing;
+ SpiceLinkAuthMechanism auth_mechanism;
+ int skip_auth;
+} RedLinkInfo;
+
+RedLinkInfo *spice_server_add_client_create_link(SpiceServer *s, int socket, int skip_auth);
+void reds_handle_new_link(RedLinkInfo *link);
+
#endif
diff --git a/server/reds.c b/server/reds.c
index 98c8706..bd16764 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -74,9 +74,16 @@
#ifdef USE_SMARTCARD
#include "smartcard.h"
#endif
+#if USE_LIBWEBSOCKETS
+#include "reds_websockets.h"
+#endif
#include "reds-private.h"
+static VDIReadBuf *vdi_port_read_buf_get(void);
+static VDIReadBuf *vdi_port_read_buf_ref(VDIReadBuf *buf);
+static void vdi_port_read_buf_unref(VDIReadBuf *buf);
+
SpiceCoreInterface *core = NULL;
static SpiceCharDeviceInstance *vdagent = NULL;
static SpiceMigrateInstance *migration_interface = NULL;
@@ -99,6 +106,10 @@ static TicketAuthentication taTicket;
static int spice_port = -1;
static int spice_secure_port = -1;
+#if USE_LIBWEBSOCKETS
+static int spice_ws_port = -1;
+static int spice_wss_port = -1;
+#endif
static int spice_listen_socket_fd = -1;
static char spice_addr[256];
static int spice_family = PF_UNSPEC;
@@ -127,26 +138,6 @@ static bool exit_on_disconnect = FALSE;
static RedsState *reds = NULL;
-typedef struct AsyncRead {
- RedsStream *stream;
- void *opaque;
- uint8_t *now;
- uint8_t *end;
- void (*done)(void *opaque);
- void (*error)(void *opaque, int err);
-} AsyncRead;
-
-typedef struct RedLinkInfo {
- RedsStream *stream;
- AsyncRead asyc_read;
- SpiceLinkHeader link_header;
- SpiceLinkMess *link_mess;
- int mess_pos;
- TicketInfo tiTicketing;
- SpiceLinkAuthMechanism auth_mechanism;
- int skip_auth;
-} RedLinkInfo;
-
typedef struct RedSSLParameters {
char keyfile_password[256];
char certs_file[256];
@@ -2718,7 +2709,7 @@ static void reds_handle_read_header_done(void *opaque)
async_read_handler(0, 0, &link->asyc_read);
}
-static void reds_handle_new_link(RedLinkInfo *link)
+void reds_handle_new_link(RedLinkInfo *link)
{
AsyncRead *obj = &link->asyc_read;
obj->opaque = link;
@@ -2882,7 +2873,6 @@ static void reds_accept_ssl_connection(int fd, int event, void *data)
}
}
-
static void reds_accept(int fd, int event, void *data)
{
int socket;
@@ -2896,25 +2886,33 @@ static void reds_accept(int fd, int event, void *data)
close(socket);
}
-
-SPICE_GNUC_VISIBLE int spice_server_add_client(SpiceServer *s, int socket, int skip_auth)
+RedLinkInfo *spice_server_add_client_create_link(SpiceServer *s, int socket, int skip_auth)
{
RedLinkInfo *link;
- RedsStream *stream;
spice_assert(reds == s);
if (!(link = reds_init_client_connection(socket))) {
spice_warning("accept failed");
- return -1;
+ return NULL;
}
link->skip_auth = skip_auth;
+ return link;
+}
+
+SPICE_GNUC_VISIBLE int spice_server_add_client(SpiceServer *s, int socket, int skip_auth)
+{
+ RedLinkInfo *link;
+ RedsStream *stream;
+ link = spice_server_add_client_create_link(s, socket, skip_auth);
+ if (!link) {
+ return -1;
+ }
stream = link->stream;
stream->read = stream_read_cb;
stream->write = stream_write_cb;
stream->writev = stream_writev_cb;
-
reds_handle_new_link(link);
return 0;
}
@@ -3033,6 +3031,12 @@ static int reds_init_net(void)
return -1;
}
}
+
+#if USE_LIBWEBSOCKETS
+ if (spice_ws_port != -1 || spice_wss_port != -1) {
+ reds_init_websocket(reds, spice_addr, spice_ws_port, spice_wss_port);
+ }
+#endif
return 0;
}
@@ -3949,6 +3953,27 @@ SPICE_GNUC_VISIBLE int spice_server_set_port(SpiceServer *s, int port)
return 0;
}
+
+SPICE_GNUC_VISIBLE int spice_server_set_ws_ports(SpiceServer *s, int ws_port, int wss_port)
+{
+#if USE_LIBWEBSOCKETS
+ spice_assert(reds == s);
+ if ((ws_port < 1 || ws_port > 0xffff) && (wss_port < 1 || wss_port > 0xffff)) {
+ return -1;
+ }
+ if (ws_port > 0 && ws_port < 0xffff) {
+ spice_ws_port = ws_port;
+ }
+ if (wss_port > 0 && wss_port < 0xffff) {
+ spice_wss_port = wss_port;
+ }
+ return 0;
+#else
+ fprintf(stderr, "libwebsockets is unsupported in this spice build\n");
+ return -1;
+#endif
+}
+
SPICE_GNUC_VISIBLE void spice_server_set_addr(SpiceServer *s, const char *addr, int flags)
{
spice_assert(reds == s);
diff --git a/server/reds.h b/server/reds.h
index f8e8d56..0d4f933 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -65,6 +65,20 @@ typedef struct RedsSASL {
} RedsSASL;
#endif
+#ifdef USE_LIBWEBSOCKETS
+typedef struct RedsWebSocket {
+ struct libwebsocket_context *context;
+ struct libwebsocket *wsi;
+ SpiceWatch *watch;
+ int fd;
+ unsigned events;
+ /* buffer of available data to read, always starts at offset 0 to data_avail - 1. */
+ unsigned char *data;
+ unsigned data_len;
+ unsigned data_avail;
+} RedsWebSocket;
+#endif
+
struct RedsStream {
int socket;
SpiceWatch *watch;
@@ -73,6 +87,9 @@ struct RedsStream {
receive may return data afterward. check the flag before calling receive*/
int shutdown;
SSL *ssl;
+#ifdef USE_LIBWEBSOCKETS
+ RedsWebSocket *ws;
+#endif
#if HAVE_SASL
RedsSASL sasl;
diff --git a/server/reds_websockets.c b/server/reds_websockets.c
new file mode 100644
index 0000000..affface
--- /dev/null
+++ b/server/reds_websockets.c
@@ -0,0 +1,310 @@
+#include "config.h"
+
+#include <unistd.h>
+#include <errno.h>
+#include <libwebsockets.h>
+
+#include "spice.h"
+#include "reds.h"
+#include "reds-private.h"
+#include "reds_websockets.h"
+
+static ssize_t stream_write_ws_cb(RedsStream *s, const void *buf, size_t size)
+{
+ /* TODO: better way to handle the requirement of libwebsocket, perhaps
+ * we should make a writev version for libwebsocket. Assuming writev doesn't
+ * cause a linearlizing copy itself. */
+ ssize_t ret;
+ unsigned char *padded_buf = spice_malloc(size + LWS_SEND_BUFFER_PRE_PADDING +
+ LWS_SEND_BUFFER_POST_PADDING);
+ spice_assert(s && s->ws);
+ memcpy(padded_buf + LWS_SEND_BUFFER_PRE_PADDING, buf, size);
+ ret = libwebsocket_write(s->ws->wsi, &padded_buf[LWS_SEND_BUFFER_PRE_PADDING], size,
+ LWS_WRITE_BINARY);
+ free(padded_buf);
+ return ret == 0 ? size : -1; /* XXX exact bytes required? if not this is
+ good enough, else need to change
+ libwebsocket */
+}
+
+static void reds_websocket_append_data(RedsWebSocket *ws, unsigned char *buf,
+ size_t size)
+{
+ if (!ws->data) {
+ ws->data = spice_malloc(size);
+ ws->data_len = size;
+ ws->data_avail = 0;
+ }
+ if (ws->data_len < size + ws->data_avail) {
+ ws->data_len = size + ws->data_avail;
+ ws->data = spice_realloc(ws->data, ws->data_len);
+ }
+ memcpy(ws->data + ws->data_avail, buf, size);
+ ws->data_avail += size;
+}
+
+static ssize_t reds_websocket_read_data(RedsWebSocket *ws, unsigned char *buf,
+ size_t size)
+{
+ ssize_t ret;
+
+ ret = ws->data_avail > size ? size : ws->data_avail;
+ if (ret > 0) {
+ memcpy(buf, ws->data, ret);
+ }
+ if (ret > 0 && ret < ws->data_avail) {
+ memmove(ws->data, ws->data + ret, ws->data_avail - ret);
+ }
+ ws->data_avail -= ret;
+ if (ws->data_avail == 0 && ret == size) {
+ free(ws->data);
+ ws->data = NULL;
+ ws->data_len = ws->data_avail = 0;
+ }
+ return ret;
+}
+
+static int reds_libwebsocket_service_fd(RedsState *s, struct pollfd *pfd)
+{
+ int ret;
+ if (s->ws_in_service_fd) {
+ return 0;
+ }
+ s->ws_in_service_fd = 1;
+ ret = libwebsocket_service_fd(s->ws_context, pfd);
+ s->ws_in_service_fd = 0;
+ if (ret != 0) {
+ if (errno == EAGAIN) {
+ spice_debug("libwebsocket_servide_fd EAGAIN, pfd->revents = %d",
+ pfd->revents);
+ return 0;
+ }
+ /* since read is the last systemcall, errno should be set correctly */
+ spice_debug("libwebsocket_service_fd errored; (%d) %s",
+ errno, sys_errlist[errno]);
+ return -1;
+ }
+ return 0;
+}
+
+static ssize_t stream_read_ws_cb(RedsStream *s, void *buf, size_t size)
+{
+ RedsWebSocket *ws;
+ struct pollfd pfd;
+ RedsState *reds_state;
+
+ /* TODO: perhaps change libwebsocket to allow a socket like read. Then
+ * we can avoid the whole RedsWebSocket->data{,_len,_avail}. */
+ spice_assert(s && s->ws);
+ ws = s->ws;
+ reds_state = libwebsocket_context_user(ws->context);
+ if (size == 0) {
+ return 0;
+ }
+ spice_debug("%p %d / %d", ws->data, ws->data_avail, ws->data_len);
+ if (ws->data_avail < size && !reds_state->ws_in_service_fd) {
+ pfd.fd = ws->fd;
+ pfd.events = ws->events;
+ pfd.revents = POLLIN;
+ if (reds_libwebsocket_service_fd(reds_state, &pfd)) {
+ return -1;
+ }
+ }
+ if (ws->data_avail == 0) {
+ errno = EAGAIN; /* force a reset of the watch on the fd, so that
+ libwebsocket_service_fd has a chance to run */
+ return -1;
+ }
+ return reds_websocket_read_data(ws, buf, size);
+}
+
+static RedsWebSocket *reds_ws_from_fd(RedsState *s, int fd)
+{
+ int i;
+
+ for (i = 0 ; i < s->ws_count ; ++i) {
+ if (s->ws[i].fd == fd) {
+ return &s->ws[i];
+ }
+ }
+ spice_error("%s: no match for %d (%d ws sockets)\n", __func__,
+ fd, s->ws_count);
+ return NULL;
+}
+
+static int callback_http(struct libwebsocket_context *context,
+ struct libwebsocket *wsi,
+ void *user, void *in, size_t len)
+{
+ const char *message = "TODO: serve spice-html5";
+ char buf[512];
+ int n;
+
+ n = snprintf(buf, sizeof(buf),
+ "HTTP/1.0 200 OK\x0d\x0a"
+ "Server: spice\x0d\x0a"
+ "Content-Type: text/html\x0d\x0a"
+ "Content-Length: %zu\x0d\x0a"
+ "\x0d\x0a"
+ "%s", strlen(message), message);
+ libwebsocket_write(wsi, (unsigned char *)buf, n, LWS_WRITE_HTTP);
+ return 0;
+}
+
+static int spice_server_add_ws_client(SpiceServer *s, int socket, int skip_auth,
+ RedsWebSocket *ws)
+{
+ RedLinkInfo *link;
+ RedsStream *stream;
+
+ link = spice_server_add_client_create_link(s, socket, skip_auth);
+ if (!link) {
+ return -1;
+ }
+ stream = link->stream;
+ stream->read = stream_read_ws_cb;
+ stream->write = stream_write_ws_cb;
+ stream->writev = NULL; /* falls back to write iteration */
+ stream->ws = ws;
+ reds_handle_new_link(link);
+ return 0;
+}
+
+static void watch_ws(int fd, int event, void *data)
+{
+ struct libwebsocket_context *context = data;
+ RedsState *s = libwebsocket_context_user(context);
+ struct pollfd pfd = {
+ .fd = fd,
+ .events = reds_ws_from_fd(s, fd)->events,
+ .revents = (event & SPICE_WATCH_EVENT_READ ? POLLIN : 0) |
+ (event & SPICE_WATCH_EVENT_WRITE ? POLLOUT : 0)
+ };
+
+ reds_libwebsocket_service_fd(s, &pfd);
+}
+
+static int callback_ws(struct libwebsocket_context *context,
+ struct libwebsocket *wsi,
+ enum libwebsocket_callback_reasons reason, void *user,
+ void *in, size_t len)
+{
+ int fd;
+ RedsState *s = libwebsocket_context_user(context);
+ int n;
+ RedsWebSocket *ws;
+ int events;
+
+ spice_debug("%s: reason %d user %lu len %zd \n", __func__, reason,
+ (unsigned long)user, len);
+ switch (reason) {
+ case LWS_CALLBACK_HTTP:
+ return callback_http(context, wsi, user, in, len);
+
+ case LWS_CALLBACK_ADD_POLL_FD:
+ if (s->ws_count >= REDS_MAX_WEBSOCKETS) {
+ spice_warning("exceeded websockets maximum watches");
+ return 1; /* close connection */
+ }
+ spice_debug("adding ws for fd %d", (int)(long)user);
+ events = (int)(long)len;
+ ws = &s->ws[s->ws_count];
+ ws->watch = core->watch_add((int)(long)user,
+ (events & POLLIN ? SPICE_WATCH_EVENT_READ: 0) |
+ (events & POLLOUT ? SPICE_WATCH_EVENT_WRITE : 0),
+ watch_ws, (void *)context);
+ ws->fd = (int)(long)user;
+ ws->events = events;
+ s->ws_count++;
+ break;
+
+ case LWS_CALLBACK_DEL_POLL_FD:
+ spice_debug("removing ws for fd %d", (int)(long)user);
+ for (n = 0; n < s->ws_count; n++) {
+ if (s->ws[n].fd == (int)(long)user) {
+ s->ws[n] = s->ws[s->ws_count - 1];
+ }
+ s->ws_count--;
+ }
+ break;
+
+ case LWS_CALLBACK_SET_MODE_POLL_FD:
+ reds_ws_from_fd(s, (int)(long)user)->events |= (int)(long)len;
+ break;
+
+ case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
+ reds_ws_from_fd(s, (int)(long)user)->events &= (int)(long)len;
+ break;
+
+ case LWS_CALLBACK_ESTABLISHED:
+ fd = libwebsocket_get_socket_fd(wsi);
+ ws = reds_ws_from_fd(s, fd);
+ *(RedsWebSocket **)user = ws;
+ ws->wsi = wsi;
+ ws->context = context;
+ ws->data_avail = 0;
+ spice_debug("LWS_CALLBACK_ESTABLISHED\n");
+ spice_server_add_ws_client(s, fd, 0, ws);
+ break;
+
+ case LWS_CALLBACK_RECEIVE:
+ spice_debug("LWS_CALLBACK_CLIENT_RECEIVE\n");
+ spice_assert(user != NULL);
+ ws = *(RedsWebSocket **)user;
+ spice_assert(ws != NULL);
+ reds_websocket_append_data(ws, in, len);
+ break;
+
+ case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
+ case LWS_CALLBACK_CLIENT_ESTABLISHED:
+ case LWS_CALLBACK_CLOSED:
+ case LWS_CALLBACK_CLIENT_RECEIVE:
+ case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
+ case LWS_CALLBACK_CLIENT_WRITEABLE:
+ case LWS_CALLBACK_SERVER_WRITEABLE:
+ case LWS_CALLBACK_BROADCAST:
+ case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
+ case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
+ case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
+ case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS:
+ case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
+ case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
+ case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
+ case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
+ break;
+ }
+ return 0;
+}
+
+static struct libwebsocket_protocols ws_protocols[] = {
+ /* first protocol must always be HTTP handler */
+
+ {
+ "binary", /* name - based on spice-html5 :) */
+ callback_ws, /* callback */
+ sizeof(void*), /* per_session_data_size */
+ /* below initializing library used values to avoid warning */
+ NULL,
+ 0,
+ 0,
+ 0
+ },
+ {
+ NULL, NULL, 0, NULL, 0, 0, 0 /* End of list */
+ }
+};
+
+void reds_init_websocket(RedsState *s, const char *addr,
+ int ws_port, int wss_port)
+{
+ if (ws_port != -1) {
+ s->ws_context = libwebsocket_create_context(ws_port,
+ strlen(addr) ? addr : NULL,
+ ws_protocols, libwebsocket_internal_extensions,
+ NULL /*cert_path*/, NULL /*key_path*/, -1, -1, 0 /*opts*/,
+ s);
+ }
+ if (wss_port != -1) {
+ spice_error("TODO: secure websocket not supported");
+ }
+}
diff --git a/server/reds_websockets.h b/server/reds_websockets.h
new file mode 100644
index 0000000..ce0a975
--- /dev/null
+++ b/server/reds_websockets.h
@@ -0,0 +1,9 @@
+#ifndef REDS_WEBSOCKETS_H
+#define REDS_WEBSOCKETS_H
+
+#include "reds-private.h"
+
+void reds_init_websocket(RedsState *s, const char *addr,
+ int ws_port, int wss_port);
+
+#endif
diff --git a/server/spice-server.syms b/server/spice-server.syms
index eadfed8..aef8f5a 100644
--- a/server/spice-server.syms
+++ b/server/spice-server.syms
@@ -130,3 +130,8 @@ SPICE_SERVER_0.11.4 {
global:
spice_server_set_exit_on_disconnect;
} SPICE_SERVER_0.11.2;
+
+SPICE_SERVER_0.12.1 {
+global:
+ spice_server_set_ws_ports;
+} SPICE_SERVER_0.11.4;
diff --git a/server/spice.h b/server/spice.h
index c1478e0..7ca5033 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -434,6 +434,13 @@ void spice_server_destroy(SpiceServer *s);
int spice_server_set_compat_version(SpiceServer *s,
spice_compat_version_t version);
int spice_server_set_port(SpiceServer *s, int port);
+
+/* @ws_port: -1 for don't set, otherwise in [1,65535] port to listen
+ * for unencrypted websocket connections.
+ * @wss_port: -1 for don't set, otherwise in [1,65535] port to listen
+ * for encrypted websocket connections.
+ */
+int spice_server_set_ws_ports(SpiceServer *s, int ws_port, int wss_port);
void spice_server_set_addr(SpiceServer *s, const char *addr, int flags);
int spice_server_set_listen_socket_fd(SpiceServer *s, int listen_fd);
int spice_server_set_exit_on_disconnect(SpiceServer *s, int flag);
diff --git a/server/tests/test_display_base.c b/server/tests/test_display_base.c
index 588e960..9eaa584 100644
--- a/server/tests/test_display_base.c
+++ b/server/tests/test_display_base.c
@@ -701,13 +701,14 @@ static int flush_resources(QXLInstance *qin)
return TRUE;
}
-static void client_monitors_config(QXLInstance *qin, VDAgentMonitorsConfig *monitors_config)
+static int client_monitors_config(QXLInstance *qin, VDAgentMonitorsConfig *monitors_config)
{
if (!monitors_config) {
printf("%s: NULL monitors_config\n", __func__);
} else {
printf("%s: %d\n", __func__, monitors_config->num_of_monitors);
}
+ return 0;
}
QXLInterface display_sif = {
@@ -815,6 +816,7 @@ Test *test_new(SpiceCoreInterface *core)
printf("TESTER: listening on port %d (unsecure)\n", port);
spice_server_set_port(server, port);
spice_server_set_noauth(server);
+ spice_server_set_ws_ports(test->server, 5959, -1);
spice_server_init(server, core);
cursor_init();
commit 2c85436dc6c2e9febecfb84c579e7a50a4a6bd6b
Author: Alon Levy <alevy at redhat.com>
Date: Thu Oct 25 12:28:12 2012 +0200
server/red_worker: stride > 0 is tested, remove abort
Tested using the wip driver and xf86-video-modesetting.
diff --git a/server/red_worker.c b/server/red_worker.c
index 3e78c63..31d1341 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -9270,9 +9270,6 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, ui
RedSurface *surface = &worker->surfaces[surface_id];
uint32_t i;
- if (stride >= 0) {
- spice_critical("Untested path stride >= 0");
- }
spice_warn_if(surface->context.canvas);
surface->context.canvas_draws_on_surface = FALSE;
More information about the Spice-commits
mailing list