[Spice-devel] [PATCH spice-gtk 4/5] Add NBD channel
Hans de Goede
hdegoede at redhat.com
Sat Jun 8 07:35:52 PDT 2013
Hi,
Looks good, ack.
Regards,
Hans
On 06/05/2013 05:39 PM, Marc-André Lureau wrote:
> 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 | 425 +++++++++++++++++++++++++++++++++++
> 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, 564 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 b15e4bb..111f51f 100644
> --- a/doc/reference/spice-gtk-sections.txt
> +++ b/doc/reference/spice-gtk-sections.txt
> @@ -425,3 +425,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 1932b46..7998262 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) \
> @@ -179,6 +180,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) \
> @@ -229,6 +231,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 \
> @@ -447,11 +450,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@\" }," \
> @@ -466,7 +477,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" \
> @@ -589,6 +600,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..e5978fd
> --- /dev/null
> +++ b/gtk/channel-nbd.c
> @@ -0,0 +1,425 @@
> +/* -*- 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-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 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) {
> + g_message("PORTEVENT");
> + 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->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 558a4d7..053372a 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 0a32d6c..5630741 100644
> --- a/gtk/spice-channel.c
> +++ b/gtk/spice-channel.c
> @@ -1882,6 +1882,7 @@ static const char *to_string[] = {
> [ SPICE_CHANNEL_SMARTCARD ] = "smartcard",
> [ SPICE_CHANNEL_USBREDIR ] = "usbredir",
> [ SPICE_CHANNEL_PORT ] = "port",
> + [ SPICE_CHANNEL_NBD ] = "nbd",
> };
>
> const gchar* spice_channel_type_to_string(gint type)
> @@ -1924,6 +1925,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);
> }
>
> @@ -1988,6 +1990,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 5a580d6..269a675 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
>
More information about the Spice-devel
mailing list