[virt-viewer PATCH v2] remote-viewer: add handler for SIGINT signal

Daniel P. Berrangé berrange at redhat.com
Fri Jan 17 11:09:38 UTC 2020


On Fri, Jan 17, 2020 at 12:04:08PM +0100, Francesco Giudici wrote:
> When remote-viewer is started from terminal, CTRL-C sends a SIGINT
> signal to the program causing immediate termination. On linux clients
> usb redirected devices are left without any kernel driver attached,
> causing them to appear as no more available to the host.
> Add a SIGINT handler to allow a clean exit, in particular to allow
> the kernel to attach back the host driver.
> The issue is present on linux only.
> 
> To perform usb device redirection, virt-viewer leverages spice-gtk
> library, which in turn relies on usbredir library, which uses libusb.
> In order to take control of the usb device the auto-loaded kernel
> driver must be detached. This is achieved (in the very end) with
> libusb_detach_kernel_driver(). Then the device interfaces can be claimed
> with libusb_claim_interface() and get in control to the application.
> During normal application termination, the usb channel is teared down,
> performing a reset of the usb device and giving back the control of the
> device to the kernel (libusb_attach_kernel_driver()).
> If the application quits without doing this, the device interfaces will
> end up with no driver attached, making them not usable in the host
> system.
> 
> Note that enabling libusb_set_auto_detach_kernel_driver() does not solve
> the issue, as this is just a convenient API from libusb: libusb will
> take care of detaching/attaching the driver to the interfaces of the usb
> device each time a call to libusb_release_interface() and
> libusb_claim_interface() is performed. An unexpected quit of the
> application will skip the libusb_release_interface() call too, leaving
> the interfaces without any driver attached.
> 
> Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1713311
> 
> Signed-off-by: Francesco Giudici <fgiudici at redhat.com>
> ---
>  src/virt-viewer-app.c | 79 +++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 79 insertions(+)
> 
> diff --git a/src/virt-viewer-app.c b/src/virt-viewer-app.c
> index da8cfa9..06e237b 100644
> --- a/src/virt-viewer-app.c
> +++ b/src/virt-viewer-app.c
> @@ -36,6 +36,7 @@
>  #include <glib/gprintf.h>
>  #include <glib/gi18n.h>
>  #include <errno.h>
> +#include <fcntl.h>
>  
>  #ifdef HAVE_SYS_SOCKET_H
>  #include <sys/socket.h>
> @@ -1756,6 +1757,74 @@ static gboolean opt_fullscreen = FALSE;
>  static gboolean opt_kiosk = FALSE;
>  static gboolean opt_kiosk_quit = FALSE;
>  
> +#ifndef G_OS_WIN32
> +static int sigint_pipe[2];
> +
> +static void
> +sigint_handler(int signum)
> +{
> +    int savedErrno;
> +
> +    g_return_if_fail(signum == SIGINT);
> +
> +    savedErrno = errno;
> +    if (write(sigint_pipe[1], "x", 1) == -1 && errno != EAGAIN)
> +        g_debug("SIGINT handler failure\n");
> +    errno = savedErrno;
> +}
> +
> +static void
> +register_sigint_handler()
> +{
> +    int flags, i;
> +    struct sigaction sa;
> +
> +    if (pipe(sigint_pipe) == -1)
> +        goto err;
> +
> +    for (i = 0; i < 2; i++) {
> +        flags = fcntl(sigint_pipe[i], F_GETFL);
> +        if (flags == -1)
> +            goto err;
> +        flags |= O_NONBLOCK;
> +        if (fcntl(sigint_pipe[i], F_SETFL, flags) == -1)
> +            goto err;
> +    }
> +
> +    sigemptyset(&sa.sa_mask);
> +    sa.sa_flags = SA_RESTART;
> +    sa.sa_handler = sigint_handler;
> +    if (sigaction(SIGINT, &sa, NULL) == -1)
> +        goto err;
> +
> +    return;
> +
> +err:
> +    g_debug("Cannot register SIGINT handler\n");
> +}
> +
> +static gboolean
> +sigint_cb(GIOChannel *source,
> +          GIOCondition condition,
> +          gpointer data)
> +{
> +    VirtViewerApp *self = VIRT_VIEWER_APP(data);
> +    VirtViewerAppPrivate *priv = self->priv;
> +    gchar sbuf;
> +
> +    g_assert(condition == G_IO_IN);
> +
> +    g_debug("got SIGINT, quitting\n");
> +    if (priv->started)
> +        virt_viewer_app_quit(self);
> +    else
> +        exit(0);
> +
> +    g_io_channel_read_chars (source, &sbuf, 1, NULL, NULL);
> +    return TRUE;
> +}
> +#endif
> +
>  static void
>  title_maybe_changed(VirtViewerApp *self, GParamSpec* pspec G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED)
>  {
> @@ -1766,10 +1835,20 @@ static void
>  virt_viewer_app_init(VirtViewerApp *self)
>  {
>      GError *error = NULL;
> +#ifndef G_OS_WIN32
> +    GIOChannel *sigint_channel = NULL;
> +#endif
> +
>      self->priv = virt_viewer_app_get_instance_private(self);
>  
>      gtk_window_set_default_icon_name("virt-viewer");
>  
> +#ifndef G_OS_WIN32
> +    register_sigint_handler();
> +    sigint_channel = g_io_channel_unix_new(sigint_pipe[0]);
> +    g_io_add_watch(sigint_channel, G_IO_IN, sigint_cb, self);
> +#endif

Just yesterday I learnt that GLib has native support for signal handling
in its event loop which allows this to be simplified:

   g_unix_signal_add (SIGINT, sigint_cb, self);

>      self->priv->displays = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_object_unref);
>      self->priv->config = g_key_file_new();
>      self->priv->config_file = g_build_filename(g_get_user_config_dir(),
> -- 
> 2.21.1
> 
> 

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|




More information about the virt-tools-list mailing list