On Thu, 2015-10-15 at 09:54 +0200, Fabiano Fidêncio wrote: > Jonathon, > > On Wed, Oct 14, 2015 at 9:26 PM, Jonathon Jongsma < > jjongsma@xxxxxxxxxx> wrote: > > The file transfer progress dialog depends on new API provided by > > spice-gtk to display the progress of each individual file being > > transferred between the client and the guest. If we're not building > > against a new enough version of spice-gtk, the dialog will simply > > be > > disabled. > > > > The dialog also allows each transfer to be cancelled individually, > > or to > > cancel all ongoing transfers at once. > > --- > > configure.ac | 4 + > > src/Makefile.am | 7 + > > src/virt-viewer-file-transfer-dialog.c | 227 > > +++++++++++++++++++++++++++++++++ > > src/virt-viewer-file-transfer-dialog.h | 61 +++++++++ > > src/virt-viewer-session-spice.c | 35 +++++ > > 5 files changed, 334 insertions(+) > > create mode 100644 src/virt-viewer-file-transfer-dialog.c > > create mode 100644 src/virt-viewer-file-transfer-dialog.h > > > > diff --git a/configure.ac b/configure.ac > > index cf75f0e..4aad0ba 100644 > > --- a/configure.ac > > +++ b/configure.ac > > @@ -180,8 +180,12 @@ AS_IF([test "x$with_spice_gtk" = "xyes"], > > [PKG_CHECK_MODULES(SPICE_CONTROLLER, [spice-controller])] > > [PKG_CHECK_MODULES(SPICE_PROTOCOL, [spice-protocol >= > > $SPICE_PROTOCOL_REQUIRED])] > > [AC_DEFINE([HAVE_SPICE_GTK], 1, [Have spice-gtk?])] > > + [PKG_CHECK_EXISTS([spice-client-glib-2.0 >= 0.30.6], > > + [with_file_transfer=1], > > [with_file_transfer=0])] > > + [AC_DEFINE_UNQUOTED([HAVE_FILE_TRANSFER], > > [$with_file_transfer], ["Use spice-gtk file transfer capability"])] > > ) > > AM_CONDITIONAL([HAVE_SPICE_GTK], [test "x$with_spice_gtk" = > > "xyes"]) > > +AM_CONDITIONAL([HAVE_FILE_TRANSFER], [test $with_file_transfer -eq > > 1]) > > > > AC_ARG_WITH([ovirt], > > AS_HELP_STRING([--without-ovirt], [Ignore presence of librest > > and disable oVirt support])) > > diff --git a/src/Makefile.am b/src/Makefile.am > > index 1ebc24e..7d4d2cf 100644 > > --- a/src/Makefile.am > > +++ b/src/Makefile.am > > @@ -85,6 +85,13 @@ libvirt_viewer_la_SOURCES += \ > > virt-viewer-display-spice.h \ > > virt-viewer-display-spice.c \ > > $(NULL) > > + > > +if HAVE_FILE_TRANSFER > > +libvirt_viewer_la_SOURCES += \ > > + virt-viewer-file-transfer-dialog.h \ > > + virt-viewer-file-transfer-dialog.c \ > > + $(NULL) > > +endif > > endif > > > > This approach for detecting the spice-gtk version and set the > HAVE_FILE_TRANSFER is okay. But, personally, I would have gone for > the > SPICE_GTK_CHECK_VERSION() in the files (considering it will work for > your purposes) :-) I talked with Fabiano briefly about this on IRC. To summarize: I had initially started implementing it with SPICE_GTK_CHECK_VERSION(), but there were some slight complications. In particular, I want to avoid compiling virt-viewer-file-transfer-dialog.[ch] completely in the case of an old version of spice-gtk. To do this, I could either use a configure check, or I could wrap the entire source and header file in SPICE_GTK_CHECK_VERSION() (and essentially compile an empty file). I find this approach a bit nicer. > > > if HAVE_OVIRT > > diff --git a/src/virt-viewer-file-transfer-dialog.c b/src/virt > > -viewer-file-transfer-dialog.c > > new file mode 100644 > > index 0000000..cdc4290 > > --- /dev/null > > +++ b/src/virt-viewer-file-transfer-dialog.c > > @@ -0,0 +1,227 @@ > > +/* > > + * Virt Viewer: A virtual machine console viewer > > + * > > + * Copyright (C) 2015 Red Hat, Inc. > > + * > > + * 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 General Public License for more details. > > + * > > + * You should have received a copy of the GNU General Public > > License > > + * along with this program; if not, write to the Free Software > > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 > > -1307 USA > > + */ > > + > > +#include "virt-viewer-file-transfer-dialog.h" > > +#include <glib/gi18n.h> > > + > > +G_DEFINE_TYPE(VirtViewerFileTransferDialog, > > virt_viewer_file_transfer_dialog, GTK_TYPE_DIALOG) > > + > > +#define FILE_TRANSFER_DIALOG_PRIVATE(o) \ > > + (G_TYPE_INSTANCE_GET_PRIVATE((o), > > VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG, > > VirtViewerFileTransferDialogPrivate)) > > + > > +struct _VirtViewerFileTransferDialogPrivate > > +{ > > + /* GHashTable<SpiceFileTransferTask, widgets> */ > > + GHashTable *file_transfers; > > +}; > > + > > + > > +static void > > +virt_viewer_file_transfer_dialog_dispose(GObject *object) > > +{ > > + VirtViewerFileTransferDialog *self = > > VIRT_VIEWER_FILE_TRANSFER_DIALOG(object); > > + > > + if (self->priv->file_transfers) { > > + g_hash_table_unref(self->priv->file_transfers); > > + self->priv->file_transfers = NULL; > > + } > > + > > + G_OBJECT_CLASS(virt_viewer_file_transfer_dialog_parent_class) > > ->dispose(object); > > +} > > + > > +static void > > +virt_viewer_file_transfer_dialog_class_init(VirtViewerFileTransfer > > DialogClass *klass) > > +{ > > + GObjectClass *object_class = G_OBJECT_CLASS(klass); > > + > > + g_type_class_add_private(klass, > > sizeof(VirtViewerFileTransferDialogPrivate)); > > + > > + object_class->dispose = > > virt_viewer_file_transfer_dialog_dispose; > > +} > > + > > +static void > > +dialog_response(GtkDialog *dialog, > > + gint response_id, > > + gpointer user_data G_GNUC_UNUSED) > > +{ > > + VirtViewerFileTransferDialog *self = > > VIRT_VIEWER_FILE_TRANSFER_DIALOG(dialog); > > + GHashTableIter iter; > > + gpointer key, value; > > + > > + switch (response_id) { > > + case GTK_RESPONSE_CANCEL: > > + /* cancel all current tasks */ > > + g_hash_table_iter_init(&iter, self->priv > > ->file_transfers); > > + > > + while (g_hash_table_iter_next(&iter, &key, &value)) { > > + > > spice_file_transfer_task_cancel(SPICE_FILE_TRANSFER_TASK(key)); > > + } > > + break; > > + case GTK_RESPONSE_DELETE_EVENT: > > + /* silently ignore */ > > + break; > > + default: > > + g_warn_if_reached(); > > + } > > +} > > + > > +static void task_cancel_clicked(GtkButton *button G_GNUC_UNUSED, > > + gpointer user_data) > > +{ > > + SpiceFileTransferTask *task = > > SPICE_FILE_TRANSFER_TASK(user_data); > > No need to cast here ... > > > + spice_file_transfer_task_cancel(task); > > +} > > + > > +typedef struct { > > + GtkWidget *vbox; > > + GtkWidget *hbox; > > + GtkWidget *progress; > > + GtkWidget *label; > > + GtkWidget *cancel; > > +} TaskWidgets; > > + > > +static TaskWidgets *task_widgets_new(SpiceFileTransferTask *task) > > +{ > > + TaskWidgets *w = g_new0(TaskWidgets, 1); > > + > > +#if !GTK_CHECK_VERSION(3, 0, 0) > > + w->vbox = gtk_vbox_new(FALSE, 6); > > + w->hbox = gtk_hbox_new(FALSE, 12); > > +#else > > + w->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); > > + w->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); > > +#endif > > + w->progress = gtk_progress_bar_new(); > > + w->label = > > gtk_label_new(spice_file_transfer_task_get_filename(task)); > > +#if !GTK_CHECK_VERSION(3, 0, 0) > > + w->cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL); > > +#else > > + w->cancel = gtk_button_new_from_icon_name("gtk-cancel", > > GTK_ICON_SIZE_SMALL_TOOLBAR); > > +#endif > > +#if GTK_CHECK_VERSION(3, 0, 0) > > + gtk_widget_set_hexpand(w->progress, TRUE); > > + gtk_widget_set_valign(w->progress, GTK_ALIGN_CENTER); > > + gtk_widget_set_hexpand(w->label, TRUE); > > + gtk_widget_set_valign(w->label, GTK_ALIGN_END); > > + gtk_widget_set_halign(w->label, GTK_ALIGN_START); > > + gtk_widget_set_hexpand(w->cancel, FALSE); > > + gtk_widget_set_valign(w->cancel, GTK_ALIGN_CENTER); > > +#endif > > + > > + g_signal_connect(w->cancel, "clicked", > > + G_CALLBACK(task_cancel_clicked), task); > > + > > + gtk_box_pack_start(GTK_BOX(w->hbox), w->progress, TRUE, TRUE, > > 0); > > + gtk_box_pack_start(GTK_BOX(w->hbox), w->cancel, FALSE, TRUE, > > 0); > > + gtk_box_pack_start(GTK_BOX(w->vbox), w->label, TRUE, TRUE, 0); > > + gtk_box_pack_start(GTK_BOX(w->vbox), w->hbox, TRUE, TRUE, 0); > > + > > + gtk_widget_show_all(w->vbox); > > + return w; > > +} > > + > > +static gboolean delete_event(GtkWidget *widget, > > + GdkEvent *event G_GNUC_UNUSED, > > + gpointer user_data G_GNUC_UNUSED) > > +{ > > + /* don't allow window to be deleted, just process the response > > signal, > > + * which may result in the window being hidden */ > > + gtk_dialog_response(GTK_DIALOG(widget), GTK_RESPONSE_CANCEL); > > + return TRUE; > > +} > > + > > +static void > > +virt_viewer_file_transfer_dialog_init(VirtViewerFileTransferDialog > > *self) > > +{ > > + GtkBox *content = > > GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(self))); > > + > > + self->priv = FILE_TRANSFER_DIALOG_PRIVATE(self); > > + > > + gtk_widget_set_size_request(GTK_WIDGET(content), 400, -1); > > + gtk_container_set_border_width(GTK_CONTAINER(content), 12); > > + self->priv->file_transfers = > > g_hash_table_new_full(g_direct_hash, g_direct_equal, > > + > > g_object_unref, > > + > > (GDestroyNotify)g_free); > > + gtk_dialog_add_button(GTK_DIALOG(self), _("Cancel"), > > GTK_RESPONSE_CANCEL); > > + gtk_dialog_set_default_response(GTK_DIALOG(self), > > + GTK_RESPONSE_CANCEL); > > + g_signal_connect(self, "response", > > G_CALLBACK(dialog_response), NULL); > > + g_signal_connect(self, "delete-event", > > G_CALLBACK(delete_event), NULL); > > +} > > + > > +VirtViewerFileTransferDialog * > > +virt_viewer_file_transfer_dialog_new(GtkWindow *parent) > > +{ > > + return g_object_new(VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG, > > + "title", _("File Transfers"), > > + "transient-for", parent, > > + "resizable", FALSE, > > + NULL); > > +} > > + > > +static void task_progress_notify(GObject *object, > > + GParamSpec *pspec G_GNUC_UNUSED, > > + gpointer user_data) > > +{ > > + VirtViewerFileTransferDialog *self = > > VIRT_VIEWER_FILE_TRANSFER_DIALOG(user_data); > > + SpiceFileTransferTask *task = > > SPICE_FILE_TRANSFER_TASK(object); > > + TaskWidgets *w = g_hash_table_lookup(self->priv > > ->file_transfers, task); > > + g_return_if_fail(w); > > + > > + double pct = spice_file_transfer_task_get_progress(task); > > + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(w->progress), > > pct); > > +} > > + > > +static void task_finished(SpiceFileTransferTask *task, > > + GError *error, > > + gpointer user_data) > > +{ > > + VirtViewerFileTransferDialog *self = > > VIRT_VIEWER_FILE_TRANSFER_DIALOG(user_data); > > + TaskWidgets *w = g_hash_table_lookup(self->priv > > ->file_transfers, task); > > + > > + if (error && !g_error_matches(error, G_IO_ERROR, > > G_IO_ERROR_CANCELLED)) > > + g_warning("File transfer task %p failed: %s", task, error > > ->message); > > + > > + g_return_if_fail(w); > > + > > + gtk_widget_destroy(w->vbox); > > + > > + g_hash_table_remove(self->priv->file_transfers, task); > > + > > + /* if this is the last transfer, close the dialog */ > > + if (!g_hash_table_size(self->priv->file_transfers)) > > + gtk_widget_hide(GTK_WIDGET(self)); > > +} > > + > > +void > > virt_viewer_file_transfer_dialog_add_task(VirtViewerFileTransferDia > > log *self, > > + > > SpiceFileTransferTask *task) > > +{ > > + GtkBox *content = > > GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(self))); > > + TaskWidgets *w = task_widgets_new(task); > > + > > + gtk_box_pack_start(content, > > + w->vbox, > > + TRUE, TRUE, 12); > > + g_hash_table_insert(self->priv->file_transfers, > > g_object_ref(task), w); > > + g_signal_connect(task, "notify::progress", > > G_CALLBACK(task_progress_notify), self); > > + g_signal_connect(task, "finished", G_CALLBACK(task_finished), > > self); > > + > > + gtk_widget_show(GTK_WIDGET(self)); > > +} > > diff --git a/src/virt-viewer-file-transfer-dialog.h b/src/virt > > -viewer-file-transfer-dialog.h > > new file mode 100644 > > index 0000000..c6b1034 > > --- /dev/null > > +++ b/src/virt-viewer-file-transfer-dialog.h > > @@ -0,0 +1,61 @@ > > +/* > > + * Virt Viewer: A virtual machine console viewer > > + * > > + * Copyright (C) 2015 Red Hat, Inc. > > + * > > + * 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 General Public License for more details. > > + * > > + * You should have received a copy of the GNU General Public > > License > > + * along with this program; if not, write to the Free Software > > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 > > -1307 USA > > + */ > > + > > +#ifndef __VIRT_VIEWER_FILE_TRANSFER_DIALOG_H__ > > +#define __VIRT_VIEWER_FILE_TRANSFER_DIALOG_H__ > > + > > +#include <gtk/gtk.h> > > +#include <spice-client.h> > > + > > +G_BEGIN_DECLS > > + > > +#define VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG > > virt_viewer_file_transfer_dialog_get_type() > > + > > +#define VIRT_VIEWER_FILE_TRANSFER_DIALOG(obj) > > (G_TYPE_CHECK_INSTANCE_CAST((obj), > > VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG, > > VirtViewerFileTransferDialog)) > > +#define VIRT_VIEWER_FILE_TRANSFER_DIALOG_CLASS(klass) > > (G_TYPE_CHECK_CLASS_CAST((klass), > > VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG, > > VirtViewerFileTransferDialogClass)) > > +#define VIRT_VIEWER_IS_FILE_TRANSFER_DIALOG(obj) > > (G_TYPE_CHECK_INSTANCE_TYPE((obj), > > VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG)) > > +#define VIRT_VIEWER_IS_FILE_TRANSFER_DIALOG_CLASS(klass) > > (G_TYPE_CHECK_CLASS_TYPE((klass), > > VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG)) > > +#define VIRT_VIEWER_FILE_TRANSFER_DIALOG_GET_CLASS(obj) > > (G_TYPE_INSTANCE_GET_CLASS((obj), > > VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG, > > VirtViewerFileTransferDialogClass)) > > + > > +typedef struct _VirtViewerFileTransferDialog > > VirtViewerFileTransferDialog; > > +typedef struct _VirtViewerFileTransferDialogClass > > VirtViewerFileTransferDialogClass; > > +typedef struct _VirtViewerFileTransferDialogPrivate > > VirtViewerFileTransferDialogPrivate; > > + > > +struct _VirtViewerFileTransferDialog > > +{ > > + GtkDialog parent; > > + > > + VirtViewerFileTransferDialogPrivate *priv; > > +}; > > + > > +struct _VirtViewerFileTransferDialogClass > > +{ > > + GtkDialogClass parent_class; > > +}; > > + > > +GType virt_viewer_file_transfer_dialog_get_type(void) > > G_GNUC_CONST; > > + > > +VirtViewerFileTransferDialog > > *virt_viewer_file_transfer_dialog_new(GtkWindow *parent); > > +void > > virt_viewer_file_transfer_dialog_add_task(VirtViewerFileTransferDia > > log *self, > > + > > SpiceFileTransferTask *task); > > + > > +G_END_DECLS > > + > > +#endif /* __VIRT_VIEWER_FILE_TRANSFER_DIALOG_H__ */ > > diff --git a/src/virt-viewer-session-spice.c b/src/virt-viewer > > -session-spice.c > > index eb0761d..dfb106e 100644 > > --- a/src/virt-viewer-session-spice.c > > +++ b/src/virt-viewer-session-spice.c > > @@ -44,6 +44,10 @@ > > #include "gbinding.c" > > #endif > > > > +#if HAVE_FILE_TRANSFER > > +#include "virt-viewer-file-transfer-dialog.h" > > +#endif > > + > > G_DEFINE_TYPE (VirtViewerSessionSpice, virt_viewer_session_spice, > > VIRT_VIEWER_TYPE_SESSION) > > > > > > @@ -58,6 +62,10 @@ struct _VirtViewerSessionSpicePrivate { > > gboolean has_sw_smartcard_reader; > > guint pass_try; > > gboolean did_auto_conf; > > +#if HAVE_FILE_TRANSFER > > + VirtViewerFileTransferDialog *file_transfer_dialog; > > +#endif > > + > > }; > > > > #define VIRT_VIEWER_SESSION_SPICE_GET_PRIVATE(o) > > (G_TYPE_INSTANCE_GET_PRIVATE((o), VIRT_VIEWER_TYPE_SESSION_SPICE, > > VirtViewerSessionSpicePrivate)) > > @@ -155,6 +163,12 @@ virt_viewer_session_spice_dispose(GObject > > *obj) > > spice->priv->audio = NULL; > > > > g_clear_object(&spice->priv->main_window); > > +#if HAVE_FILE_TRANSFER > > + if (spice->priv->file_transfer_dialog) { > > + gtk_widget_destroy(spice->priv->file_transfer_dialog); > > Please, do: > gtk_widget_destroy(GTK_WIDGET(spice->priv > ->file_transfer_dialog)); > > Otherwise we will have warnings in the build time: > virt-viewer-session-spice.c: In function > 'virt_viewer_session_spice_dispose': > virt-viewer-session-spice.c:168:28: warning: passing argument 1 of > 'gtk_widget_destroy' from incompatible pointer type > [-Wincompatible-pointer-types] > gtk_widget_destroy(spice->priv->file_transfer_dialog); > ^ > In file included from /usr/include/gtk-3.0/gtk/gtkapplication.h:27:0, > from /usr/include/gtk-3.0/gtk/gtkwindow.h:33, > from /usr/include/gtk-3.0/gtk/gtkdialog.h:33, > from /usr/include/gtk-3.0/gtk/gtkaboutdialog.h:30, > from /usr/include/gtk-3.0/gtk/gtk.h:31, > from > /home/ffidenci/local/upstream/include/spice-client-gtk-3.0/usb-device > -widget.h:28, > from virt-viewer-session-spice.c:34: > /usr/include/gtk-3.0/gtk/gtkwidget.h:639:9: note: expected 'GtkWidget > * {aka struct _GtkWidget *}' but argument is of type > 'VirtViewerFileTransferDialog * {aka struct > _VirtViewerFileTransferDialog *}' > void gtk_widget_destroy (GtkWidget *widget); Hmm, I wonder how I overlooked that warning. I'll fix it. > > > + spice->priv->file_transfer_dialog = NULL; > > + } > > +#endif > > > > G_OBJECT_CLASS(virt_viewer_session_spice_parent_class) > > ->dispose(obj); > > } > > @@ -232,6 +246,11 @@ virt_viewer_session_spice_constructed(GObject > > *obj) > > > > G_CALLBACK(update_share_folder), self, > > G_CONNECT_SWAPPED); > > > > +#if HAVE_FILE_TRANSFER > > + self->priv->file_transfer_dialog = > > + virt_viewer_file_transfer_dialog_new(self->priv > > ->main_window); > > +#endif > > + > > G_OBJECT_CLASS(virt_viewer_session_spice_parent_class) > > ->constructed(obj); > > } > > > > @@ -897,6 +916,18 @@ > > virt_viewer_session_spice_display_monitors(SpiceChannel *channel, > > > > } > > > > +#if HAVE_FILE_TRANSFER > > +static void > > +on_new_file_transfer(SpiceMainChannel *channel G_GNUC_UNUSED, > > + SpiceFileTransferTask *task, > > + gpointer user_data) > > +{ > > + VirtViewerSessionSpice *self = > > VIRT_VIEWER_SESSION_SPICE(user_data); > > + virt_viewer_file_transfer_dialog_add_task(self->priv > > ->file_transfer_dialog, > > + task); > > +} > > +#endif > > + > > static void > > virt_viewer_session_spice_channel_new(SpiceSession *s, > > SpiceChannel *channel, > > @@ -929,6 +960,10 @@ > > virt_viewer_session_spice_channel_new(SpiceSession *s, > > > > virt_viewer_signal_connect_object(channel, "notify::agent > > -connected", > > > > G_CALLBACK(agent_connected_changed), self, 0); > > +#if HAVE_FILE_TRANSFER > > + virt_viewer_signal_connect_object(channel, "new-file > > -transfer", > > + > > G_CALLBACK(on_new_file_transfer), self, 0); > > +#endif > > } > > > > if (SPICE_IS_DISPLAY_CHANNEL(channel)) { > > -- > > 2.4.3 > > > > _______________________________________________ > > virt-tools-list mailing list > > virt-tools-list@xxxxxxxxxx > > https://www.redhat.com/mailman/listinfo/virt-tools-list > > > While doing the review of these 2 patches I found a problem with your > spice-gtk patches that I would like to have fixed there before > pushing > this series to virt-viewer. > The problem in spice-gtk is that we don't support folder's drag and > drop, but we still launch the dialog for this situation, ending up > with a "stuck" dialog, showing no progress, bot being able to be > canceled or closed. The test is simple, just try to transfer a folder > and you'll see what happens. Good catch, thanks! I'll fix it. > > Apart from the comments, series look good. > > Best Regards, > -- > Fabiano Fidêncio _______________________________________________ virt-tools-list mailing list virt-tools-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/virt-tools-list