[Spice-commits] 8 commits - configure.ac data/Makefile.am data/org.spice-space.lowlevelusbaccess.policy gtk/Makefile.am gtk/channel-usbredir-priv.h gtk/channel-usbredir.c gtk/map-file gtk/spice-client-glib-usb-acl-helper.c gtk/spice-session-priv.h gtk/spice-session.c gtk/spice-widget-priv.h gtk/spice-widget.c gtk/usb-acl-helper.c gtk/usb-acl-helper.h gtk/usb-device-manager.c gtk/usb-device-manager.h vapi/Makefile.am vapi/spice-client-glib-2.0.deps

Hans de Goede jwrdegoede at kemper.freedesktop.org
Wed Nov 23 06:16:40 PST 2011


 configure.ac                                  |   19 +
 data/Makefile.am                              |    4 
 data/org.spice-space.lowlevelusbaccess.policy |   20 +
 gtk/Makefile.am                               |   41 +++
 gtk/channel-usbredir-priv.h                   |   14 -
 gtk/channel-usbredir.c                        |  246 ++++++++++++++++++----
 gtk/map-file                                  |    3 
 gtk/spice-client-glib-usb-acl-helper.c        |  284 +++++++++++++++++++++++++
 gtk/spice-session-priv.h                      |    4 
 gtk/spice-session.c                           |   27 ++
 gtk/spice-widget-priv.h                       |    1 
 gtk/spice-widget.c                            |   22 +
 gtk/usb-acl-helper.c                          |  287 ++++++++++++++++++++++++++
 gtk/usb-acl-helper.h                          |   75 ++++++
 gtk/usb-device-manager.c                      |  125 ++++++++---
 gtk/usb-device-manager.h                      |   13 -
 vapi/Makefile.am                              |    1 
 vapi/spice-client-glib-2.0.deps               |    1 
 18 files changed, 1102 insertions(+), 85 deletions(-)

New commits:
commit 40cae5011adb79489f7425f02c4c660623fe2b7a
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Tue Nov 15 16:27:04 2011 +0100

    Release our keyboard grab when we're going to invoke the usb acl helper
    
    The usb acl helper asks policykit, which may want to interact with the
    user through the policykit agent, which wants to grab the keyboard, if
    we then have the keyboard grabbed, the agent says authentication has failed,
    policykit rejects the helper opening up the acl and usbredir won't work.
    
    Unfortunately the only way to work around this is to temporarily release our
    own keyboard grab, not pretty but as discussed on irc, this is the "best"
    solution.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>

diff --git a/gtk/channel-usbredir.c b/gtk/channel-usbredir.c
index 767e68c..9479b6b 100644
--- a/gtk/channel-usbredir.c
+++ b/gtk/channel-usbredir.c
@@ -227,6 +227,8 @@ static void spice_usbredir_channel_open_acl_cb(
 
     spice_usb_acl_helper_close_acl(priv->acl_helper);
     g_clear_object(&priv->acl_helper);
+    g_object_set(spice_channel_get_session(SPICE_CHANNEL(channel)),
+                 "inhibit-keyboard-grab", FALSE, NULL);
 
     g_simple_async_result_complete_in_idle(priv->result);
     g_clear_object(&priv->result);
@@ -268,6 +270,8 @@ void spice_usbredir_channel_connect_async(SpiceUsbredirChannel *channel,
         priv->result = result;
         priv->state = STATE_WAITING_FOR_ACL_HELPER;
         priv->acl_helper = spice_usb_acl_helper_new();
+        g_object_set(spice_channel_get_session(SPICE_CHANNEL(channel)),
+                     "inhibit-keyboard-grab", TRUE, NULL);
         spice_usb_acl_helper_open_acl(priv->acl_helper,
                                       g_usb_device_get_bus(device),
                                       g_usb_device_get_address(device),
diff --git a/gtk/spice-session-priv.h b/gtk/spice-session-priv.h
index 8f10407..963735b 100644
--- a/gtk/spice-session-priv.h
+++ b/gtk/spice-session-priv.h
@@ -59,6 +59,10 @@ struct _SpiceSessionPrivate {
     /* whether to enable USB redirection */
     gboolean          usbredir;
 
+    /* Set when a usbredir channel has requested the keyboard grab to be
+       temporarily released (because it is going to invoke policykit) */
+    gboolean          inhibit_keyboard_grab;
+
     GStrv             disable_effects;
     gint              color_depth;
 
diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index 8fd98c1..3366b58 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -96,6 +96,7 @@ enum {
     PROP_SMARTCARD_CERTIFICATES,
     PROP_SMARTCARD_DB,
     PROP_USBREDIR,
+    PROP_INHIBIT_KEYBOARD_GRAB,
     PROP_DISABLE_EFFECTS,
     PROP_COLOR_DEPTH,
 };
@@ -360,6 +361,9 @@ static void spice_session_get_property(GObject    *gobject,
     case PROP_USBREDIR:
         g_value_set_boolean(value, s->usbredir);
         break;
+    case PROP_INHIBIT_KEYBOARD_GRAB:
+        g_value_set_boolean(value, s->inhibit_keyboard_grab);
+        break;
     case PROP_DISABLE_EFFECTS:
         g_value_set_boxed(value, s->disable_effects);
         break;
@@ -452,6 +456,9 @@ static void spice_session_set_property(GObject      *gobject,
     case PROP_USBREDIR:
         s->usbredir = g_value_get_boolean(value);
         break;
+    case PROP_INHIBIT_KEYBOARD_GRAB:
+        s->inhibit_keyboard_grab = g_value_get_boolean(value);
+        break;
     case PROP_DISABLE_EFFECTS:
         g_strfreev(s->disable_effects);
         s->disable_effects = g_value_dup_boxed(value);
@@ -798,6 +805,24 @@ static void spice_session_class_init(SpiceSessionClass *klass)
                           G_PARAM_STATIC_STRINGS));
 
     /**
+     * SpiceSession::inhibit-keyboard-grab
+     *
+     * This boolean is set by the usbredir channel to indicate to #SpiceDisplay
+     * that the keyboard grab should be temporarily released, because it is
+     * going to invoke policykit. It will get reset when the usbredir channel
+     * is done with polickit.
+     *
+     * Since: 0.8
+     **/
+    g_object_class_install_property
+        (gobject_class, PROP_INHIBIT_KEYBOARD_GRAB,
+         g_param_spec_boolean("inhibit-keyboard-grab",
+                        "Inhibit Keyboard Grab",
+                        "Request that SpiceDisplays don't grab the keyboard",
+                        FALSE,
+                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+    /**
      * SpiceSession::channel-new:
      * @session: the session that emitted the signal
      * @channel: the new #SpiceChannel
diff --git a/gtk/spice-widget-priv.h b/gtk/spice-widget-priv.h
index a5791a4..a86168d 100644
--- a/gtk/spice-widget-priv.h
+++ b/gtk/spice-widget-priv.h
@@ -46,6 +46,7 @@ struct _SpiceDisplayPrivate {
 
     /* options */
     bool                    keyboard_grab_enable;
+    gboolean                keyboard_grab_inhibit;
     bool                    mouse_grab_enable;
     bool                    resize_guest_enable;
 
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index cec9aaa..f095130 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -222,6 +222,22 @@ static void gtk_session_property_changed(GObject    *gobject,
     g_object_notify(G_OBJECT(display), g_param_spec_get_name(pspec));
 }
 
+static void session_inhibit_keyboard_grab_changed(GObject    *gobject,
+                                                  GParamSpec *pspec,
+                                                  gpointer    user_data)
+{
+    SpiceDisplay *display = user_data;
+    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
+
+    g_object_get(d->session, "inhibit-keyboard-grab",
+                 &d->keyboard_grab_inhibit, NULL);
+    if (d->keyboard_grab_inhibit) {
+        try_keyboard_ungrab(display);
+    } else {
+        try_keyboard_grab(display);
+    }
+}
+
 static void spice_display_dispose(GObject *obj)
 {
     SpiceDisplay *display = SPICE_DISPLAY(obj);
@@ -330,6 +346,10 @@ spice_display_constructor(GType                  gtype,
     g_signal_connect(d->gtk_session, "notify::auto-clipboard",
                      G_CALLBACK(gtk_session_property_changed), display);
 
+    g_signal_connect(d->session, "notify::inhibit-keyboard-grab",
+                     G_CALLBACK(session_inhibit_keyboard_grab_changed),
+                     display);
+
     return obj;
 }
 
@@ -414,6 +434,8 @@ static void try_keyboard_grab(SpiceDisplay *display)
     if (d->keyboard_grab_active)
         return;
 
+    if (d->keyboard_grab_inhibit)
+        return;
     if (!d->keyboard_grab_enable)
         return;
     if (!d->keyboard_have_focus)
commit 2161b5464fa8ad995ce6116f78aacb4c2389af55
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Wed Nov 9 20:33:47 2011 +0100

    Add a suid root helper to open usb device nodes
    
    spice-client needs to be able to open the device nodes under /dev/bus/usb
    to be able to redirect a usb device to the guest. Normally opening these
    nodes is only allowed by root. This patch adds a suid root helper which
    asks policykit if it is ok to grant raw usb device access, and if policykit
    says it is ok, opens up the acl so that the spice-client can open the device
    node.
    
    As soon as spice-client closes the stdin of the helper, the helper removes
    the extra rights. This ensures that the acl gets put back to normal even if
    the spice client crashes. Normally the spice-client closes stdin directly
    after opening the device node.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>

diff --git a/configure.ac b/configure.ac
index 09d97bc..4a94d7b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -315,15 +315,34 @@ AC_ARG_ENABLE([usbredir],
                  [Enable usbredir support @<:@default=yes@:>@]),
   [],
   [enable_usbredir="yes"])
+AC_ARG_ENABLE([polkit],
+  AS_HELP_STRING([--enable-polkit=@<:@yes/no@:>@],
+                 [Enable policykit support (for the usb acl helper)@<:@default=yes@:>@]),
+  [],
+  [enable_polkit="yes"])
 
 if test "x$enable_usbredir" = "xno"; then
   AM_CONDITIONAL(WITH_USBREDIR, false)
+  AM_CONDITIONAL(WITH_POLKIT, false)
 else
   PKG_CHECK_MODULES(GUDEV, gudev-1.0)
   PKG_CHECK_MODULES(LIBUSB, libusb-1.0 >= 1.0.9)
   PKG_CHECK_MODULES(LIBUSBREDIRHOST, libusbredirhost >= 0.3.1)
   AC_DEFINE(USE_USBREDIR, [1], [Define if supporting usbredir proxying])
   AM_CONDITIONAL(WITH_USBREDIR, true)
+  if test "x$enable_polkit" = "xno"; then
+    AM_CONDITIONAL(WITH_POLKIT, false)
+  else
+    PKG_CHECK_MODULES(POLKIT, polkit-gobject-1)
+    AC_CHECK_HEADER([acl/libacl.h],,
+                    AC_MSG_ERROR([cannot find headers for libacl]))
+    AC_CHECK_LIB([acl], [acl_get_file], [ACL_LIBS=-lacl] [AC_SUBST(ACL_LIBS)],
+                 AC_MSG_ERROR([cannot find libacl]))
+    AC_DEFINE(USE_POLKIT, [1], [Define if supporting polkit])
+    AM_CONDITIONAL(WITH_POLKIT, true)
+    POLICYDIR=`${PKG_CONFIG} polkit-gobject-1 --variable=policydir`
+    AC_SUBST(POLICYDIR)
+  fi
 fi
 
 AC_ARG_WITH([coroutine],
diff --git a/data/Makefile.am b/data/Makefile.am
index 5ad1157..71934af 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -17,6 +17,7 @@ EXTRA_DIST =					\
 	spice-protocol.vapi			\
 	gtkrc					\
 	$(desktop_in_files)			\
+	org.spice-space.lowlevelusbaccess.policy \
 	$(NULL)
 
 CLEANFILES =					\
@@ -30,4 +31,7 @@ DISTCLEANFILES =				\
 vapidir = $(VAPIDIR)
 vapi_DATA = spice-protocol.vapi
 
+policydir = $(POLICYDIR)
+policy_DATA = org.spice-space.lowlevelusbaccess.policy
+
 -include $(top_srcdir)/git.mk
diff --git a/data/org.spice-space.lowlevelusbaccess.policy b/data/org.spice-space.lowlevelusbaccess.policy
new file mode 100644
index 0000000..170f5ff
--- /dev/null
+++ b/data/org.spice-space.lowlevelusbaccess.policy
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+	  "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+	  "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+<policyconfig>
+
+  <vendor>The Spice Project</vendor>
+  <vendor_url>http://spice-space.org/</vendor_url>
+  <icon_name>spice</icon_name>
+
+  <action id="org.spice-space.lowlevelusbaccess">
+    <description>Low level USB device access</description>
+    <message>Privileges are required for low level USB device access (for usb device pass through).</message>
+    <defaults>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>auth_admin_keep</allow_active>
+    </defaults>
+  </action>
+
+</policyconfig>
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 17efc89..5dfa9f3 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -21,6 +21,10 @@ EXTRA_DIST =					\
 	$(NULL)
 
 bin_PROGRAMS = spicy snappy spicy-stats
+if WITH_POLKIT
+bin_PROGRAMS += spice-client-glib-usb-acl-helper
+endif
+
 lib_LTLIBRARIES = \
 	libspice-client-glib-2.0.la
 
@@ -188,6 +192,14 @@ else
 GUSB_SRCS =
 endif
 
+if WITH_POLKIT
+USB_ACL_HELPER_SRCS = \
+	usb-acl-helper.c		\
+	usb-acl-helper.h
+else
+USB_ACL_HELPER_SRCS =
+endif
+
 libspice_client_glib_2_0_la_SOURCES =	\
 	glib-compat.h			\
 	spice-audio.c			\
@@ -224,6 +236,7 @@ libspice_client_glib_2_0_la_SOURCES =	\
 	smartcard-manager-priv.h	\
 	usb-device-manager.c		\
 	$(GUSB_SRCS)			\
+	$(USB_ACL_HELPER_SRCS)		\
 	\
 	decode.h			\
 	decode-glz.c			\
@@ -364,6 +377,32 @@ spicy_CPPFLAGS =			\
 	$(NULL)
 
 
+if WITH_POLKIT
+spice_client_glib_usb_acl_helper_SOURCES =	\
+	spice-client-glib-usb-acl-helper.c	\
+	$(NULL)
+
+spice_client_glib_usb_acl_helper_LDADD =	\
+	$(GLIB2_LIBS)				\
+	$(GIO_LIBS)				\
+	$(POLKIT_LIBS)				\
+	$(ACL_LIBS)				\
+	$(NULL)
+
+spice_client_glib_usb_acl_helper_CPPFLAGS =	\
+	$(SPICE_CFLAGS)				\
+	$(GLIB2_CFLAGS)				\
+	$(GIO_CFLAGS)				\
+	$(POLKIT_CFLAGS)			\
+	$(NULL)
+
+install-exec-hook:
+	-chown root $(DESTDIR)$(bindir)/spice-client-glib-usb-acl-helper
+	-chmod u+s  $(DESTDIR)$(bindir)/spice-client-glib-usb-acl-helper
+
+endif
+
+
 snappy_SOURCES =			\
 	snappy.c			\
 	spice-cmdline.h			\
diff --git a/gtk/channel-usbredir.c b/gtk/channel-usbredir.c
index fd54594..767e68c 100644
--- a/gtk/channel-usbredir.c
+++ b/gtk/channel-usbredir.c
@@ -26,6 +26,9 @@
 #include <gusb/gusb-context-private.h>
 #include <gusb/gusb-device-private.h>
 #include <gusb/gusb-util.h>
+#if USE_POLKIT
+#include "usb-acl-helper.h"
+#endif
 #include "channel-usbredir-priv.h"
 #endif
 
@@ -51,6 +54,16 @@
 #define SPICE_USBREDIR_CHANNEL_GET_PRIVATE(obj)                                  \
     (G_TYPE_INSTANCE_GET_PRIVATE((obj), SPICE_TYPE_USBREDIR_CHANNEL, SpiceUsbredirChannelPrivate))
 
+enum SpiceUsbredirChannelState {
+    STATE_DISCONNECTED,
+#if USE_POLKIT
+    STATE_WAITING_FOR_ACL_HELPER,
+#endif
+    STATE_CONNECTING,
+    STATE_CONNECTED,
+    STATE_DISCONNECTING,
+};
+
 struct _SpiceUsbredirChannelPrivate {
     GUsbContext *context;
     GUsbDevice *device;
@@ -61,7 +74,11 @@ struct _SpiceUsbredirChannelPrivate {
     const uint8_t *read_buf;
     int read_buf_size;
     SpiceMsgOut *msg_out;
-    gboolean up;
+    enum SpiceUsbredirChannelState state;
+#if USE_POLKIT
+    GSimpleAsyncResult *result;
+    SpiceUsbAclHelper *acl_helper;
+#endif
 };
 
 static void spice_usbredir_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
@@ -111,6 +128,27 @@ static void spice_usbredir_channel_dispose(GObject *obj)
         G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->dispose(obj);
 }
 
+/*
+ * Note we don't have a finalize to unref our : device / context / acl_helper /
+ * result references. The reason for this is that depending on our state they
+ * are either:
+ * 1) Already unreferenced
+ * 2) Will be unreferenced by the disconnect call from dispose
+ * 3) Will be unreferenced by spice_usbredir_channel_open_acl_cb
+ *
+ * Now the last one may seem like an issue, since what will happen if
+ * spice_usbredir_channel_open_acl_cb will run after finalization?
+ *
+ * This will never happens since the GSimpleAsyncResult created before we
+ * get into the STATE_WAITING_FOR_ACL_HELPER takes a reference to its
+ * source object, which is our SpiceUsbredirChannel object, so
+ * the finalize won't hapen until spice_usbredir_channel_open_acl_cb runs,
+ * and unrefs priv->result which will in turn unref ourselve once the
+ * complete_in_idle call it does has completed. And once
+ * spice_usbredir_channel_open_acl_cb has run, all references we hold have
+ * been released even in the 3th scenario.
+ */
+
 static const spice_msg_handler usbredir_handlers[] = {
     [ SPICE_MSG_SPICEVMC_DATA ] = usbredir_handle_msg,
 };
@@ -125,6 +163,12 @@ static gboolean spice_usbredir_channel_open_device(
     libusb_device_handle *handle = NULL;
     int rc;
 
+    g_return_val_if_fail(priv->state == STATE_DISCONNECTED
+#if USE_POLKIT
+                         || priv->state == STATE_WAITING_FOR_ACL_HELPER
+#endif
+                         , FALSE);
+
     rc = libusb_open(_g_usb_device_get_device(priv->device), &handle);
     if (rc != 0) {
         g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
@@ -148,10 +192,47 @@ static gboolean spice_usbredir_channel_open_device(
     }
 
     spice_channel_connect(SPICE_CHANNEL(channel));
+    priv->state = STATE_CONNECTING;
 
     return TRUE;
 }
 
+#if USE_POLKIT
+static void spice_usbredir_channel_open_acl_cb(
+    GObject *gobject, GAsyncResult *acl_res, gpointer user_data)
+{
+    SpiceUsbAclHelper *acl_helper = SPICE_USB_ACL_HELPER(gobject);
+    SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(user_data);
+    SpiceUsbredirChannelPrivate *priv = channel->priv;
+    GError *err = NULL;
+
+    g_return_if_fail(acl_helper == priv->acl_helper);
+    g_return_if_fail(priv->state == STATE_WAITING_FOR_ACL_HELPER ||
+                     priv->state == STATE_DISCONNECTING);
+
+    spice_usb_acl_helper_open_acl_finish(acl_helper, acl_res, &err);
+    if (!err && priv->state == STATE_DISCONNECTING) {
+        err = g_error_new_literal(G_IO_ERROR, G_IO_ERROR_CANCELLED,
+                                  "USB redirection channel connect cancelled");
+    }
+    if (!err) {
+        spice_usbredir_channel_open_device(channel, &err);
+    }
+    if (err) {
+        g_simple_async_result_take_error(priv->result, err);
+        g_clear_object(&priv->context);
+        g_clear_object(&priv->device);
+        priv->state = STATE_DISCONNECTED;
+    }
+
+    spice_usb_acl_helper_close_acl(priv->acl_helper);
+    g_clear_object(&priv->acl_helper);
+
+    g_simple_async_result_complete_in_idle(priv->result);
+    g_clear_object(&priv->result);
+}
+#endif
+
 G_GNUC_INTERNAL
 void spice_usbredir_channel_connect_async(SpiceUsbredirChannel *channel,
                                           GUsbContext          *context,
@@ -173,7 +254,7 @@ void spice_usbredir_channel_connect_async(SpiceUsbredirChannel *channel,
     result = g_simple_async_result_new(G_OBJECT(channel), callback, user_data,
                                        spice_usbredir_channel_connect_async);
 
-    if (priv->device) {
+    if (priv->state != STATE_DISCONNECTED) {
         g_simple_async_result_set_error(result,
                             SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
                             "Error channel is busy");
@@ -183,9 +264,22 @@ void spice_usbredir_channel_connect_async(SpiceUsbredirChannel *channel,
     priv->context = g_object_ref(context);
     priv->device  = g_object_ref(device);
     if (!spice_usbredir_channel_open_device(channel, &err)) {
+#if USE_POLKIT
+        priv->result = result;
+        priv->state = STATE_WAITING_FOR_ACL_HELPER;
+        priv->acl_helper = spice_usb_acl_helper_new();
+        spice_usb_acl_helper_open_acl(priv->acl_helper,
+                                      g_usb_device_get_bus(device),
+                                      g_usb_device_get_address(device),
+                                      cancellable,
+                                      spice_usbredir_channel_open_acl_cb,
+                                      channel);
+        return;
+#else
         g_simple_async_result_take_error(result, err);
         g_clear_object(&priv->context);
         g_clear_object(&priv->device);
+#endif
     }
 
 done:
@@ -217,15 +311,27 @@ void spice_usbredir_channel_disconnect(SpiceUsbredirChannel *channel)
 
     SPICE_DEBUG("disconnecting usb channel %p", channel);
 
-    spice_channel_disconnect(SPICE_CHANNEL(channel), SPICE_CHANNEL_NONE);
-    priv->up = FALSE;
-
-    if (priv->host) {
+    switch (priv->state) {
+    case STATE_DISCONNECTED:
+    case STATE_DISCONNECTING:
+        break;
+#if USE_POLKIT
+    case STATE_WAITING_FOR_ACL_HELPER:
+        priv->state = STATE_DISCONNECTING;
+        /* We're still waiting for the acl helper -> cancel it */
+        spice_usb_acl_helper_close_acl(priv->acl_helper);
+        break;
+#endif
+    case STATE_CONNECTING:
+    case STATE_CONNECTED:
+        spice_channel_disconnect(SPICE_CHANNEL(channel), SPICE_CHANNEL_NONE);
         /* This also closes the libusb handle we passed to its _open */
         usbredirhost_close(priv->host);
         priv->host = NULL;
         g_clear_object(&priv->device);
         g_clear_object(&priv->context);
+        priv->state = STATE_DISCONNECTED;
+        break;
     }
 }
 
@@ -243,7 +349,8 @@ void spice_usbredir_channel_do_write(SpiceUsbredirChannel *channel)
     /* No recursion allowed! */
     g_return_if_fail(priv->msg_out == NULL);
 
-    if (!priv->up || !usbredirhost_has_data_to_write(priv->host))
+    if (priv->state != STATE_CONNECTED ||
+            !usbredirhost_has_data_to_write(priv->host))
         return;
 
     priv->msg_out = spice_msg_out_new(SPICE_CHANNEL(channel),
@@ -345,7 +452,9 @@ static void spice_usbredir_channel_up(SpiceChannel *c)
     SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(c);
     SpiceUsbredirChannelPrivate *priv = channel->priv;
 
-    priv->up = TRUE;
+    g_return_if_fail(priv->state == STATE_CONNECTING);
+
+    priv->state = STATE_CONNECTED;
     /* Flush any pending writes */
     spice_usbredir_channel_do_write(channel);
 }
diff --git a/gtk/spice-client-glib-usb-acl-helper.c b/gtk/spice-client-glib-usb-acl-helper.c
new file mode 100644
index 0000000..2e31bd3
--- /dev/null
+++ b/gtk/spice-client-glib-usb-acl-helper.c
@@ -0,0 +1,284 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2011 Red Hat, Inc.
+   Copyright (C) 2009 Kay Sievers <kay.sievers at vrfy.org>
+
+   Red Hat Authors:
+   Hans de Goede <hdegoede at redhat.com>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; either version 2 of the License,
+   or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gio/gunixinputstream.h>
+#include <polkit/polkit.h>
+#include <acl/libacl.h>
+
+#define FATAL_ERROR(...) \
+    do { \
+        /* We print the error both to stdout, for the app invoking us and \
+           stderr for the end user */ \
+        fprintf(stdout, "Error " __VA_ARGS__); \
+        fprintf(stderr, "spice-client-glib-usb-helper: Error " __VA_ARGS__); \
+        exit_status = 1; \
+        cleanup(); \
+    } while (0)
+
+#define ERROR(...) \
+    do { \
+        fprintf(stdout, __VA_ARGS__); \
+        cleanup(); \
+    } while (0)
+
+enum state {
+    STATE_WAITING_FOR_BUS_N_DEV,
+    STATE_WAITING_FOR_POL_KIT,
+    STATE_WAITING_FOR_STDIN_EOF,
+};
+
+static enum state state = STATE_WAITING_FOR_BUS_N_DEV;
+static int exit_status;
+static int busnum, devnum;
+static char path[PATH_MAX];
+static GMainLoop *loop;
+static GDataInputStream *stdin_stream;
+static GCancellable *polkit_cancellable;
+static PolkitSubject *subject;
+static PolkitAuthority *authority;
+
+/*
+ * This function is a copy of the same function in udev, written by Kay
+ * Sievers, you can find it in udev in extras/udev-acl/udev-acl.c
+ */
+static int set_facl(const char* filename, uid_t uid, int add)
+{
+    int get;
+    acl_t acl;
+    acl_entry_t entry = NULL;
+    acl_entry_t e;
+    acl_permset_t permset;
+    int ret;
+
+    /* don't touch ACLs for root */
+    if (uid == 0)
+        return 0;
+
+    /* read current record */
+    acl = acl_get_file(filename, ACL_TYPE_ACCESS);
+    if (!acl)
+        return -1;
+
+    /* locate ACL_USER entry for uid */
+    get = acl_get_entry(acl, ACL_FIRST_ENTRY, &e);
+    while (get == 1) {
+        acl_tag_t t;
+
+        acl_get_tag_type(e, &t);
+        if (t == ACL_USER) {
+            uid_t *u;
+
+            u = (uid_t*)acl_get_qualifier(e);
+            if (u == NULL) {
+                ret = -1;
+                goto out;
+            }
+            if (*u == uid) {
+                entry = e;
+                acl_free(u);
+                break;
+            }
+            acl_free(u);
+        }
+
+        get = acl_get_entry(acl, ACL_NEXT_ENTRY, &e);
+    }
+
+    /* remove ACL_USER entry for uid */
+    if (!add) {
+        if (entry == NULL) {
+            ret = 0;
+            goto out;
+        }
+        acl_delete_entry(acl, entry);
+        goto update;
+    }
+
+    /* create ACL_USER entry for uid */
+    if (entry == NULL) {
+        ret = acl_create_entry(&acl, &entry);
+        if (ret != 0)
+            goto out;
+        acl_set_tag_type(entry, ACL_USER);
+        acl_set_qualifier(entry, &uid);
+    }
+
+    /* add permissions for uid */
+    acl_get_permset(entry, &permset);
+    acl_add_perm(permset, ACL_READ|ACL_WRITE);
+update:
+    /* update record */
+    acl_calc_mask(&acl);
+    ret = acl_set_file(filename, ACL_TYPE_ACCESS, acl);
+    if (ret != 0)
+        goto out;
+out:
+    acl_free(acl);
+    return ret;
+}
+
+static void cleanup()
+{
+    if (polkit_cancellable)
+        g_cancellable_cancel(polkit_cancellable);
+
+    if (state == STATE_WAITING_FOR_STDIN_EOF)
+        set_facl(path, getuid(), 0);
+
+    g_main_loop_quit(loop);
+}
+
+static void check_authorization_cb(PolkitAuthority *authority,
+                                   GAsyncResult *res, gpointer data)
+{
+    PolkitAuthorizationResult *result;
+    GError *err = NULL;
+
+    g_clear_object(&polkit_cancellable);
+
+    result = polkit_authority_check_authorization_finish(authority, res, &err);
+    if (err) {
+        FATAL_ERROR("PoliciKit error: %s\n", err->message);
+        g_error_free(err);
+        return;
+    }
+
+    if (!polkit_authorization_result_get_is_authorized(result)) {
+        ERROR("Not authorized\n");
+        return;
+    }
+
+    snprintf(path, PATH_MAX, "/dev/bus/usb/%03d/%03d", busnum, devnum);
+    if (set_facl(path, getuid(), 1)) {
+        FATAL_ERROR("setting facl: %s\n", strerror(errno));
+        return;
+    }
+
+    fprintf(stdout, "SUCCESS\n");
+    fflush(stdout);
+    state = STATE_WAITING_FOR_STDIN_EOF;
+}
+
+static void stdin_read_complete(GObject *src, GAsyncResult *res, gpointer data)
+{
+    char *s, *ep;
+    GError *err = NULL;
+    gsize len;
+
+    s = g_data_input_stream_read_line_finish(G_DATA_INPUT_STREAM(src), res,
+                                             &len, &err);
+    if (!s) {
+        if (err) {
+            FATAL_ERROR("Reading from stdin: %s\n", err->message);
+            g_error_free(err);
+            return;
+        }
+
+        switch (state) {
+        case STATE_WAITING_FOR_BUS_N_DEV:
+            FATAL_ERROR("EOF while waiting for bus and device num\n");
+            break;
+        case STATE_WAITING_FOR_POL_KIT:
+            ERROR("Cancelled while waiting for authorization\n");
+            break;
+        case STATE_WAITING_FOR_STDIN_EOF:
+            cleanup();
+            break;
+        }
+        return;
+    }
+
+    switch (state) {
+    case STATE_WAITING_FOR_BUS_N_DEV:
+        busnum = strtol(s, &ep, 10);
+        if (!isspace(*ep)) {
+            FATAL_ERROR("Invalid busnum / devnum: %s\n", s);
+            break;
+        }
+        devnum = strtol(ep, &ep, 10);
+        if (*ep != '\0') {
+            FATAL_ERROR("Invalid busnum / devnum: %s\n", s);
+            break;
+        }
+
+        state = STATE_WAITING_FOR_POL_KIT;
+
+        polkit_cancellable = g_cancellable_new();
+        polkit_authority_check_authorization(
+            authority, subject, "org.spice-space.lowlevelusbaccess", NULL,
+            POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
+            polkit_cancellable, (GAsyncReadyCallback)check_authorization_cb,
+            loop);
+
+        g_data_input_stream_read_line_async(stdin_stream, G_PRIORITY_DEFAULT,
+                                            NULL, stdin_read_complete, NULL);
+        break;
+    default:
+        FATAL_ERROR("Unexpected extra input in state %d: %s\n", state, s);
+    }
+}
+
+int main(void)
+{
+    pid_t parent_pid;
+    GInputStream *stdin_unix_stream;
+
+    g_type_init();
+
+    loop = g_main_loop_new(NULL, FALSE);
+
+    authority = polkit_authority_get_sync(NULL, NULL);
+    parent_pid = getppid ();
+    if (parent_pid == 1) {
+            FATAL_ERROR("Parent process was reaped by init(1)\n");
+            return 1;
+    }
+    subject = polkit_unix_process_new(parent_pid);
+
+    stdin_unix_stream = g_unix_input_stream_new(STDIN_FILENO, 0);
+    stdin_stream = g_data_input_stream_new(stdin_unix_stream);
+    g_data_input_stream_set_newline_type(stdin_stream,
+                                         G_DATA_STREAM_NEWLINE_TYPE_LF);
+    g_clear_object(&stdin_unix_stream);
+    g_data_input_stream_read_line_async(stdin_stream, G_PRIORITY_DEFAULT, NULL,
+                                        stdin_read_complete, NULL);
+
+    g_main_loop_run(loop);
+
+    if (polkit_cancellable)
+        g_clear_object(&polkit_cancellable);
+    g_object_unref(stdin_stream);
+    g_object_unref(authority);
+    g_object_unref(subject);
+    g_main_loop_unref(loop);
+
+    return exit_status;
+}
diff --git a/gtk/usb-acl-helper.c b/gtk/usb-acl-helper.c
new file mode 100644
index 0000000..e5f8b4d
--- /dev/null
+++ b/gtk/usb-acl-helper.c
@@ -0,0 +1,287 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2011 Red Hat, Inc.
+
+   Red Hat Authors:
+   Hans de Goede <hdegoede at redhat.com>
+
+   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 "config.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "usb-acl-helper.h"
+
+/* ------------------------------------------------------------------ */
+/* gobject glue                                                       */
+
+#define SPICE_USB_ACL_HELPER_GET_PRIVATE(obj)                                  \
+    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), SPICE_TYPE_USB_ACL_HELPER, SpiceUsbAclHelperPrivate))
+
+struct _SpiceUsbAclHelperPrivate {
+    GSimpleAsyncResult *result;
+    GIOChannel *in_ch;
+    GIOChannel *out_ch;
+    GCancellable *cancellable;
+    gulong cancellable_id;
+};
+
+G_DEFINE_TYPE(SpiceUsbAclHelper, spice_usb_acl_helper, G_TYPE_OBJECT);
+
+static void spice_usb_acl_helper_init(SpiceUsbAclHelper *self)
+{
+    self->priv = SPICE_USB_ACL_HELPER_GET_PRIVATE(self);
+}
+
+static void spice_usb_acl_helper_cleanup(SpiceUsbAclHelper *self)
+{
+    SpiceUsbAclHelperPrivate *priv = self->priv;
+
+    g_cancellable_disconnect(priv->cancellable, priv->cancellable_id);
+    priv->cancellable = NULL;
+    priv->cancellable_id = 0;
+
+    g_clear_object(&priv->result);
+
+    if (priv->in_ch) {
+        g_io_channel_unref(priv->in_ch);
+        priv->in_ch = NULL;
+    }
+
+    if (priv->out_ch) {
+        g_io_channel_unref(priv->out_ch);
+        priv->out_ch = NULL;
+    }
+}
+
+static void spice_usb_acl_helper_finalize(GObject *gobject)
+{
+    spice_usb_acl_helper_cleanup(SPICE_USB_ACL_HELPER(gobject));
+}
+
+static void spice_usb_acl_helper_class_init(SpiceUsbAclHelperClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+    gobject_class->finalize     = spice_usb_acl_helper_finalize;
+
+    g_type_class_add_private(klass, sizeof(SpiceUsbAclHelperPrivate));
+}
+
+/* ------------------------------------------------------------------ */
+/* callbacks                                                          */
+
+static gboolean cb_out_watch(GIOChannel    *channel,
+                             GIOCondition   cond,
+                             gpointer      *user_data)
+{
+    SpiceUsbAclHelper *self = SPICE_USB_ACL_HELPER(user_data);
+    SpiceUsbAclHelperPrivate *priv = self->priv;
+    gboolean success = FALSE;
+    GError *err = NULL;
+    GIOStatus status;
+    gchar *string;
+    gsize size;
+
+    /* Check that we've not been cancelled */
+    if (priv->result == NULL)
+        goto done;
+
+    g_return_val_if_fail(channel == priv->out_ch, FALSE);
+
+    status = g_io_channel_read_line(priv->out_ch, &string, &size, NULL, &err);
+    switch (status) {
+        case G_IO_STATUS_NORMAL:
+            string[strlen(string) - 1] = 0;
+            if (!strcmp(string, "SUCCESS")) {
+                success = TRUE;
+            } else {
+                g_simple_async_result_set_error(priv->result,
+                            SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                            "Error setting USB device node ACL: '%s'",
+                            string);
+            }
+            g_free(string);
+            break;
+        case G_IO_STATUS_ERROR:
+            g_simple_async_result_take_error(priv->result, err);
+            break;
+        case G_IO_STATUS_EOF:
+            g_simple_async_result_set_error(priv->result,
+                        SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                        "Unexpected EOF reading from acl helper stdout");
+        case G_IO_STATUS_AGAIN:
+            return TRUE; /* Wait for more input */
+    }
+
+    g_cancellable_disconnect(priv->cancellable, priv->cancellable_id);
+    priv->cancellable = NULL;
+    priv->cancellable_id = 0;
+
+    g_simple_async_result_complete_in_idle(priv->result);
+    g_clear_object(&priv->result);
+
+    if (!success)
+        spice_usb_acl_helper_cleanup(self);
+
+done:
+    g_object_unref(self);
+    return FALSE;
+}
+
+static void cancelled_cb(GCancellable *cancellable, gpointer user_data)
+{
+    SpiceUsbAclHelper *self = SPICE_USB_ACL_HELPER(user_data);
+
+    spice_usb_acl_helper_close_acl(self);
+}
+
+static void helper_child_watch_cb(GPid pid, gint status, gpointer user_data)
+{
+    /* Nothing to do, but we need the child watch to avoid zombies */
+}
+
+/* ------------------------------------------------------------------ */
+/* private api                                                        */
+
+G_GNUC_INTERNAL
+SpiceUsbAclHelper *spice_usb_acl_helper_new(void)
+{
+    GObject *obj;
+
+    obj = g_object_new(SPICE_TYPE_USB_ACL_HELPER, NULL);
+
+    return SPICE_USB_ACL_HELPER(obj);
+}
+
+G_GNUC_INTERNAL
+void spice_usb_acl_helper_open_acl(SpiceUsbAclHelper *self,
+                                   gint busnum, gint devnum,
+                                   GCancellable *cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer user_data)
+{
+    g_return_if_fail(SPICE_IS_USB_ACL_HELPER(self));
+
+    SpiceUsbAclHelperPrivate *priv = self->priv;
+    GSimpleAsyncResult *result;
+    GError *err = NULL;
+    GIOStatus status;
+    GPid helper_pid;
+    gsize bytes_written;
+    gchar *argv[] = { "spice-client-glib-usb-acl-helper", NULL };
+    gint in, out;
+    gchar buf[128];
+
+    result = g_simple_async_result_new(G_OBJECT(self), callback, user_data,
+                                       spice_usb_acl_helper_open_acl);
+
+    if (priv->out_ch) {
+        g_simple_async_result_set_error(result,
+                            SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                            "Error acl-helper already has an acl open");
+        goto done;
+    }
+
+    if (g_cancellable_set_error_if_cancelled(cancellable, &err)) {
+        g_simple_async_result_take_error(result, err);
+        goto done;
+    }
+
+    if (!g_spawn_async_with_pipes(NULL, argv, NULL,
+                           G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
+                           NULL, NULL, &helper_pid, &in, &out, NULL, &err)) {
+        g_simple_async_result_take_error(result, err);
+        goto done;
+    }
+    g_child_watch_add(helper_pid, helper_child_watch_cb, NULL);
+
+    priv->in_ch = g_io_channel_unix_new(in);
+    g_io_channel_set_close_on_unref(priv->in_ch, TRUE);
+
+    priv->out_ch = g_io_channel_unix_new(out);
+    g_io_channel_set_close_on_unref(priv->out_ch, TRUE);
+    status = g_io_channel_set_flags(priv->out_ch, G_IO_FLAG_NONBLOCK, &err);
+    if (status != G_IO_STATUS_NORMAL) {
+        g_simple_async_result_take_error(result, err);
+        goto done;
+    }
+
+    snprintf(buf, sizeof(buf), "%d %d\n", busnum, devnum);
+    status = g_io_channel_write_chars(priv->in_ch, buf, -1,
+                                      &bytes_written, &err);
+    if (status != G_IO_STATUS_NORMAL) {
+        g_simple_async_result_take_error(result, err);
+        goto done;
+    }
+    status = g_io_channel_flush(priv->in_ch, &err);
+    if (status != G_IO_STATUS_NORMAL) {
+        g_simple_async_result_take_error(result, err);
+        goto done;
+    }
+
+    priv->result = result;
+    if (cancellable) {
+        priv->cancellable = cancellable;
+        priv->cancellable_id = g_cancellable_connect(cancellable,
+                                                     G_CALLBACK(cancelled_cb),
+                                                     self, NULL);
+    }
+    g_io_add_watch(priv->out_ch, G_IO_IN|G_IO_HUP,
+                   (GIOFunc)cb_out_watch, g_object_ref(self));
+    return;
+
+done:
+    spice_usb_acl_helper_cleanup(self);
+    g_simple_async_result_complete_in_idle(result);
+    g_object_unref(result);
+}
+
+G_GNUC_INTERNAL
+gboolean spice_usb_acl_helper_open_acl_finish(
+    SpiceUsbAclHelper *self, GAsyncResult *res, GError **err)
+{
+    GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT(res);
+
+    g_return_val_if_fail(g_simple_async_result_is_valid(res, G_OBJECT(self),
+                                               spice_usb_acl_helper_open_acl),
+                         FALSE);
+
+    if (g_simple_async_result_propagate_error(result, err))
+        return FALSE;
+
+    return TRUE;
+}
+
+G_GNUC_INTERNAL
+void spice_usb_acl_helper_close_acl(SpiceUsbAclHelper *self)
+{
+    g_return_if_fail(SPICE_IS_USB_ACL_HELPER(self));
+
+    SpiceUsbAclHelperPrivate *priv = self->priv;
+
+    /* If the acl open has not completed yet report it as cancelled */
+    if (priv->result) {
+        g_simple_async_result_set_error(priv->result,
+                    G_IO_ERROR, G_IO_ERROR_CANCELLED,
+                    "Setting USB device node ACL cancelled");
+        g_simple_async_result_complete_in_idle(priv->result);
+    }
+
+    spice_usb_acl_helper_cleanup(self);
+}
diff --git a/gtk/usb-acl-helper.h b/gtk/usb-acl-helper.h
new file mode 100644
index 0000000..c43bdd0
--- /dev/null
+++ b/gtk/usb-acl-helper.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2011 Red Hat, Inc.
+
+   Red Hat Authors:
+   Hans de Goede <hdegoede at redhat.com>
+
+   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_USB_ACL_HELPER_H__
+#define __SPICE_USB_ACL_HELPER_H__
+
+#include "spice-client.h"
+#include <gio/gio.h>
+
+/* Note the entire usb-acl-helper class is private to spice-client-glib !! */
+
+G_BEGIN_DECLS
+
+#define SPICE_TYPE_USB_ACL_HELPER            (spice_usb_acl_helper_get_type ())
+#define SPICE_USB_ACL_HELPER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SPICE_TYPE_USB_ACL_HELPER, SpiceUsbAclHelper))
+#define SPICE_USB_ACL_HELPER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SPICE_TYPE_USB_ACL_HELPER, SpiceUsbAclHelperClass))
+#define SPICE_IS_USB_ACL_HELPER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SPICE_TYPE_USB_ACL_HELPER))
+#define SPICE_IS_USB_ACL_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_USB_ACL_HELPER))
+#define SPICE_USB_ACL_HELPER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_USB_ACL_HELPER, SpiceUsbAclHelperClass))
+
+#define SPICE_TYPE_USB_DEVICE                    (spice_usb_device_get_type())
+
+typedef struct _SpiceUsbAclHelper SpiceUsbAclHelper;
+typedef struct _SpiceUsbAclHelperClass SpiceUsbAclHelperClass;
+typedef struct _SpiceUsbAclHelperPrivate SpiceUsbAclHelperPrivate;
+
+struct _SpiceUsbAclHelper
+{
+    GObject parent;
+
+    /*< private >*/
+    SpiceUsbAclHelperPrivate *priv;
+    /* Do not add fields to this struct */
+};
+
+struct _SpiceUsbAclHelperClass
+{
+    GObjectClass parent_class;
+};
+
+GType spice_usb_device_get_type(void);
+GType spice_usb_acl_helper_get_type(void);
+
+SpiceUsbAclHelper *spice_usb_acl_helper_new(void);
+
+void spice_usb_acl_helper_open_acl(SpiceUsbAclHelper *self,
+                                   gint busnum, gint devnum,
+                                   GCancellable *cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer user_data);
+gboolean spice_usb_acl_helper_open_acl_finish(
+    SpiceUsbAclHelper *self, GAsyncResult *res, GError **err);
+
+void spice_usb_acl_helper_close_acl(SpiceUsbAclHelper *self);
+
+G_END_DECLS
+
+#endif /* __SPICE_USB_ACL_HELPER_H__ */
commit 012f2de1215b2f654571e2ca924f106c24315ba4
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Tue Nov 8 14:52:10 2011 +0100

    channel-usbredir: Make spice_usbredir_channel_connect async
    
    With the (upcoming) introduction of the usb device node acl helper, which
    uses policykit, spice_usbredir_channel_connect() may take a long time as
    it will be waiting for the helper, which will be waiting for policykit which
    may be interacting with the user -> Make spice_usbredir_channel_connect() async
    
    Note that this patch only changes spice_usbredir_channel_connect's
    API to use the standard GIO async API, it is not actually async after this
    patch since it does not yet call the acl helper.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>

diff --git a/gtk/channel-usbredir-priv.h b/gtk/channel-usbredir-priv.h
index 05988e1..8bb42a5 100644
--- a/gtk/channel-usbredir-priv.h
+++ b/gtk/channel-usbredir-priv.h
@@ -28,10 +28,16 @@
 
 G_BEGIN_DECLS
 
-gboolean spice_usbredir_channel_connect(SpiceUsbredirChannel *channel,
-                                        GUsbContext *context,
-                                        GUsbDevice *device,
-                                        GError **err);
+void spice_usbredir_channel_connect_async(SpiceUsbredirChannel *channel,
+                                          GUsbContext          *context,
+                                          GUsbDevice           *device,
+                                          GCancellable         *cancellable,
+                                          GAsyncReadyCallback   callback,
+                                          gpointer              user_data);
+gboolean spice_usbredir_channel_connect_finish(SpiceUsbredirChannel *channel,
+                                               GAsyncResult         *res,
+                                               GError              **err);
+
 void spice_usbredir_channel_disconnect(SpiceUsbredirChannel *channel);
 
 GUsbDevice *spice_usbredir_channel_get_device(SpiceUsbredirChannel *channel);
diff --git a/gtk/channel-usbredir.c b/gtk/channel-usbredir.c
index 4194b48..fd54594 100644
--- a/gtk/channel-usbredir.c
+++ b/gtk/channel-usbredir.c
@@ -153,27 +153,60 @@ static gboolean spice_usbredir_channel_open_device(
 }
 
 G_GNUC_INTERNAL
-gboolean spice_usbredir_channel_connect(SpiceUsbredirChannel *channel,
-                                        GUsbContext *context,
-                                        GUsbDevice *device,
-                                        GError **err)
+void spice_usbredir_channel_connect_async(SpiceUsbredirChannel *channel,
+                                          GUsbContext          *context,
+                                          GUsbDevice           *device,
+                                          GCancellable         *cancellable,
+                                          GAsyncReadyCallback   callback,
+                                          gpointer              user_data)
 {
     SpiceUsbredirChannelPrivate *priv = channel->priv;
+    GSimpleAsyncResult *result;
+    GError *err = NULL;
 
-    g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
+    g_return_if_fail(SPICE_IS_USBREDIR_CHANNEL(channel));
+    g_return_if_fail(context != NULL);
+    g_return_if_fail(device != NULL);
 
     SPICE_DEBUG("connecting usb channel %p", channel);
 
-    spice_channel_disconnect(SPICE_CHANNEL(channel), SPICE_CHANNEL_NONE);
+    result = g_simple_async_result_new(G_OBJECT(channel), callback, user_data,
+                                       spice_usbredir_channel_connect_async);
+
+    if (priv->device) {
+        g_simple_async_result_set_error(result,
+                            SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                            "Error channel is busy");
+        goto done;
+    }
 
     priv->context = g_object_ref(context);
     priv->device  = g_object_ref(device);
-    if (!spice_usbredir_channel_open_device(channel, err)) {
+    if (!spice_usbredir_channel_open_device(channel, &err)) {
+        g_simple_async_result_take_error(result, err);
         g_clear_object(&priv->context);
         g_clear_object(&priv->device);
-        return FALSE;
     }
 
+done:
+    g_simple_async_result_complete_in_idle(result);
+    g_object_unref(result);
+}
+
+G_GNUC_INTERNAL
+gboolean spice_usbredir_channel_connect_finish(SpiceUsbredirChannel *channel,
+                                               GAsyncResult         *res,
+                                               GError              **err)
+{
+    GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT(res);
+
+    g_return_val_if_fail(g_simple_async_result_is_valid(res, G_OBJECT(channel),
+                                     spice_usbredir_channel_connect_async),
+                         FALSE);
+
+    if (g_simple_async_result_propagate_error(result, err))
+        return FALSE;
+
     return TRUE;
 }
 
diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index 9b02066..b1d6c95 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -480,6 +480,21 @@ static void spice_usb_device_manager_dev_removed(GUsbDeviceList *devlist,
     g_signal_emit(manager, signals[DEVICE_REMOVED], 0, device);
     g_ptr_array_remove(priv->devices, device);
 }
+
+static void spice_usb_device_manager_channel_connect_cb(
+    GObject *gobject, GAsyncResult *channel_res, gpointer user_data)
+{
+    SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(gobject);
+    GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT(user_data);
+    GError *err = NULL;
+
+    spice_usbredir_channel_connect_finish(channel, channel_res, &err);
+    if (err) {
+        g_simple_async_result_take_error(result, err);
+    }
+    g_simple_async_result_complete(result);
+    g_object_unref(result);
+}
 #endif
 
 /* ------------------------------------------------------------------ */
@@ -635,13 +650,13 @@ void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
         if (spice_usbredir_channel_get_device(channel))
             continue; /* Skip already used channels */
 
-        if (!spice_usbredir_channel_connect(channel, priv->context,
-                                            (GUsbDevice *)device, &e)) {
-            g_simple_async_result_take_error(result, e);
-            goto done;
-        }
-
-        goto done;
+        spice_usbredir_channel_connect_async(channel,
+                                 priv->context,
+                                 (GUsbDevice *)device,
+                                 cancellable,
+                                 spice_usb_device_manager_channel_connect_cb,
+                                 result);
+        return;
     }
 #endif
 
commit 47d7c79be52f23b663e48af4eaeaba32dc486c43
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Wed Nov 16 09:23:37 2011 +0100

    usb-device-manager: Make spice_usb_device_manager_connect_device async
    
    With the (upcoming) introduction of the usb device node acl helper, which
    uses policykit, spice_usbredir_channel_connect() may take a long time as
    it will be waiting for the helper, which will be waiting for policykit which
    may be interacting with the user. So spice_usbredir_channel_connect() will
    need to become async, and since spice_usb_device_manager_connect_device
    calls spice_usbredir_channel_connect it thus also needs to become async.
    
    Note that this patch only changes spice_usb_device_manager_connect_device's
    API to use the standard GIO async API, it is not actually async after this
    patch since spice_usbredir_channel_connect is not yet async.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>

diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index a3960a7..17efc89 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -571,7 +571,7 @@ gtk_introspection_files =			\
 	$(NULL)
 
 SpiceClientGLib-2.0.gir: libspice-client-glib-2.0.la
-SpiceClientGLib_2_0_gir_INCLUDES = GObject-2.0
+SpiceClientGLib_2_0_gir_INCLUDES = GObject-2.0 Gio-2.0
 SpiceClientGLib_2_0_gir_CFLAGS = $(SPICE_COMMON_CPPFLAGS)
 SpiceClientGLib_2_0_gir_LIBS = libspice-client-glib-2.0.la
 SpiceClientGLib_2_0_gir_FILES = $(glib_introspection_files)
diff --git a/gtk/map-file b/gtk/map-file
index f9bc46c..b6b84f1 100644
--- a/gtk/map-file
+++ b/gtk/map-file
@@ -87,7 +87,8 @@ spice_usb_device_manager_get_type;
 spice_usb_device_manager_get;
 spice_usb_device_manager_get_devices;
 spice_usb_device_manager_is_device_connected;
-spice_usb_device_manager_connect_device;
+spice_usb_device_manager_connect_device_async;
+spice_usb_device_manager_connect_device_finish;
 spice_usb_device_manager_disconnect_device;
 spice_util_get_debug;
 spice_util_get_version_string;
diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index 2fb3dbb..9b02066 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -22,7 +22,6 @@
 #include "config.h"
 
 #include <glib-object.h>
-#include <gio/gio.h> /* For GInitable */
 
 #include "glib-compat.h"
 
@@ -424,6 +423,27 @@ static gboolean spice_usb_device_manager_source_callback(gpointer user_data)
     return TRUE;
 }
 
+static void spice_usb_device_manager_auto_connect_cb(GObject      *gobject,
+                                                     GAsyncResult *res,
+                                                     gpointer      user_data)
+{
+    SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(gobject);
+    SpiceUsbDevice *device = user_data;
+    GError *err = NULL;
+
+    spice_usb_device_manager_connect_device_finish(self, res, &err);
+    if (err) {
+        gchar *desc = spice_usb_device_get_description(device);
+        g_prefix_error(&err, "Could not auto-redirect %s: ", desc);
+        g_free(desc);
+
+        g_warning("%s", err->message);
+        g_signal_emit(self, signals[AUTO_CONNECT_FAILED], 0, device, err);
+        g_error_free(err);
+    }
+    g_object_unref(device);
+}
+
 static void spice_usb_device_manager_dev_added(GUsbDeviceList *devlist,
                                                GUsbDevice     *_device,
                                                GUdevDevice    *udev,
@@ -436,17 +456,9 @@ static void spice_usb_device_manager_dev_added(GUsbDeviceList *devlist,
     g_ptr_array_add(priv->devices, g_object_ref(device));
 
     if (priv->auto_connect) {
-        GError *err = NULL;
-        spice_usb_device_manager_connect_device(manager, device, &err);
-        if (err) {
-            gchar *desc = spice_usb_device_get_description(device);
-            g_prefix_error(&err, "Could not auto-redirect %s: ", desc);
-            g_free(desc);
-
-            g_warning("%s", err->message);
-            g_signal_emit(manager, signals[AUTO_CONNECT_FAILED], 0, device, err);
-            g_error_free(err);
-        }
+        spice_usb_device_manager_connect_device_async(manager, device, NULL,
+                                   spice_usb_device_manager_auto_connect_cb,
+                                   g_object_ref(device));
     }
 
     SPICE_DEBUG("device added %p", device);
@@ -570,36 +582,47 @@ gboolean spice_usb_device_manager_is_device_connected(SpiceUsbDeviceManager *sel
 }
 
 /**
- * spice_usb_device_manager_connect_device:
+ * spice_usb_device_manager_connect_device_async:
  * @manager: the #SpiceUsbDeviceManager manager
  * @device: a #SpiceUsbDevice to redirect
- *
- * Returns: %TRUE if @device has been successfully connected and
- * associated with a redirection chanel
+ * @cancellable: a #GCancellable or NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to callback
  */
-gboolean spice_usb_device_manager_connect_device(SpiceUsbDeviceManager *self,
-                                                 SpiceUsbDevice *device, GError **err)
+void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
+                                             SpiceUsbDevice *device,
+                                             GCancellable *cancellable,
+                                             GAsyncReadyCallback callback,
+                                             gpointer user_data)
 {
-    g_return_val_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self), FALSE);
-    g_return_val_if_fail(device != NULL, FALSE);
-    g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
+    GSimpleAsyncResult *result;
+
+    g_return_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self));
+    g_return_if_fail(device != NULL);
 
     SPICE_DEBUG("connecting device %p", device);
 
+    result = g_simple_async_result_new(G_OBJECT(self), callback, user_data,
+                               spice_usb_device_manager_connect_device_async);
+
 #ifdef USE_USBREDIR
     SpiceUsbDeviceManagerPrivate *priv = self->priv;
+    GError *e = NULL;
     guint i;
 
     if (spice_usb_device_manager_is_device_connected(self, device)) {
-        g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+        g_simple_async_result_set_error(result,
+                            SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
                             "Cannot connect an already connected usb device");
-        return FALSE;
+        goto done;
     }
 
     if (!priv->source) {
-        priv->source = g_usb_source_new(priv->main_context, priv->context, err);
-        if (*err)
-            return FALSE;
+        priv->source = g_usb_source_new(priv->main_context, priv->context, &e);
+        if (e) {
+            g_simple_async_result_take_error(result, e);
+            goto done;
+        }
 
         g_usb_source_set_callback(priv->source,
                                   spice_usb_device_manager_source_callback,
@@ -612,14 +635,39 @@ gboolean spice_usb_device_manager_connect_device(SpiceUsbDeviceManager *self,
         if (spice_usbredir_channel_get_device(channel))
             continue; /* Skip already used channels */
 
-        return spice_usbredir_channel_connect(channel, priv->context,
-                                              (GUsbDevice *)device, err);
+        if (!spice_usbredir_channel_connect(channel, priv->context,
+                                            (GUsbDevice *)device, &e)) {
+            g_simple_async_result_take_error(result, e);
+            goto done;
+        }
+
+        goto done;
     }
 #endif
 
-    g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
-                        "No free USB channel");
-    return FALSE;
+    g_simple_async_result_set_error(result,
+                            SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                            "No free USB channel");
+#ifdef USE_USBREDIR
+done:
+#endif
+    g_simple_async_result_complete_in_idle(result);
+    g_object_unref(result);
+}
+
+gboolean spice_usb_device_manager_connect_device_finish(
+    SpiceUsbDeviceManager *self, GAsyncResult *res, GError **err)
+{
+    GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT(res);
+
+    g_return_val_if_fail(g_simple_async_result_is_valid(res, G_OBJECT(self),
+                               spice_usb_device_manager_connect_device_async),
+                         FALSE);
+
+    if (g_simple_async_result_propagate_error(simple, err))
+        return FALSE;
+
+    return TRUE;
 }
 
 /**
diff --git a/gtk/usb-device-manager.h b/gtk/usb-device-manager.h
index 00f8eb2..2417674 100644
--- a/gtk/usb-device-manager.h
+++ b/gtk/usb-device-manager.h
@@ -22,6 +22,7 @@
 #define __SPICE_USB_DEVICE_MANAGER_H__
 
 #include "spice-client.h"
+#include <gio/gio.h>
 
 G_BEGIN_DECLS
 
@@ -96,9 +97,15 @@ GPtrArray *spice_usb_device_manager_get_devices(SpiceUsbDeviceManager *manager);
 
 gboolean spice_usb_device_manager_is_device_connected(SpiceUsbDeviceManager *manager,
                                                       SpiceUsbDevice *device);
-gboolean spice_usb_device_manager_connect_device(SpiceUsbDeviceManager *manager,
-                                                 SpiceUsbDevice *device,
-                                                 GError **err);
+void spice_usb_device_manager_connect_device_async(
+                                             SpiceUsbDeviceManager *manager,
+                                             SpiceUsbDevice *device,
+                                             GCancellable *cancellable,
+                                             GAsyncReadyCallback callback,
+                                             gpointer user_data);
+gboolean spice_usb_device_manager_connect_device_finish(
+    SpiceUsbDeviceManager *self, GAsyncResult *res, GError **err);
+
 void spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager *manager,
                                                 SpiceUsbDevice *device);
 
diff --git a/vapi/Makefile.am b/vapi/Makefile.am
index 87ffb8a..49f8509 100644
--- a/vapi/Makefile.am
+++ b/vapi/Makefile.am
@@ -4,6 +4,7 @@ if WITH_VALA
 vapidir = $(datadir)/vala/vapi
 vapi_DATA =						\
 	spice-client-glib-2.0.vapi			\
+	spice-client-glib-2.0.deps			\
 	spice-client-gtk-$(SPICE_GTK_API_VERSION).deps	\
 	spice-client-gtk-$(SPICE_GTK_API_VERSION).vapi	\
 	$(NULL)
diff --git a/vapi/spice-client-glib-2.0.deps b/vapi/spice-client-glib-2.0.deps
new file mode 100644
index 0000000..cd10dfd
--- /dev/null
+++ b/vapi/spice-client-glib-2.0.deps
@@ -0,0 +1 @@
+gio-2.0
commit 40dee564a03da47cb301067e19ee9f16c8c92175
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Wed Nov 9 19:44:00 2011 +0100

    channel-usbredir: Move usb device opening into a helper function
    
    This is a preparation patch for adding the usb device node acl helper
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>

diff --git a/gtk/channel-usbredir.c b/gtk/channel-usbredir.c
index 9ded4a1..4194b48 100644
--- a/gtk/channel-usbredir.c
+++ b/gtk/channel-usbredir.c
@@ -118,23 +118,14 @@ static const spice_msg_handler usbredir_handlers[] = {
 /* ------------------------------------------------------------------ */
 /* private api                                                        */
 
-G_GNUC_INTERNAL
-gboolean spice_usbredir_channel_connect(SpiceUsbredirChannel *channel,
-                                        GUsbContext *context,
-                                        GUsbDevice *device,
-                                        GError **err)
+static gboolean spice_usbredir_channel_open_device(
+    SpiceUsbredirChannel *channel, GError **err)
 {
     SpiceUsbredirChannelPrivate *priv = channel->priv;
     libusb_device_handle *handle = NULL;
     int rc;
 
-    g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
-
-    SPICE_DEBUG("connecting usb channel %p", channel);
-
-    spice_usbredir_channel_disconnect(channel);
-
-    rc = libusb_open(_g_usb_device_get_device(device), &handle);
+    rc = libusb_open(_g_usb_device_get_device(priv->device), &handle);
     if (rc != 0) {
         g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
                     "Could not open usb device: %s [%i]",
@@ -143,7 +134,7 @@ gboolean spice_usbredir_channel_connect(SpiceUsbredirChannel *channel,
     }
 
     priv->catch_error = err;
-    priv->host = usbredirhost_open(_g_usb_context_get_context(context),
+    priv->host = usbredirhost_open(_g_usb_context_get_context(priv->context),
                                    handle, usbredir_log,
                                    usbredir_read_callback,
                                    usbredir_write_callback,
@@ -152,13 +143,36 @@ gboolean spice_usbredir_channel_connect(SpiceUsbredirChannel *channel,
                                    usbredirhost_fl_write_cb_owns_buffer);
     priv->catch_error = NULL;
     if (!priv->host) {
+        g_return_val_if_fail(err == NULL || *err != NULL, FALSE);
         return FALSE;
     }
 
+    spice_channel_connect(SPICE_CHANNEL(channel));
+
+    return TRUE;
+}
+
+G_GNUC_INTERNAL
+gboolean spice_usbredir_channel_connect(SpiceUsbredirChannel *channel,
+                                        GUsbContext *context,
+                                        GUsbDevice *device,
+                                        GError **err)
+{
+    SpiceUsbredirChannelPrivate *priv = channel->priv;
+
+    g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
+
+    SPICE_DEBUG("connecting usb channel %p", channel);
+
+    spice_channel_disconnect(SPICE_CHANNEL(channel), SPICE_CHANNEL_NONE);
+
     priv->context = g_object_ref(context);
     priv->device  = g_object_ref(device);
-
-    spice_channel_connect(SPICE_CHANNEL(channel));
+    if (!spice_usbredir_channel_open_device(channel, err)) {
+        g_clear_object(&priv->context);
+        g_clear_object(&priv->device);
+        return FALSE;
+    }
 
     return TRUE;
 }
commit 884fd019fab50c90e3364f6a2ab5422e4194cbcd
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Wed Nov 16 16:45:49 2011 +0100

    channel-usbredir: Do disconnect from dispose instead of from finalize
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>

diff --git a/gtk/channel-usbredir.c b/gtk/channel-usbredir.c
index a070c22..9ded4a1 100644
--- a/gtk/channel-usbredir.c
+++ b/gtk/channel-usbredir.c
@@ -66,7 +66,7 @@ struct _SpiceUsbredirChannelPrivate {
 
 static void spice_usbredir_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
 static void spice_usbredir_channel_up(SpiceChannel *channel);
-static void spice_usbredir_channel_finalize(GObject *obj);
+static void spice_usbredir_channel_dispose(GObject *obj);
 static void usbredir_handle_msg(SpiceChannel *channel, SpiceMsgIn *in);
 
 static void usbredir_log(void *user_data, int level, const char *msg);
@@ -91,7 +91,7 @@ static void spice_usbredir_channel_class_init(SpiceUsbredirChannelClass *klass)
     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
     SpiceChannelClass *channel_class = SPICE_CHANNEL_CLASS(klass);
 
-    gobject_class->finalize     = spice_usbredir_channel_finalize;
+    gobject_class->dispose      = spice_usbredir_channel_dispose;
     channel_class->handle_msg   = spice_usbredir_handle_msg;
     channel_class->channel_up   = spice_usbredir_channel_up;
 
@@ -100,14 +100,15 @@ static void spice_usbredir_channel_class_init(SpiceUsbredirChannelClass *klass)
 }
 
 #ifdef USE_USBREDIR
-static void spice_usbredir_channel_finalize(GObject *obj)
+static void spice_usbredir_channel_dispose(GObject *obj)
 {
     SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(obj);
 
     spice_usbredir_channel_disconnect(channel);
 
-    if (G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->finalize)
-        G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->finalize(obj);
+    /* Chain up to the parent class */
+    if (G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->dispose)
+        G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->dispose(obj);
 }
 
 static const spice_msg_handler usbredir_handlers[] = {
commit e744abad4be96aa53374b6183feea620452990ac
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Wed Nov 16 11:12:04 2011 +0100

    channel-usbredir: Fixup and simplify #ifdef USE_USBREDIR handling
    
    glib does not like it when objects have a private data size of 0, so don't
    declare any private data when compiling without USE_USBREDIR set.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>

diff --git a/gtk/channel-usbredir.c b/gtk/channel-usbredir.c
index c0991c9..a070c22 100644
--- a/gtk/channel-usbredir.c
+++ b/gtk/channel-usbredir.c
@@ -46,11 +46,12 @@
  * from the Spice client to the VM. This channel handles these messages.
  */
 
+#ifdef USE_USBREDIR
+
 #define SPICE_USBREDIR_CHANNEL_GET_PRIVATE(obj)                                  \
     (G_TYPE_INSTANCE_GET_PRIVATE((obj), SPICE_TYPE_USBREDIR_CHANNEL, SpiceUsbredirChannelPrivate))
 
 struct _SpiceUsbredirChannelPrivate {
-#ifdef USE_USBREDIR
     GUsbContext *context;
     GUsbDevice *device;
     struct usbredirhost *host;
@@ -61,42 +62,32 @@ struct _SpiceUsbredirChannelPrivate {
     int read_buf_size;
     SpiceMsgOut *msg_out;
     gboolean up;
-#endif
 };
 
-G_DEFINE_TYPE(SpiceUsbredirChannel, spice_usbredir_channel, SPICE_TYPE_CHANNEL)
-
 static void spice_usbredir_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
 static void spice_usbredir_channel_up(SpiceChannel *channel);
+static void spice_usbredir_channel_finalize(GObject *obj);
 static void usbredir_handle_msg(SpiceChannel *channel, SpiceMsgIn *in);
 
-#ifdef USE_USBREDIR
 static void usbredir_log(void *user_data, int level, const char *msg);
 static int usbredir_read_callback(void *user_data, uint8_t *data, int count);
 static int usbredir_write_callback(void *user_data, uint8_t *data, int count);
 #endif
 
+G_DEFINE_TYPE(SpiceUsbredirChannel, spice_usbredir_channel, SPICE_TYPE_CHANNEL)
+
 /* ------------------------------------------------------------------ */
 
 static void spice_usbredir_channel_init(SpiceUsbredirChannel *channel)
 {
-    channel->priv = SPICE_USBREDIR_CHANNEL_GET_PRIVATE(channel);
-}
-
-static void spice_usbredir_channel_finalize(GObject *obj)
-{
 #ifdef USE_USBREDIR
-    SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(obj);
-
-    spice_usbredir_channel_disconnect(channel);
+    channel->priv = SPICE_USBREDIR_CHANNEL_GET_PRIVATE(channel);
 #endif
-
-    if (G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->finalize)
-        G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->finalize(obj);
 }
 
 static void spice_usbredir_channel_class_init(SpiceUsbredirChannelClass *klass)
 {
+#ifdef USE_USBREDIR
     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
     SpiceChannelClass *channel_class = SPICE_CHANNEL_CLASS(klass);
 
@@ -105,14 +96,24 @@ static void spice_usbredir_channel_class_init(SpiceUsbredirChannelClass *klass)
     channel_class->channel_up   = spice_usbredir_channel_up;
 
     g_type_class_add_private(klass, sizeof(SpiceUsbredirChannelPrivate));
+#endif
+}
+
+#ifdef USE_USBREDIR
+static void spice_usbredir_channel_finalize(GObject *obj)
+{
+    SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(obj);
+
+    spice_usbredir_channel_disconnect(channel);
+
+    if (G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->finalize)
+        G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->finalize(obj);
 }
 
 static const spice_msg_handler usbredir_handlers[] = {
     [ SPICE_MSG_SPICEVMC_DATA ] = usbredir_handle_msg,
 };
 
-#ifdef USE_USBREDIR
-
 /* ------------------------------------------------------------------ */
 /* private api                                                        */
 
@@ -272,8 +273,6 @@ static int usbredir_write_callback(void *user_data, uint8_t *data, int count)
     return count;
 }
 
-#endif /* USE_USBREDIR */
-
 /* --------------------------------------------------------------------- */
 /* coroutine context                                                     */
 static void spice_usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *msg)
@@ -295,19 +294,16 @@ static void spice_usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *msg)
 
 static void spice_usbredir_channel_up(SpiceChannel *c)
 {
-#ifdef USE_USBREDIR
     SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(c);
     SpiceUsbredirChannelPrivate *priv = channel->priv;
 
     priv->up = TRUE;
     /* Flush any pending writes */
     spice_usbredir_channel_do_write(channel);
-#endif
 }
 
 static void usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *in)
 {
-#ifdef USE_USBREDIR
     SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(c);
     SpiceUsbredirChannelPrivate *priv = channel->priv;
     int size;
@@ -325,5 +321,6 @@ static void usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *in)
     usbredirhost_read_guest_data(priv->host);
     /* Send any acks, etc. which may be queued now */
     spice_usbredir_channel_do_write(channel);
-#endif
 }
+
+#endif /* USE_USBREDIR */
commit b0d2365a86d265190c6e89978a7be4d0ad8031a7
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Wed Nov 16 09:17:07 2011 +0100

    Correct Since tag for SpiceSession:enable-usbredir:
    
    This was introduced post 0.7, and given the major changes done in git
    sofar the next release will be 0.8 rather then 0.7.1
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>

diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index 9e30c74..8fd98c1 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -786,7 +786,7 @@ static void spice_session_class_init(SpiceSessionClass *klass)
      * If set to TRUE, the usbredir channel will be enabled and USB devices
      * can be redirected to the guest
      *
-     * Since: 0.7
+     * Since: 0.8
      **/
     g_object_class_install_property
         (gobject_class, PROP_USBREDIR,


More information about the Spice-commits mailing list