From: Marc-André Lureau <marcandre.lureau@xxxxxxxxxx> This channel implements the NBD protocol and the port events defined by Spice protocol. --- doc/reference/spice-gtk-docs.xml | 1 + doc/reference/spice-gtk-sections.txt | 18 ++ doc/reference/spice-gtk.types | 3 +- gtk/Makefile.am | 16 +- gtk/channel-nbd.c | 449 +++++++++++++++++++++++++++++++++++ gtk/channel-nbd.h | 88 +++++++ gtk/map-file | 5 + gtk/spice-channel.c | 6 + gtk/spice-client.h | 1 + gtk/spice-glib-sym-file | 4 + 10 files changed, 588 insertions(+), 3 deletions(-) create mode 100644 gtk/channel-nbd.c create mode 100644 gtk/channel-nbd.h diff --git a/doc/reference/spice-gtk-docs.xml b/doc/reference/spice-gtk-docs.xml index 4a9a3cf..b7cea5a 100644 --- a/doc/reference/spice-gtk-docs.xml +++ b/doc/reference/spice-gtk-docs.xml @@ -37,6 +37,7 @@ <xi:include href="xml/channel-smartcard.xml"/> <xi:include href="xml/channel-usbredir.xml"/> <xi:include href="xml/channel-port.xml"/> + <xi:include href="xml/channel-nbd.xml"/> </chapter> <chapter> diff --git a/doc/reference/spice-gtk-sections.txt b/doc/reference/spice-gtk-sections.txt index 8d61aa9..56cb00b 100644 --- a/doc/reference/spice-gtk-sections.txt +++ b/doc/reference/spice-gtk-sections.txt @@ -429,3 +429,21 @@ SPICE_PORT_CHANNEL_GET_CLASS SpicePortChannelPrivate </SECTION> +<SECTION> +<FILE>channel-nbd</FILE> +<TITLE>SpiceNbdChannel</TITLE> +SpiceNbdChannel +SpiceNbdChannelClass +<SUBSECTION> +<SUBSECTION Standard> +SPICE_NBD_CHANNEL +SPICE_IS_NBD_CHANNEL +SPICE_TYPE_NBD_CHANNEL +spice_nbd_channel_get_type +SPICE_NBD_CHANNEL_CLASS +SPICE_IS_NBD_CHANNEL_CLASS +SPICE_NBD_CHANNEL_GET_CLASS +<SUBSECTION Private> +SpiceNbdChannelPrivate +</SECTION> + diff --git a/doc/reference/spice-gtk.types b/doc/reference/spice-gtk.types index 2f52845..29741fe 100644 --- a/doc/reference/spice-gtk.types +++ b/doc/reference/spice-gtk.types @@ -42,4 +42,5 @@ spice_usbredir_channel_get_type spice_usb_device_get_type spice_usb_device_manager_get_type spice_usb_device_widget_get_type -spice_port_channel_get_type \ No newline at end of file +spice_port_channel_get_type +spice_nbd_channel_get_type \ No newline at end of file diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 7b38d1c..6a800bb 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -76,6 +76,7 @@ SPICE_COMMON_CPPFLAGS = \ -DUSB_IDS=\""$(USB_IDS)"\" \ -DSPICE_DISABLE_ABORT \ -I$(top_srcdir) \ + -I$(srcdir)/nbd \ $(COMMON_CFLAGS) \ $(PIXMAN_CFLAGS) \ $(CELT051_CFLAGS) \ @@ -181,6 +182,7 @@ libspice_client_glib_2_0_la_LDFLAGS = \ libspice_client_glib_2_0_la_LIBADD = \ $(top_builddir)/spice-common/common/libspice-common.la \ $(top_builddir)/spice-common/common/libspice-common-client.la \ + nbd/libnbd.la \ $(GLIB2_LIBS) \ $(GIO_LIBS) \ $(GOBJECT2_LIBS) \ @@ -231,6 +233,7 @@ libspice_client_glib_2_0_la_SOURCES = \ gio-coroutine.h \ \ channel-base.c \ + channel-nbd.c \ channel-cursor.c \ channel-display.c \ channel-display-priv.h \ @@ -449,11 +452,19 @@ spice-marshal.c: spice-marshal.txt spice-marshal.h: spice-marshal.txt $(AM_V_GEN)glib-genmarshal --header $< > $@ || (rm -f $@ && exit 1) -spice-glib-enums.c: spice-channel.h channel-inputs.h spice-session.h +SPICE_GLIB_ENUMS_FILES = \ + channel-nbd.h \ + channel-inputs.h \ + spice-channel.h \ + spice-session.h \ + $(NULL) + +spice-glib-enums.c: $(SPICE_GLIB_ENUMS_FILES) $(AM_V_GEN)glib-mkenums --fhead "#include <glib-object.h>\n" \ --fhead "#include \"spice-glib-enums.h\"\n\n" \ --fprod "\n#include \"spice-session.h\"\n" \ --fprod "\n#include \"spice-channel.h\"\n" \ + --fprod "\n#include \"channel-nbd.h\"\n" \ --fprod "\n#include \"channel-inputs.h\"\n" \ --vhead "static const G@Type@Value _@enum_name@_values[] = {" \ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ @@ -468,7 +479,7 @@ spice-glib-enums.c: spice-channel.h channel-inputs.h spice-session.h --vtail " return type;\n}\n\n" \ $^ > $@ -spice-glib-enums.h: spice-channel.h channel-inputs.h spice-session.h +spice-glib-enums.h: $(SPICE_GLIB_ENUMS_FILES) $(AM_V_GEN)glib-mkenums --fhead "#ifndef SPICE_GLIB_ENUMS_H\n" \ --fhead "#define SPICE_GLIB_ENUMS_H\n\n" \ --fhead "G_BEGIN_DECLS\n\n" \ @@ -591,6 +602,7 @@ glib_introspection_files = \ spice-glib-enums.c \ spice-option.c \ spice-util.c \ + channel-nbd.c \ channel-cursor.c \ channel-display.c \ channel-inputs.c \ diff --git a/gtk/channel-nbd.c b/gtk/channel-nbd.c new file mode 100644 index 0000000..786e9a2 --- /dev/null +++ b/gtk/channel-nbd.c @@ -0,0 +1,449 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2013 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ +#include "spice-client.h" +#include "spice-common.h" +#include "spice-channel-priv.h" +#include "spice-session-priv.h" +#include "spice-marshal.h" +#include "glib-compat.h" +#include "nbd/nbd.h" +#include "vmcstream.h" + +/** + * SECTION:channel-nbd + * @short_description: blockdevice communication channel + * @title: Block device Channel + * @section_id: + * @see_also: #SpiceChannel + * @stability: Stable + * @include: channel-nbd.h + * + * The "nbd" channel exports block device to a Spice server, using the + * Network Block Device protocol. This is mainly useful to redirect + * ISO images to a CD device for example. + * + * As of today, the implementation allows read-only operations. + * + * Since: 0.20 + */ + +#define SPICE_NBD_CHANNEL_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE((obj), SPICE_TYPE_NBD_CHANNEL, SpiceNbdChannelPrivate)) + +struct _SpiceNbdChannelPrivate { + NbdServer *server; + + gboolean pending_update; + gboolean pending_session; + NbdServerSession *session; + + GCancellable *cancellable; + NbdExport *export; + SpiceVmcStream *stream; +}; + +G_DEFINE_TYPE(SpiceNbdChannel, spice_nbd_channel, SPICE_TYPE_PORT_CHANNEL) + +/* Properties */ +enum { + PROP_0, + + PROP_NBD_FILE, + PROP_NBD_OPENED, +}; + +static void spice_nbd_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg); +static void update_nbd_session(SpiceNbdChannel *self); + +static void +session_closed(GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + SpiceNbdChannel *self = user_data; + SpiceNbdChannelPrivate *c = self->priv; + GError *error = NULL; + + CHANNEL_DEBUG(self, "session closed"); + + if (!nbd_server_session_close_finish(c->session, res, &error)) { + g_warning("%s", error->message); + g_clear_error(&error); + } + + g_clear_object(&c->session); + g_object_notify(G_OBJECT(self), "nbd-opened"); + + c->pending_update = FALSE; + update_nbd_session(self); +} + +static void port_event(SpiceNbdChannel *self, gint event) +{ + SpiceNbdChannelPrivate *c = self->priv; + + CHANNEL_DEBUG(self, "port event:%d pending:%d", event, c->pending_update); + if (event != SPICE_PORT_EVENT_OPENED || + !c->pending_update) + return; + + if (c->session) + nbd_server_session_close_async(c->session, + NULL, + session_closed, + self); + else { + c->pending_update = FALSE; + update_nbd_session(self); + } + +} + +static void spice_nbd_channel_init(SpiceNbdChannel *channel) +{ + SpiceNbdChannelPrivate *c = SPICE_NBD_CHANNEL_GET_PRIVATE(channel); + + channel->priv = c; + + c->stream = spice_vmc_stream_new(SPICE_CHANNEL(channel)); + c->server = nbd_server_new(); + c->cancellable = g_cancellable_new(); +} + +/** + * spice_nbd_channel_get_file: + * @channel: a %SpiceNbdChannel + * + * Returns the %GFile that has been set with + * spice_nbd_channel_set_file_async(). + * + * Returns: (transfer none): The associated %GFile or %NULL. + **/ +GFile* spice_nbd_channel_get_file(SpiceNbdChannel *self) +{ + GFile *file = NULL; + + g_return_val_if_fail(SPICE_IS_NBD_CHANNEL(self), NULL); + SpiceNbdChannelPrivate *c = self->priv; + + if (c->export) + file = nbd_export_get_file(c->export); + + return file; +} + +static void spice_nbd_get_property(GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + g_return_if_fail(SPICE_IS_NBD_CHANNEL(object)); + SpiceNbdChannel *self = SPICE_NBD_CHANNEL(object); + SpiceNbdChannelPrivate *c = self->priv; + + switch (prop_id) { + case PROP_NBD_FILE: + g_value_set_object(value, spice_nbd_channel_get_file(self)); + break; + case PROP_NBD_OPENED: + g_value_set_boolean(value, !!c->session); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void migration_state_cb(SpiceSession *session, GParamSpec *pspec, + SpiceNbdChannel *self) +{ + SpiceSessionMigration state = spice_session_get_migration_state(session); + + if (state != SPICE_SESSION_MIGRATION_NONE) + return; + + CHANNEL_DEBUG(self, "migration finished, re-new the nbd session on dest"); + update_nbd_session(self); +} + +static void spice_nbd_channel_constructed(GObject *object) +{ + SpiceChannelPrivate *c = SPICE_CHANNEL(object)->priv; + + spice_g_signal_connect_object(c->session, "notify::migration-state", + G_CALLBACK(migration_state_cb), object, 0); + + if (G_OBJECT_CLASS(spice_nbd_channel_parent_class)->constructed) + G_OBJECT_CLASS(spice_nbd_channel_parent_class)->constructed(object); +} + +static void spice_nbd_channel_finalize(GObject *object) +{ + SpiceNbdChannelPrivate *c = SPICE_NBD_CHANNEL(object)->priv; + + g_clear_object(&c->export); + g_clear_object(&c->server); + g_clear_object(&c->session); + g_clear_object(&c->cancellable); + + G_OBJECT_CLASS(spice_nbd_channel_parent_class)->finalize(object); +} + +static void +session_ready(GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + SpiceNbdChannel *self = user_data; + SpiceNbdChannelPrivate *c = self->priv; + GError *error = NULL; + + g_warn_if_fail(c->session == NULL); + c->session = nbd_server_session_new_finish(res, &error); + CHANNEL_DEBUG(self, "New NBD session %p", c->session); + c->pending_session = FALSE; + + if (!c->session) { + if (error) + g_warning("%s", error->message); + g_clear_error(&error); + } else + g_object_notify(G_OBJECT(self), "nbd-opened"); +} + +static void update_nbd_session(SpiceNbdChannel *self) +{ + SpiceNbdChannelPrivate *c = self->priv; + + if (spice_channel_get_state(SPICE_CHANNEL(self)) != SPICE_CHANNEL_STATE_READY || + !c->export || + c->pending_update || + c->pending_session) + return; + + if (c->session) { + spice_port_event(SPICE_PORT_CHANNEL(self), SPICE_PORT_EVENT_BREAK); + c->pending_update = TRUE; + return; + } + + c->pending_session = TRUE; + nbd_server_session_new(c->server, + G_IO_STREAM(c->stream), c->export, + c->cancellable, session_ready, self); +} + +static void spice_nbd_channel_up(SpiceChannel *channel) +{ + SpiceNbdChannel *self = SPICE_NBD_CHANNEL(channel); + + update_nbd_session(self); +} + +static void spice_nbd_channel_class_init(SpiceNbdChannelClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + SpiceChannelClass *channel_class = SPICE_CHANNEL_CLASS(klass); + + gobject_class->constructed = spice_nbd_channel_constructed; + gobject_class->finalize = spice_nbd_channel_finalize; + gobject_class->get_property = spice_nbd_get_property; + channel_class->handle_msg = spice_nbd_handle_msg; + channel_class->channel_up = spice_nbd_channel_up; + + g_signal_override_class_handler("port-event", + SPICE_TYPE_NBD_CHANNEL, + G_CALLBACK(port_event)); + + g_object_class_install_property + (gobject_class, PROP_NBD_FILE, + g_param_spec_string("nbd-file", + "file", + "Associated block device file", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (gobject_class, PROP_NBD_OPENED, + g_param_spec_boolean("nbd-opened", + "opened", + "Session opened", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_type_class_add_private(klass, sizeof(SpiceNbdChannelPrivate)); +} + +static void +export_ready(GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *error = NULL; + GSimpleAsyncResult *simple = user_data; + SpiceNbdChannel *self = SPICE_NBD_CHANNEL(g_async_result_get_source_object(G_ASYNC_RESULT(simple))); + SpiceNbdChannelPrivate *c = self->priv; + + NbdExport *export = nbd_export_new_finish(res, &error); + CHANNEL_DEBUG(self, "set export %p", export); + if (!export) { + g_simple_async_result_take_error(simple, error); + goto end; + } + + if (c->export) + nbd_server_remove_export(c->server, c->export); + + nbd_server_add_export(c->server, export); + + g_clear_object(&c->export); + c->export = export; + g_object_notify(G_OBJECT(self), "nbd-file"); + + update_nbd_session(self); + g_simple_async_result_set_op_res_gboolean(simple, TRUE); + +end: + g_simple_async_result_complete(simple); + g_object_unref(simple); + g_object_unref(self); +} + +/** + * spice_nbd_set_file_async: + * @channel: a #SpiceNbdChannel + * @file: the block device image file + * @flags: open options + * @cancellable: (allow-none): optional GCancellable object, NULL to ignore + * @callback: (scope async): callback to call when the request is satisfied + * @user_data: (closure): the data to pass to callback function + * + * Update the @file image associated with @channel. + * + * The caller is responsible to finish any previous call, no + * concurrent call will be permitted. + **/ +void +spice_nbd_channel_set_file_async(SpiceNbdChannel *self, + GFile *file, SpiceNbdOpenFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + + g_return_if_fail(SPICE_IS_NBD_CHANNEL(self)); + g_return_if_fail(!file || G_IS_FILE(file)); + + SpiceNbdChannelPrivate *c = self->priv; + + if (c->pending_session) { + g_simple_async_report_error_in_idle(G_OBJECT (self), + callback, + user_data, + SPICE_CLIENT_ERROR, + SPICE_CLIENT_ERROR_FAILED, + "Channel has already a pending session"); + return; + } + + simple = g_simple_async_result_new(G_OBJECT(self), + callback, + user_data, + spice_nbd_channel_set_file_async); + + CHANNEL_DEBUG(self, "set file %p 0x%X", file, flags); + + if (!file) { + g_clear_object(&c->export); + g_object_notify(G_OBJECT(self), "nbd-file"); + + update_nbd_session(self); + + g_simple_async_result_set_op_res_gboolean(simple, TRUE); + g_simple_async_result_complete_in_idle(simple); + g_object_unref(simple); + } else + nbd_export_new("", file, flags, cancellable, export_ready, simple); +} + +/** + * spice_nbd_set_file_finish: + * @channel: a #SpiceNbdChannel + * @result: a #GAsyncResult + * @error: a #GError location to store the error occurring, or %NULL + * to ignore + * + * Finishes an asynchronous file read operation started with + * spice_nbd_set_file_async(). + * + * Returns: %TRUE on success. + **/ +gboolean +spice_nbd_channel_set_file_finish(SpiceNbdChannel *self, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail(SPICE_IS_NBD_CHANNEL(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + g_return_val_if_fail(g_simple_async_result_is_valid(result, + G_OBJECT(self), + spice_nbd_channel_set_file_async), + FALSE); + + simple = (GSimpleAsyncResult *)result; + + if (g_simple_async_result_propagate_error(simple, error)) + return FALSE; + + return g_simple_async_result_get_op_res_gboolean(simple); +} + +/* coroutine context */ +static void nbd_handle_msg(SpiceChannel *channel, SpiceMsgIn *in) +{ + SpiceNbdChannel *self = SPICE_NBD_CHANNEL(channel); + SpiceNbdChannelPrivate *c = self->priv; + int size; + uint8_t *buf; + + buf = spice_msg_in_raw(in, &size); + + spice_vmc_input_stream_co_data( + SPICE_VMC_INPUT_STREAM(g_io_stream_get_input_stream(G_IO_STREAM(c->stream))), + buf, size); +} + + +/* coroutine context */ +static void spice_nbd_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg) +{ + int type = spice_msg_in_type(msg); + SpiceChannelClass *parent_class; + + parent_class = SPICE_CHANNEL_CLASS(spice_nbd_channel_parent_class); + + if (type == SPICE_MSG_SPICEVMC_DATA) + nbd_handle_msg(channel, msg); + else if (parent_class->handle_msg) + parent_class->handle_msg(channel, msg); + else + g_return_if_reached(); +} diff --git a/gtk/channel-nbd.h b/gtk/channel-nbd.h new file mode 100644 index 0000000..2cb2d7a --- /dev/null +++ b/gtk/channel-nbd.h @@ -0,0 +1,88 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2013 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ +#ifndef __SPICE_CLIENT_NBD_CHANNEL_H__ +#define __SPICE_CLIENT_NBD_CHANNEL_H__ + +#include <gio/gio.h> +#include "spice-client.h" +#include "channel-port.h" + +G_BEGIN_DECLS + +#define SPICE_TYPE_NBD_CHANNEL (spice_nbd_channel_get_type()) +#define SPICE_NBD_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SPICE_TYPE_NBD_CHANNEL, SpiceNbdChannel)) +#define SPICE_NBD_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SPICE_TYPE_NBD_CHANNEL, SpiceNbdChannelClass)) +#define SPICE_IS_NBD_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SPICE_TYPE_NBD_CHANNEL)) +#define SPICE_IS_NBD_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SPICE_TYPE_NBD_CHANNEL)) +#define SPICE_NBD_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SPICE_TYPE_NBD_CHANNEL, SpiceNbdChannelClass)) + +typedef struct _SpiceNbdChannel SpiceNbdChannel; +typedef struct _SpiceNbdChannelClass SpiceNbdChannelClass; +typedef struct _SpiceNbdChannelPrivate SpiceNbdChannelPrivate; + +/** + * SpiceNbdOpenFlags: + * + **/ +typedef enum +{ + SPICE_NBD_OPEN_NONE = 0, + + SPICE_NBD_OPEN_READWRITE = 1 << 0, +} SpiceNbdOpenFlags; + +/** + * SpiceNbdChannel: + * + * The #SpiceNbdChannel struct is opaque and should not be accessed directly. + */ +struct _SpiceNbdChannel { + SpicePortChannel parent; + + /*< private >*/ + SpiceNbdChannelPrivate *priv; + /* Do not add fields to this struct */ +}; + +/** + * SpiceNbdChannelClass: + * @parent_class: Parent class. + * + * Class structure for #SpiceNbdChannel. + */ +struct _SpiceNbdChannelClass { + SpicePortChannelClass parent_class; + + /*< private >*/ + /* Do not add fields to this struct */ +}; + +GType spice_nbd_channel_get_type(void); + +void spice_nbd_channel_set_file_async(SpiceNbdChannel *channel, + GFile *file, SpiceNbdOpenFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean spice_nbd_channel_set_file_finish(SpiceNbdChannel *channel, + GAsyncResult *result, GError **error); +GFile* spice_nbd_channel_get_file(SpiceNbdChannel *channel); + +G_END_DECLS + +#endif /* __SPICE_CLIENT_NBD_CHANNEL_H__ */ diff --git a/gtk/map-file b/gtk/map-file index 368b44f..43ed851 100644 --- a/gtk/map-file +++ b/gtk/map-file @@ -3,6 +3,11 @@ global: spice_audio_get; spice_audio_get_type; spice_audio_new; +spice_nbd_channel_get_type; +spice_nbd_open_flags_get_type; +spice_nbd_channel_set_file_async; +spice_nbd_channel_set_file_finish; +spice_nbd_channel_get_file; spice_channel_connect; spice_channel_destroy; spice_channel_disconnect; diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c index 901c193..b8acfb4 100644 --- a/gtk/spice-channel.c +++ b/gtk/spice-channel.c @@ -1859,6 +1859,7 @@ static const char *to_string[] = { [ SPICE_CHANNEL_SMARTCARD ] = "smartcard", [ SPICE_CHANNEL_USBREDIR ] = "usbredir", [ SPICE_CHANNEL_PORT ] = "port", + [ SPICE_CHANNEL_NBD ] = "nbd", }; /** @@ -1919,6 +1920,7 @@ gchar *spice_channel_supported_string(void) #ifdef USE_USBREDIR spice_channel_type_to_string(SPICE_CHANNEL_USBREDIR), #endif + spice_channel_type_to_string(SPICE_CHANNEL_NBD), NULL); } @@ -1983,6 +1985,10 @@ SpiceChannel *spice_channel_new(SpiceSession *s, int type, int id) break; } #endif + case SPICE_CHANNEL_NBD: { + gtype = SPICE_TYPE_NBD_CHANNEL; + break; + } case SPICE_CHANNEL_PORT: gtype = SPICE_TYPE_PORT_CHANNEL; break; diff --git a/gtk/spice-client.h b/gtk/spice-client.h index 730d11a..5b9b01b 100644 --- a/gtk/spice-client.h +++ b/gtk/spice-client.h @@ -41,6 +41,7 @@ #include "channel-smartcard.h" #include "channel-usbredir.h" #include "channel-port.h" +#include "channel-nbd.h" #include "smartcard-manager.h" #include "usb-device-manager.h" diff --git a/gtk/spice-glib-sym-file b/gtk/spice-glib-sym-file index 4fc8643..da7d14d 100644 --- a/gtk/spice-glib-sym-file +++ b/gtk/spice-glib-sym-file @@ -1,6 +1,10 @@ spice_audio_get spice_audio_get_type spice_audio_new +spice_nbd_channel_get_type +spice_nbd_open_flags_get_type +spice_nbd_set_file_async +spice_nbd_set_file_finish spice_channel_connect spice_channel_destroy spice_channel_disconnect -- 1.8.3.1 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel