diff options
author | Simon McVittie <smcv@collabora.com> | 2020-10-28 11:05:39 +0000 |
---|---|---|
committer | Simon McVittie <smcv@collabora.com> | 2020-10-28 12:03:59 +0000 |
commit | e5cee9ce5acab2d4e51f3f461e726dd8f373a873 (patch) | |
tree | c287d5d65261a9aa4f8fd75b850c5716fed81324 /gio | |
parent | 70279f84463e799eb0d98c9cb2026fbea4fc75e8 (diff) | |
download | glib-e5cee9ce5acab2d4e51f3f461e726dd8f373a873.tar.gz |
gio/tests/gdbus-peer: Exercise fds attached to a large messagewip/smcv/big-dbus-write-with-fds
This incidentally also exercises the intended pattern for sending fds in
a D-Bus message: the fd list is meant to contain exactly those fds that
are referenced by a handle (type 'h') in the body of the message, with
numeric handle value n corresponding to g_unix_fd_list_peek_fds(...)[n].
Being able to send and receive file descriptors that are not referenced by
a handle (as in OpenFile here) is a quirk of the GDBus API, and while it's
entirely possible in the wire protocol, other D-Bus implementations like
libdbus and sd-bus typically don't provide APIs that make this possible.
Reproduces: https://gitlab.gnome.org/GNOME/glib/-/issues/2074
Signed-off-by: Simon McVittie <smcv@collabora.com>
Diffstat (limited to 'gio')
-rw-r--r-- | gio/tests/gdbus-peer.c | 199 |
1 files changed, 135 insertions, 64 deletions
diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index ca53528d2..23a22981a 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -76,6 +76,12 @@ typedef struct gboolean signal_received; } PeerData; +/* This needs to be enough to usually take more than one write(), + * to reproduce + * <https://gitlab.gnome.org/GNOME/glib/-/issues/2074>. + * 1 MiB ought to be enough. */ +#define BIG_MESSAGE_ARRAY_SIZE (1024 * 1024) + static const gchar *test_interface_introspection_xml = "<node>" " <interface name='org.gtk.GDBus.PeerTestInterface'>" @@ -88,6 +94,11 @@ static const gchar *test_interface_introspection_xml = " <method name='OpenFile'>" " <arg type='s' name='path' direction='in'/>" " </method>" + " <method name='OpenFileWithBigMessage'>" + " <arg type='s' name='path' direction='in'/>" + " <arg type='h' name='handle' direction='out'/>" + " <arg type='ay' name='junk' direction='out'/>" + " </method>" " <signal name='PeerSignal'>" " <arg type='s' name='a_string'/>" " </signal>" @@ -164,7 +175,8 @@ test_interface_method_call (GDBusConnection *connection, g_dbus_method_invocation_return_value (invocation, NULL); } - else if (g_strcmp0 (method_name, "OpenFile") == 0) + else if (g_strcmp0 (method_name, "OpenFile") == 0 || + g_strcmp0 (method_name, "OpenFileWithBigMessage") == 0) { #ifdef G_OS_UNIX const gchar *path; @@ -190,6 +202,21 @@ test_interface_method_call (GDBusConnection *connection, g_object_unref (fd_list); g_object_unref (invocation); + if (g_strcmp0 (method_name, "OpenFileWithBigMessage") == 0) + { + char *junk; + + junk = g_new0 (char, BIG_MESSAGE_ARRAY_SIZE); + g_dbus_message_set_body (reply, + g_variant_new ("(h@ay)", + 0, + g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, + junk, + BIG_MESSAGE_ARRAY_SIZE, + 1))); + g_free (junk); + } + error = NULL; g_dbus_connection_send_message (connection, reply, @@ -723,6 +750,7 @@ do_test_peer (void) const gchar *s; GThread *service_thread; gulong signal_handler_id; + gsize i; memset (&data, '\0', sizeof (PeerData)); data.current_connections = g_ptr_array_new_with_free_func (g_object_unref); @@ -843,73 +871,116 @@ do_test_peer (void) g_assert_cmpint (data.num_method_calls, ==, 3); g_signal_handler_disconnect (proxy, signal_handler_id); - /* check for UNIX fd passing */ + /* + * Check for UNIX fd passing. + * + * The first time through, we use a very simple method call. Note that + * because this does not have a G_VARIANT_TYPE_HANDLE in the message body + * to refer to the fd, it is a GDBus-specific idiom that would not + * interoperate with libdbus or sd-bus + * (see <https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1726>). + * + * The second time, we call a method that returns a fd attached to a + * large message, to reproduce + * <https://gitlab.gnome.org/GNOME/glib/-/issues/2074>. It also happens + * to follow the more usual pattern for D-Bus messages containing a + * G_VARIANT_TYPE_HANDLE to refer to attached fds. + */ + for (i = 0; i < 2; i++) + { #ifdef G_OS_UNIX - { - GDBusMessage *method_call_message; - GDBusMessage *method_reply_message; - GUnixFDList *fd_list; - gint fd; - gchar *buf; - gsize len; - gchar *buf2; - gsize len2; - const char *testfile = g_test_get_filename (G_TEST_DIST, "file.c", NULL); - - method_call_message = g_dbus_message_new_method_call (NULL, /* name */ - "/org/gtk/GDBus/PeerTestObject", - "org.gtk.GDBus.PeerTestInterface", - "OpenFile"); - g_dbus_message_set_body (method_call_message, g_variant_new ("(s)", testfile)); - error = NULL; - method_reply_message = g_dbus_connection_send_message_with_reply_sync (c, - method_call_message, - G_DBUS_SEND_MESSAGE_FLAGS_NONE, - -1, - NULL, /* out_serial */ - NULL, /* cancellable */ - &error); - g_assert_no_error (error); - g_assert (g_dbus_message_get_message_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_METHOD_RETURN); - fd_list = g_dbus_message_get_unix_fd_list (method_reply_message); - g_assert (fd_list != NULL); - g_assert_cmpint (g_unix_fd_list_get_length (fd_list), ==, 1); - error = NULL; - fd = g_unix_fd_list_get (fd_list, 0, &error); - g_assert_no_error (error); - g_object_unref (method_call_message); - g_object_unref (method_reply_message); + GDBusMessage *method_call_message; + GDBusMessage *method_reply_message; + GUnixFDList *fd_list; + gint fd; + gchar *buf; + gsize len; + gchar *buf2; + gsize len2; + const char *testfile = g_test_get_filename (G_TEST_DIST, "file.c", NULL); + const char *method = "OpenFile"; + GVariant *body; + + if (i == 1) + method = "OpenFileWithBigMessage"; + + method_call_message = g_dbus_message_new_method_call (NULL, /* name */ + "/org/gtk/GDBus/PeerTestObject", + "org.gtk.GDBus.PeerTestInterface", + method); + g_dbus_message_set_body (method_call_message, g_variant_new ("(s)", testfile)); + error = NULL; + method_reply_message = g_dbus_connection_send_message_with_reply_sync (c, + method_call_message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + -1, + NULL, /* out_serial */ + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + g_assert (g_dbus_message_get_message_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_METHOD_RETURN); - error = NULL; - len = 0; - buf = read_all_from_fd (fd, &len, &error); - g_assert_no_error (error); - g_assert (buf != NULL); - close (fd); + body = g_dbus_message_get_body (method_reply_message); - error = NULL; - g_file_get_contents (testfile, - &buf2, - &len2, - &error); - g_assert_no_error (error); - g_assert_cmpmem (buf, len, buf2, len2); - g_free (buf2); - g_free (buf); - } + if (i == 1) + { + gint32 handle = -1; + GVariant *junk = NULL; + + g_assert_cmpstr (g_variant_get_type_string (body), ==, "(hay)"); + g_variant_get (body, "(h@ay)", &handle, &junk); + g_assert_cmpint (handle, ==, 0); + g_assert_cmpuint (g_variant_n_children (junk), ==, BIG_MESSAGE_ARRAY_SIZE); + g_variant_unref (junk); + } + else + { + g_assert_null (body); + } + + fd_list = g_dbus_message_get_unix_fd_list (method_reply_message); + g_assert (fd_list != NULL); + g_assert_cmpint (g_unix_fd_list_get_length (fd_list), ==, 1); + error = NULL; + fd = g_unix_fd_list_get (fd_list, 0, &error); + g_assert_no_error (error); + g_object_unref (method_call_message); + g_object_unref (method_reply_message); + + error = NULL; + len = 0; + buf = read_all_from_fd (fd, &len, &error); + g_assert_no_error (error); + g_assert (buf != NULL); + close (fd); + + error = NULL; + g_file_get_contents (testfile, + &buf2, + &len2, + &error); + g_assert_no_error (error); + g_assert_cmpmem (buf, len, buf2, len2); + g_free (buf2); + g_free (buf); #else - error = NULL; - result = g_dbus_proxy_call_sync (proxy, - "OpenFile", - g_variant_new ("(s)", "boo"), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, /* GCancellable */ - &error); - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); - g_assert (result == NULL); - g_error_free (error); + /* We do the same number of iterations on non-Unix, so that + * the method call count will match. In this case we use + * OpenFile both times, because the difference between this + * and OpenFileWithBigMessage is only relevant on Unix. */ + error = NULL; + result = g_dbus_proxy_call_sync (proxy, + "OpenFile", + g_variant_new ("(s)", "boo"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); + g_assert (result == NULL); + g_error_free (error); #endif /* G_OS_UNIX */ + } /* Check that g_socket_get_credentials() work - (though this really * should be in socket.c) @@ -1017,7 +1088,7 @@ do_test_peer (void) g_variant_get (result, "(&s)", &s); g_assert_cmpstr (s, ==, "You greeted me with 'Hey Again Peer!'."); g_variant_unref (result); - g_assert_cmpint (data.num_method_calls, ==, 5); + g_assert_cmpint (data.num_method_calls, ==, 6); #if 0 /* TODO: THIS TEST DOESN'T WORK YET */ |