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

Francesco Giudici fgiudici at redhat.com
Fri Jan 17 14:04:34 UTC 2020



On 17/01/20 12:09, Daniel P. Berrangé wrote:
> 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);

Oh, right, this is much clean and easy.. initially I did the hard way to 
share the code with windows. But turned out we don't need Windows 
support here... pushing v3 patch in a while!

Thanks

Francesco

> 
>>       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
> 





More information about the virt-tools-list mailing list