dbus-daemon test: exercise pending fd timeout test
authorSimon McVittie <smcv@debian.org>
Tue, 16 Aug 2016 17:27:43 +0000 (18:27 +0100)
committerSimon McVittie <smcv@debian.org>
Tue, 16 Aug 2016 17:27:43 +0000 (18:27 +0100)
Signed-off-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
configure.ac
test/Makefile.am
test/data/valid-config-files/pending-fd-timeout.conf.in [new file with mode: 0644]
test/dbus-daemon.c

index e029714..460b32b 100644 (file)
@@ -248,7 +248,11 @@ with_glib=yes
 AS_IF([test "x$enable_modular_tests" != xno],
   [
   PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.36, gio-2.0 >= 2.36],
-    [],
+    [
+      AS_IF([test "x$dbus_unix" = xyes],
+        [PKG_CHECK_MODULES([GIO_UNIX], [gio-unix-2.0],
+           [AC_DEFINE([HAVE_GIO_UNIX], [1], [Define if you have gio-unix-2.0])], [])])
+    ],
     [if test "x$enable_modular_tests" = xyes; then
       AC_MSG_NOTICE([Full test coverage (--enable-modular-tests=yes or --enable-tests=yes) requires GLib])
       AC_MSG_ERROR([$GLIB_ERRORS])
index aae3fa7..eaf2df8 100644 (file)
@@ -13,6 +13,7 @@ AM_CPPFLAGS = \
        $(DBUS_STATIC_BUILD_CPPFLAGS) \
        -DDBUS_COMPILATION \
        $(GLIB_CFLAGS) \
+       $(GIO_UNIX_CFLAGS) \
        $(NULL)
 
 # improve backtraces from test stuff
@@ -252,6 +253,7 @@ test_dbus_daemon_SOURCES = dbus-daemon.c
 test_dbus_daemon_LDADD = \
     libdbus-testutils.la \
     $(GLIB_LIBS) \
+    $(GIO_UNIX_LIBS) \
     $(NULL)
 
 test_dbus_daemon_eavesdrop_SOURCES = dbus-daemon-eavesdrop.c
@@ -353,6 +355,7 @@ in_data = \
        data/valid-config-files/max-names-per-connection.conf.in \
        data/valid-config-files/max-replies-per-connection.conf.in \
        data/valid-config-files/multi-user.conf.in \
+       data/valid-config-files/pending-fd-timeout.conf.in \
        data/valid-config-files/systemd-activation.conf.in \
        data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoExec.service.in \
        data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoService.service.in \
diff --git a/test/data/valid-config-files/pending-fd-timeout.conf.in b/test/data/valid-config-files/pending-fd-timeout.conf.in
new file mode 100644 (file)
index 0000000..849d885
--- /dev/null
@@ -0,0 +1,20 @@
+
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+  <!-- Our well-known bus type, don't change this -->
+  <type>session</type>
+  <listen>@TEST_LISTEN@</listen>
+
+  <policy context="default">
+    <!-- Allow everything to be sent -->
+    <allow send_destination="*" eavesdrop="true"/>
+    <!-- Allow everything to be received -->
+    <allow eavesdrop="true"/>
+    <!-- Allow anyone to own anything -->
+    <allow own="*"/>
+  </policy>
+
+  <!-- 500ms -->
+  <limit name="pending_fd_timeout">500</limit>
+</busconfig>
index 0ca6574..5d121ca 100644 (file)
@@ -34,6 +34,7 @@
 
 #include <glib.h>
 #include <glib/gstdio.h>
+#include <gio/gio.h>
 
 #include "test-utils-glib.h"
 
 #ifdef DBUS_UNIX
 # include <unistd.h>
 # include <sys/types.h>
+
+# ifdef HAVE_GIO_UNIX
+    /* The CMake build system doesn't know how to check for this yet */
+#   include <gio/gunixfdmessage.h>
+# endif
 #endif
 
 /* Platforms where we know that credentials-passing passes both the
@@ -913,6 +919,117 @@ test_max_names_per_connection (Fixture *f,
   g_assert_cmpint (ret, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
 }
 
+#if defined(DBUS_UNIX) && defined(HAVE_UNIX_FD_PASSING) && defined(HAVE_GIO_UNIX)
+
+static DBusHandlerResult
+wait_for_disconnected_cb (DBusConnection *client_conn,
+    DBusMessage *message,
+    void *data)
+{
+  gboolean *disconnected = data;
+
+  if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected"))
+    {
+      *disconnected = TRUE;
+      return DBUS_HANDLER_RESULT_HANDLED;
+    }
+
+  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static const guchar partial_message[] =
+{
+  DBUS_LITTLE_ENDIAN,
+  DBUS_MESSAGE_TYPE_METHOD_CALL,
+  0, /* flags */
+  1, /* version */
+  0xff, 0xff, 0, 0, /* length of body = 65535 bytes */
+  1, 2, 3, 4, /* cookie */
+  0xff, 0xff, 0, 0, /* length of header fields array = 65535 bytes */
+  42 /* pretending to be the beginning of the header fields array */
+};
+
+static void
+send_all_with_fd (GSocket *socket,
+                  const guchar *partial_message,
+                  gsize len,
+                  int fd)
+{
+  GSocketControlMessage *fdm = g_unix_fd_message_new ();
+  GError *error = NULL;
+  gssize sent;
+  GOutputVector vector = { partial_message, len };
+
+  g_unix_fd_message_append_fd (G_UNIX_FD_MESSAGE (fdm), fd, &error);
+  g_assert_no_error (error);
+
+  sent = g_socket_send_message (socket, NULL, &vector, 1, &fdm, 1,
+      G_SOCKET_MSG_NONE, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpint (sent, >=, 1);
+  g_assert_cmpint (sent, <=, vector.size);
+
+  while (((gsize) sent) < vector.size)
+    {
+      vector.size -= sent;
+      vector.buffer = ((const guchar *) vector.buffer) + sent;
+      sent = g_socket_send_message (socket, NULL, &vector, 1, NULL, 0,
+          G_SOCKET_MSG_NONE, NULL, &error);
+      g_assert_no_error (error);
+      g_assert_cmpint (sent, >=, 1);
+      g_assert_cmpint (sent, <=, vector.size);
+    }
+
+  g_object_unref (fdm);
+}
+
+static void
+test_pending_fd_timeout (Fixture *f,
+    gconstpointer context)
+{
+  GError *error = NULL;
+  gint64 start;
+  int fd;
+  GSocket *socket;
+  gboolean have_mem;
+  gboolean disconnected = FALSE;
+
+  if (f->skip)
+    return;
+
+  have_mem = dbus_connection_add_filter (f->left_conn, wait_for_disconnected_cb,
+      &disconnected, NULL);
+  g_assert (have_mem);
+
+  /* This is not API. Never do this. */
+
+  if (!dbus_connection_get_socket (f->left_conn, &fd))
+    g_error ("failed to steal fd from left connection");
+
+  socket = g_socket_new_from_fd (fd, &error);
+  g_assert_no_error (error);
+  g_assert (socket != NULL);
+
+  /* We send part of a message that contains a fd, then stop. */
+  start = g_get_monotonic_time ();
+  send_all_with_fd (socket, partial_message, G_N_ELEMENTS (partial_message),
+                    fd);
+
+  while (!disconnected)
+    {
+      test_progress ('.');
+      test_main_context_iterate (f->ctx, TRUE);
+
+      /* It should take no longer than 500ms to get disconnected. We'll
+       * be generous and allow 1000ms. */
+      g_assert_cmpint (g_get_monotonic_time (), <=, start + G_USEC_PER_SEC);
+    }
+
+  g_object_unref (socket);
+}
+
+#endif
+
 static void
 teardown (Fixture *f,
     gconstpointer context G_GNUC_UNUSED)
@@ -1022,6 +1139,13 @@ static Config max_names_per_connection_config = {
     SPECIFY_ADDRESS
 };
 
+#if defined(DBUS_UNIX) && defined(HAVE_UNIX_FD_PASSING) && defined(HAVE_GIO_UNIX)
+static Config pending_fd_timeout_config = {
+    NULL, 1, "valid-config-files/pending-fd-timeout.conf",
+    SPECIFY_ADDRESS
+};
+#endif
+
 int
 main (int argc,
     char **argv)
@@ -1054,6 +1178,13 @@ main (int argc,
   g_test_add ("/limits/max-names-per-connection", Fixture,
       &max_names_per_connection_config,
       setup, test_max_names_per_connection, teardown);
+
+#if defined(DBUS_UNIX) && defined(HAVE_UNIX_FD_PASSING) && defined(HAVE_GIO_UNIX)
+  g_test_add ("/limits/pending-fd-timeout", Fixture,
+      &pending_fd_timeout_config,
+      setup, test_pending_fd_timeout, teardown);
+#endif
+
 #ifdef DBUS_UNIX
   /* We can't test this in loopback.c with the rest of unix:runtime=yes,
    * because dbus_bus_get[_private] is the only way to use the default,