[Spice-devel] [PATCH spice-gtk 4/5] Add NBD channel
Marc-André Lureau
marcandre.lureau at gmail.com
Fri Nov 15 13:15:10 PST 2013
From: Marc-André Lureau <marcandre.lureau at redhat.com>
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 at Type@Value _ at 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
More information about the Spice-devel
mailing list