x11: Handle windowing errors while writing selection INCR data

This error was just logged but not raised. Do as the code comment said
and raise a pipe error at that moment, and for subsequent operations
on the output stream (although none besides close() should be expected
after propagating the error properly).

Related: https://gitlab.gnome.org/GNOME/mutter/issues/1065
This commit is contained in:
Carlos Garnacho 2020-02-24 15:33:08 +01:00
parent a13d60aae5
commit 2ecbf6d746

View File

@ -53,6 +53,7 @@ struct _MetaX11SelectionOutputStreamPrivate
guint incr : 1; guint incr : 1;
guint delete_pending : 1; guint delete_pending : 1;
guint pipe_error : 1;
}; };
G_DEFINE_TYPE_WITH_PRIVATE (MetaX11SelectionOutputStream, G_DEFINE_TYPE_WITH_PRIVATE (MetaX11SelectionOutputStream,
@ -167,6 +168,25 @@ get_element_size (int format)
} }
} }
static gboolean
meta_x11_selection_output_stream_check_pipe (MetaX11SelectionOutputStream *stream,
GError **error)
{
MetaX11SelectionOutputStreamPrivate *priv =
meta_x11_selection_output_stream_get_instance_private (stream);
if (priv->pipe_error)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_BROKEN_PIPE,
"Connection with client was broken");
return FALSE;
}
return TRUE;
}
static void static void
meta_x11_selection_output_stream_perform_flush (MetaX11SelectionOutputStream *stream) meta_x11_selection_output_stream_perform_flush (MetaX11SelectionOutputStream *stream)
{ {
@ -174,6 +194,7 @@ meta_x11_selection_output_stream_perform_flush (MetaX11SelectionOutputStream *st
meta_x11_selection_output_stream_get_instance_private (stream); meta_x11_selection_output_stream_get_instance_private (stream);
Display *xdisplay; Display *xdisplay;
size_t element_size, n_elements; size_t element_size, n_elements;
int error_code;
g_assert (!priv->delete_pending); g_assert (!priv->delete_pending);
@ -230,18 +251,28 @@ meta_x11_selection_output_stream_perform_flush (MetaX11SelectionOutputStream *st
g_cond_broadcast (&priv->cond); g_cond_broadcast (&priv->cond);
g_mutex_unlock (&priv->mutex); g_mutex_unlock (&priv->mutex);
/* XXX: handle failure here and report EPIPE for future operations on the stream? */ error_code = meta_x11_error_trap_pop_with_return (priv->x11_display);
if (meta_x11_error_trap_pop_with_return (priv->x11_display))
g_warning ("Failed to flush selection output stream");
if (priv->pending_task) if (error_code != Success)
{
char error_str[100];
XGetErrorText (xdisplay, error_code, error_str, sizeof (error_str));
g_task_return_new_error (priv->pending_task,
G_IO_ERROR,
G_IO_ERROR_BROKEN_PIPE,
"Failed to flush selection output stream: %s",
error_str);
g_clear_object (&priv->pending_task);
priv->pipe_error = TRUE;
}
else if (priv->pending_task)
{ {
size_t result; size_t result;
result = GPOINTER_TO_SIZE (g_task_get_task_data (priv->pending_task)); result = GPOINTER_TO_SIZE (g_task_get_task_data (priv->pending_task));
g_task_return_int (priv->pending_task, result); g_task_return_int (priv->pending_task, result);
g_object_unref (priv->pending_task); g_clear_object (&priv->pending_task);
priv->pending_task = NULL;
} }
} }
@ -270,6 +301,9 @@ meta_x11_selection_output_stream_write (GOutputStream *output_stream,
MetaX11SelectionOutputStreamPrivate *priv = MetaX11SelectionOutputStreamPrivate *priv =
meta_x11_selection_output_stream_get_instance_private (stream); meta_x11_selection_output_stream_get_instance_private (stream);
if (!meta_x11_selection_output_stream_check_pipe (stream, error))
return -1;
g_mutex_lock (&priv->mutex); g_mutex_lock (&priv->mutex);
g_byte_array_append (priv->data, buffer, count); g_byte_array_append (priv->data, buffer, count);
g_mutex_unlock (&priv->mutex); g_mutex_unlock (&priv->mutex);
@ -297,12 +331,19 @@ meta_x11_selection_output_stream_write_async (GOutputStream *output_stream
META_X11_SELECTION_OUTPUT_STREAM (output_stream); META_X11_SELECTION_OUTPUT_STREAM (output_stream);
MetaX11SelectionOutputStreamPrivate *priv = MetaX11SelectionOutputStreamPrivate *priv =
meta_x11_selection_output_stream_get_instance_private (stream); meta_x11_selection_output_stream_get_instance_private (stream);
GError *error = NULL;
GTask *task; GTask *task;
task = g_task_new (stream, cancellable, callback, user_data); task = g_task_new (stream, cancellable, callback, user_data);
g_task_set_source_tag (task, meta_x11_selection_output_stream_write_async); g_task_set_source_tag (task, meta_x11_selection_output_stream_write_async);
g_task_set_priority (task, io_priority); g_task_set_priority (task, io_priority);
if (!meta_x11_selection_output_stream_check_pipe (stream, &error))
{
g_task_return_error (task, error);
return;
}
g_mutex_lock (&priv->mutex); g_mutex_lock (&priv->mutex);
g_byte_array_append (priv->data, buffer, count); g_byte_array_append (priv->data, buffer, count);
g_mutex_unlock (&priv->mutex); g_mutex_unlock (&priv->mutex);
@ -369,6 +410,8 @@ meta_x11_selection_output_stream_flush (GOutputStream *output_stream,
MetaX11SelectionOutputStreamPrivate *priv = MetaX11SelectionOutputStreamPrivate *priv =
meta_x11_selection_output_stream_get_instance_private (stream); meta_x11_selection_output_stream_get_instance_private (stream);
if (!meta_x11_selection_output_stream_check_pipe (stream, error))
return FALSE;
if (!meta_x11_selection_output_request_flush (stream)) if (!meta_x11_selection_output_request_flush (stream))
return TRUE; return TRUE;
@ -394,12 +437,19 @@ meta_x11_selection_output_stream_flush_async (GOutputStream *output_stream
META_X11_SELECTION_OUTPUT_STREAM (output_stream); META_X11_SELECTION_OUTPUT_STREAM (output_stream);
MetaX11SelectionOutputStreamPrivate *priv = MetaX11SelectionOutputStreamPrivate *priv =
meta_x11_selection_output_stream_get_instance_private (stream); meta_x11_selection_output_stream_get_instance_private (stream);
GError *error = NULL;
GTask *task; GTask *task;
task = g_task_new (stream, cancellable, callback, user_data); task = g_task_new (stream, cancellable, callback, user_data);
g_task_set_source_tag (task, meta_x11_selection_output_stream_flush_async); g_task_set_source_tag (task, meta_x11_selection_output_stream_flush_async);
g_task_set_priority (task, io_priority); g_task_set_priority (task, io_priority);
if (!meta_x11_selection_output_stream_check_pipe (stream, &error))
{
g_task_return_error (task, error);
return;
}
if (!meta_x11_selection_output_stream_can_flush (stream)) if (!meta_x11_selection_output_stream_can_flush (stream))
{ {
if (meta_x11_selection_output_request_flush (stream)) if (meta_x11_selection_output_request_flush (stream))