summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2010-04-26 17:11:50 -0400
committerDavid Zeuthen <davidz@redhat.com>2010-04-26 17:11:50 -0400
commit9149eccf89f98e86902ce3487d5dfa38c70d5762 (patch)
treee168630d9a1e74e48dd267e9801a35d472fccc34
parentad152a2affc1ceff9c9a35c1c21355291509e1db (diff)
Add support for passing UNIX file descriptors
-rw-r--r--gdbus/gdbusconnection.c115
-rw-r--r--gdbus/gdbusenums.h2
-rw-r--r--gdbus/gdbusmessage.c47
-rw-r--r--gdbus/gdbusmessage.h4
-rw-r--r--gdbus/gdbusprivate.c264
-rw-r--r--gdbus/tests/peer.c104
6 files changed, 497 insertions, 39 deletions
diff --git a/gdbus/gdbusconnection.c b/gdbus/gdbusconnection.c
index bc6f028..fb5e9d6 100644
--- a/gdbus/gdbusconnection.c
+++ b/gdbus/gdbusconnection.c
@@ -28,6 +28,7 @@
#ifdef G_OS_UNIX
#include <gio/gunixconnection.h>
+#include <gio/gunixfdmessage.h>
#endif
#include <unistd.h>
@@ -819,12 +820,12 @@ static gboolean
g_dbus_connection_write (GDBusConnection *connection,
const gchar *blob,
gssize blob_size,
+ GUnixFDList *fd_list,
GError **error)
{
gboolean ret;
- gsize total_bytes_written;
- gsize remaining;
- gssize bytes_written;
+
+ ret = FALSE;
if (connection->priv->closed)
{
@@ -835,32 +836,93 @@ g_dbus_connection_write (GDBusConnection *connection,
goto out;
}
+ /* TODO: do this in private thread */
+
if (blob_size == -1)
blob_size = strlen (blob);
- ret = FALSE;
- remaining = blob_size;
- total_bytes_written = 0;
- while (remaining > 0)
- {
- /* TODO: do this async in private thread */
- bytes_written = g_output_stream_write (g_io_stream_get_output_stream (connection->priv->stream),
- (const gchar *) blob + total_bytes_written,
- remaining,
- NULL,
- error);
- if (bytes_written == -1)
- {
- g_prefix_error (error, _("Error writing blob of %" G_GSIZE_FORMAT " bytes to socket: "), remaining);
- goto out;
- }
+ g_assert (blob_size > 16);
- /* TODO: hmm, what if zero is returned? the socket is non-blocking so g_socket_send() should block... */
- g_assert (bytes_written != 0);
+ /* First, the initial 16 bytes - special case UNIX sockets here
+ * since it may involve writing an ancillary message
+ */
+#ifdef G_OS_UNIX
+ {
+ GOutputVector vector;
+ GSocketControlMessage *message;
+ GSocket *socket;
+ gssize bytes_written;
- total_bytes_written += bytes_written;
- remaining -= bytes_written;
- }
+ message = NULL;
+ if (fd_list != NULL)
+ {
+ if (!G_IS_UNIX_CONNECTION (connection->priv->stream))
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ "Tried sending a file descriptor on unsupported stream of type %s",
+ g_type_name (G_TYPE_FROM_INSTANCE (connection->priv->stream)));
+ goto out;
+ }
+ else if (!(connection->priv->capabilities & G_DBUS_CONNECTION_CAPABILITY_FLAGS_UNIX_FD_PASSING))
+ {
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ "Tried sending a file descriptor but remote peer does not support this capability");
+ goto out;
+ }
+ message = g_unix_fd_message_new_with_fd_list (fd_list);
+ }
+
+ socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (connection->priv->stream));
+ vector.buffer = blob;
+ vector.size = 16;
+
+ bytes_written = g_socket_send_message (socket,
+ NULL, /* address */
+ &vector,
+ 1,
+ message != NULL ? &message : NULL,
+ message != NULL ? 1 : 0,
+ G_SOCKET_MSG_NONE,
+ NULL, /* cancellable */
+ error);
+ if (bytes_written == -1)
+ {
+ g_prefix_error (error, _("Error writing file descriptors to socket: "));
+ g_object_unref (message);
+ goto out;
+ }
+ if (bytes_written < 16)
+ {
+ /* TODO: I think this needs to be handled ... are we guaranteed that the ancillary
+ * messages are sent?
+ */
+ g_assert_not_reached ();
+ }
+ if (message != NULL)
+ g_object_unref (message);
+ }
+#else
+ if (!g_output_stream_write_all (g_io_stream_get_output_stream (connection->priv->stream),
+ (const gchar *) blob,
+ 16,
+ NULL, /* bytes_written */
+ NULL, /* cancellable */
+ error))
+ goto out;
+#endif
+
+ /* Then write the rest of the message */
+ if (!g_output_stream_write_all (g_io_stream_get_output_stream (connection->priv->stream),
+ (const gchar *) blob + 16,
+ blob_size - 16,
+ NULL, /* bytes_written */
+ NULL, /* cancellable */
+ error))
+ goto out;
ret = TRUE;
@@ -932,6 +994,11 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection,
if (!g_dbus_connection_write (connection,
(const gchar *) blob,
blob_size,
+#ifdef G_OS_UNIX
+ g_dbus_message_get_unix_fd_list (message),
+#else
+ NULL,
+#endif
error))
goto out;
diff --git a/gdbus/gdbusenums.h b/gdbus/gdbusenums.h
index 4d15ed2..ea85995 100644
--- a/gdbus/gdbusenums.h
+++ b/gdbus/gdbusenums.h
@@ -311,6 +311,7 @@ typedef enum {
* @G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION: The name the message is intended for.
* @G_DBUS_MESSAGE_HEADER_FIELD_SENDER: Unique name of the sender of the message (filled in by the bus).
* @G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE: The signature of the message body.
+ * @G_DBUS_MESSAGE_HEADER_FIELD_UNIX_FDS: The number of UNIX file descriptors that accompany the message.
*
* Header fields used in #GDBusMessage.
*/
@@ -324,6 +325,7 @@ typedef enum {
G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION,
G_DBUS_MESSAGE_HEADER_FIELD_SENDER,
G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE,
+ G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS
} GDBusMessageHeaderField;
/**
diff --git a/gdbus/gdbusmessage.c b/gdbus/gdbusmessage.c
index f2fd90c..2bf5f84 100644
--- a/gdbus/gdbusmessage.c
+++ b/gdbus/gdbusmessage.c
@@ -356,7 +356,16 @@ g_dbus_message_set_unix_fd_list (GDBusMessage *message,
g_return_if_fail (G_IS_DBUS_MESSAGE (message));
if (message->priv->fd_list != NULL)
g_object_unref (message->priv->fd_list);
- message->priv->fd_list = fd_list != NULL ? g_object_ref (fd_list) : NULL;
+ if (fd_list != NULL)
+ {
+ message->priv->fd_list = g_object_ref (fd_list);
+ g_dbus_message_set_num_unix_fds (message, g_unix_fd_list_get_length (fd_list));
+ }
+ else
+ {
+ message->priv->fd_list = NULL;
+ g_dbus_message_set_num_unix_fds (message, 0);
+ }
}
#endif
@@ -1246,6 +1255,8 @@ g_dbus_message_to_blob (GDBusMessage *message,
GVariant *header_value;
GVariant *signature;
const gchar *signature_str;
+ gint num_fds_in_message;
+ gint num_fds_according_to_header;
ret = NULL;
@@ -1270,7 +1281,23 @@ g_dbus_message_to_blob (GDBusMessage *message,
g_data_output_stream_put_uint32 (dos, 0xF00DFACE, NULL, NULL);
g_data_output_stream_put_uint32 (dos, message->priv->serial, NULL, NULL);
- /* TODO: check we have all the right header fields etc etc */
+ num_fds_in_message = 0;
+#ifdef G_OS_UNIX
+ if (message->priv->fd_list != NULL)
+ num_fds_in_message = g_unix_fd_list_get_length (message->priv->fd_list);
+#endif
+ num_fds_according_to_header = g_dbus_message_get_num_unix_fds (message);
+ /* TODO: check we have all the right header fields and that they are the correct value etc etc */
+ if (num_fds_in_message != num_fds_according_to_header)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("Message has %d fds but the header field indicates %d fds"),
+ num_fds_in_message,
+ num_fds_according_to_header);
+ goto out;
+ }
builder = g_variant_builder_new (G_VARIANT_TYPE ("a{yv}"));//G_VARIANT_TYPE_ARRAY);
g_hash_table_iter_init (&hash_iter, message->priv->headers);
@@ -1627,3 +1654,19 @@ g_dbus_message_get_arg0 (GDBusMessage *message)
/* ---------------------------------------------------------------------------------------------------- */
+guint32
+g_dbus_message_get_num_unix_fds (GDBusMessage *message)
+{
+ g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), 0);
+ return get_uint32_header (message, G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS);
+}
+
+void
+g_dbus_message_set_num_unix_fds (GDBusMessage *message,
+ guint32 value)
+{
+ g_return_if_fail (G_IS_DBUS_MESSAGE (message));
+ set_uint32_header (message, G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS, value);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/gdbus/gdbusmessage.h b/gdbus/gdbusmessage.h
index 8ef22e4..499d158 100644
--- a/gdbus/gdbusmessage.h
+++ b/gdbus/gdbusmessage.h
@@ -134,6 +134,10 @@ const gchar *g_dbus_message_get_signature (GDBusMessage
void g_dbus_message_set_signature (GDBusMessage *message,
const gchar *value);
+guint32 g_dbus_message_get_num_unix_fds (GDBusMessage *message);
+void g_dbus_message_set_num_unix_fds (GDBusMessage *message,
+ guint32 value);
+
const gchar *g_dbus_message_get_arg0 (GDBusMessage *message);
diff --git a/gdbus/gdbusprivate.c b/gdbus/gdbusprivate.c
index a130b8f..0723a9f 100644
--- a/gdbus/gdbusprivate.c
+++ b/gdbus/gdbusprivate.c
@@ -27,6 +27,12 @@
#include <glib/gi18n.h>
+#ifdef G_OS_UNIX
+#include <gio/gunixconnection.h>
+#include <gio/gunixfdmessage.h>
+#include <unistd.h>
+#endif
+
#include "gdbustypes.h"
#include "gdbusprivate.h"
#include "gdbusmessage.h"
@@ -34,6 +40,144 @@
/* ---------------------------------------------------------------------------------------------------- */
+/* Unfortunately ancillary messages are discarded when reading from a
+ * socket using the GSocketInputStream abstraction. So we provide a
+ * very GInputStream-ish API that uses GSocket in this case (very
+ * similar to GSocketInputStream).
+ */
+
+typedef struct
+{
+ GSocket *socket;
+ GCancellable *cancellable;
+
+ void *buffer;
+ gsize count;
+
+ GSocketControlMessage ***messages;
+ gint *num_messages;
+
+ GSimpleAsyncResult *simple;
+
+ gboolean from_mainloop;
+} ReadWithControlData;
+
+static void
+read_with_control_data_free (ReadWithControlData *data)
+{
+ g_object_unref (data->socket);
+ if (data->cancellable != NULL)
+ g_object_unref (data->cancellable);
+ g_free (data);
+}
+
+static gboolean
+_g_socket_read_with_control_messages_ready (GSocket *socket,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ ReadWithControlData *data = user_data;
+ GError *error;
+ gssize result;
+ GInputVector vector;
+
+ error = NULL;
+ vector.buffer = data->buffer;
+ vector.size = data->count;
+ result = g_socket_receive_message (data->socket,
+ NULL, /* address */
+ &vector,
+ 1,
+ data->messages,
+ data->num_messages,
+ NULL,
+ data->cancellable,
+ &error);
+
+ if (result >= 0)
+ {
+ g_simple_async_result_set_op_res_gssize (data->simple, result);
+ }
+ else
+ {
+ g_assert (error != NULL);
+ g_simple_async_result_set_from_error (data->simple, error);
+ g_error_free (error);
+ }
+
+ if (data->from_mainloop)
+ g_simple_async_result_complete (data->simple);
+ else
+ g_simple_async_result_complete_in_idle (data->simple);
+
+ return FALSE;
+}
+
+static void
+_g_socket_read_with_control_messages (GSocket *socket,
+ void *buffer,
+ gsize count,
+ GSocketControlMessage ***messages,
+ gint *num_messages,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ReadWithControlData *data;
+
+ data = g_new0 (ReadWithControlData, 1);
+ data->socket = g_object_ref (socket);
+ data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL;
+ data->buffer = buffer;
+ data->count = count;
+ data->messages = messages;
+ data->num_messages = num_messages;
+
+ data->simple = g_simple_async_result_new (G_OBJECT (socket),
+ callback,
+ user_data,
+ _g_socket_read_with_control_messages);
+
+ if (!g_socket_condition_check (socket, G_IO_IN))
+ {
+ GSource *source;
+ data->from_mainloop = TRUE;
+ source = g_socket_create_source (data->socket,
+ G_IO_IN | G_IO_HUP | G_IO_ERR,
+ cancellable);
+ g_source_set_callback (source,
+ (GSourceFunc) _g_socket_read_with_control_messages_ready,
+ data,
+ (GDestroyNotify) read_with_control_data_free);
+ g_source_attach (source, g_main_context_get_thread_default ());
+ g_source_unref (source);
+ }
+ else
+ {
+ _g_socket_read_with_control_messages_ready (data->socket, G_IO_IN, data);
+ read_with_control_data_free (data);
+ }
+}
+
+static gssize
+_g_socket_read_with_control_messages_finish (GSocket *socket,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ g_return_val_if_fail (G_IS_SOCKET (socket), -1);
+ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == _g_socket_read_with_control_messages);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return -1;
+ else
+ return g_simple_async_result_get_op_res_gssize (simple);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
G_LOCK_DEFINE_STATIC (shared_thread_lock);
typedef struct
@@ -171,6 +315,13 @@ struct GDBusWorker
gsize read_buffer_allocated_size;
gsize read_buffer_cur_size;
gsize read_buffer_bytes_wanted;
+ GUnixFDList *fd_list;
+
+ GSocketControlMessage **read_ancillary_messages;
+ gint read_num_ancillary_messages;
+
+ /* if not NULL, stream is GSocketConnection */
+ GSocket *socket;
};
static GDBusWorker *
@@ -190,6 +341,8 @@ _g_dbus_worker_unref (GDBusWorker *worker)
g_object_unref (worker->stream);
g_mutex_free (worker->lock);
g_object_unref (worker->cancellable);
+ if (worker->fd_list != NULL)
+ g_object_unref (worker->fd_list);
g_free (worker);
}
}
@@ -211,7 +364,6 @@ _g_dbus_worker_emit_message (GDBusWorker *worker,
worker->message_received_callback (worker, message, worker->user_data);
}
-
static void _g_dbus_worker_do_read_unlocked (GDBusWorker *worker);
/* called in private thread shared by all GDBusConnection instances (without lock held) */
@@ -231,9 +383,75 @@ _g_dbus_worker_do_read_cb (GInputStream *input_stream,
goto out;
error = NULL;
- bytes_read = g_input_stream_read_finish (g_io_stream_get_input_stream (worker->stream),
- res,
- &error);
+ if (worker->socket == NULL)
+ bytes_read = g_input_stream_read_finish (g_io_stream_get_input_stream (worker->stream),
+ res,
+ &error);
+ else
+ bytes_read = _g_socket_read_with_control_messages_finish (worker->socket,
+ res,
+ &error);
+
+ if (worker->read_num_ancillary_messages > 0)
+ {
+ gint n;
+ for (n = 0; n < worker->read_num_ancillary_messages; n++)
+ {
+ GSocketControlMessage *control_message = G_SOCKET_CONTROL_MESSAGE (worker->read_ancillary_messages[n]);
+
+ if (FALSE)
+ {
+ }
+#ifdef G_OS_UNIX
+ else if (G_IS_UNIX_FD_MESSAGE (control_message))
+ {
+ GUnixFDMessage *fd_message;
+ gint *fds;
+ gint num_fds;
+
+ fd_message = G_UNIX_FD_MESSAGE (control_message);
+ fds = g_unix_fd_message_steal_fds (fd_message, &num_fds);
+ if (worker->fd_list == NULL)
+ {
+ worker->fd_list = g_unix_fd_list_new_from_array (fds, num_fds);
+ }
+ else
+ {
+ gint n;
+ for (n = 0; n < num_fds; n++)
+ {
+ /* TODO: really want a append_steal() */
+ g_unix_fd_list_append (worker->fd_list, fds[n], NULL);
+ close (fds[n]);
+ }
+ }
+ g_free (fds);
+ }
+#endif
+ else
+ {
+ if (error == NULL)
+ {
+ g_set_error (&error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Unexpected ancillary message of type %s received from peer",
+ g_type_name (G_TYPE_FROM_INSTANCE (control_message)));
+ _g_dbus_worker_emit_disconnected (worker, TRUE, error);
+ g_error_free (error);
+ g_object_unref (control_message);
+ n++;
+ while (n < worker->read_num_ancillary_messages)
+ g_object_unref (worker->read_ancillary_messages[n++]);
+ g_free (worker->read_ancillary_messages);
+ goto out;
+ }
+ }
+ g_object_unref (control_message);
+ }
+ g_free (worker->read_ancillary_messages);
+ }
+
if (bytes_read == -1)
{
_g_dbus_worker_emit_disconnected (worker, TRUE, error);
@@ -304,6 +522,12 @@ _g_dbus_worker_do_read_cb (GInputStream *input_stream,
goto out;
}
+ if (worker->fd_list != NULL)
+ {
+ g_dbus_message_set_unix_fd_list (message, worker->fd_list);
+ worker->fd_list = NULL;
+ }
+
/* yay, got a message, go deliver it */
_g_dbus_worker_emit_message (worker, message);
g_object_unref (message);
@@ -346,13 +570,28 @@ _g_dbus_worker_do_read_unlocked (GDBusWorker *worker)
worker->read_buffer = g_realloc (worker->read_buffer, worker->read_buffer_allocated_size);
}
- g_input_stream_read_async (g_io_stream_get_input_stream (worker->stream),
- worker->read_buffer + worker->read_buffer_cur_size,
- worker->read_buffer_bytes_wanted - worker->read_buffer_cur_size,
- G_PRIORITY_DEFAULT,
- worker->cancellable,
- (GAsyncReadyCallback) _g_dbus_worker_do_read_cb,
- _g_dbus_worker_ref (worker));
+ if (worker->socket == NULL)
+ g_input_stream_read_async (g_io_stream_get_input_stream (worker->stream),
+ worker->read_buffer + worker->read_buffer_cur_size,
+ worker->read_buffer_bytes_wanted - worker->read_buffer_cur_size,
+ G_PRIORITY_DEFAULT,
+ worker->cancellable,
+ (GAsyncReadyCallback) _g_dbus_worker_do_read_cb,
+ _g_dbus_worker_ref (worker));
+ else
+ {
+ worker->read_ancillary_messages = NULL;
+ worker->read_num_ancillary_messages = 0;
+ _g_socket_read_with_control_messages (worker->socket,
+ worker->read_buffer + worker->read_buffer_cur_size,
+ worker->read_buffer_bytes_wanted - worker->read_buffer_cur_size,
+ &worker->read_ancillary_messages,
+ &worker->read_num_ancillary_messages,
+ G_PRIORITY_DEFAULT,
+ worker->cancellable,
+ (GAsyncReadyCallback) _g_dbus_worker_do_read_cb,
+ _g_dbus_worker_ref (worker));
+ }
}
/* called in private thread shared by all GDBusConnection instances (without lock held) */
@@ -397,6 +636,9 @@ _g_dbus_worker_new (GIOStream *stream,
worker->stream = g_object_ref (stream);
worker->cancellable = g_cancellable_new ();
+ if (G_IS_SOCKET_CONNECTION (worker->stream))
+ worker->socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (worker->stream));
+
_g_dbus_shared_thread_ref (_g_dbus_worker_thread_begin_func, worker);
return worker;
diff --git a/gdbus/tests/peer.c b/gdbus/tests/peer.c
index b1fa559..610bb31 100644
--- a/gdbus/tests/peer.c
+++ b/gdbus/tests/peer.c
@@ -24,6 +24,11 @@
#include <unistd.h>
#include <string.h>
+/* for open(2) */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
#include <gio/gunixsocketaddress.h>
#include "tests.h"
@@ -60,6 +65,11 @@ static const GDBusArgInfo test_interface_hello_peer_method_out_args[] =
{"response", "s", NULL}
};
+static const GDBusArgInfo test_interface_open_file_method_in_args[] =
+{
+ {"path", "s", NULL}
+};
+
static const GDBusMethodInfo test_interface_method_info[] =
{
{
@@ -73,6 +83,12 @@ static const GDBusMethodInfo test_interface_method_info[] =
"", 0, NULL,
"", 0, NULL,
NULL
+ },
+ {
+ "OpenFile",
+ "s", 1, test_interface_open_file_method_in_args,
+ "", 0, NULL,
+ NULL
}
};
@@ -102,7 +118,7 @@ static const GDBusPropertyInfo test_interface_property_info[] =
static const GDBusInterfaceInfo test_interface_introspection_data =
{
"org.gtk.GDBus.PeerTestInterface",
- 2, test_interface_method_info,
+ 3, test_interface_method_info,
1, test_interface_signal_info,
1, test_interface_property_info,
NULL,
@@ -153,6 +169,37 @@ test_interface_method_call (GDBusConnection *connection,
g_assert_no_error (error);
g_dbus_method_invocation_return_value (invocation, NULL);
}
+ else if (g_strcmp0 (method_name, "OpenFile") == 0)
+ {
+ const gchar *path;
+ GDBusMessage *reply;
+ GError *error;
+ gint fd;
+ GUnixFDList *fd_list;
+
+ g_variant_get (parameters, "(s)", &path);
+
+ fd_list = g_unix_fd_list_new ();
+
+ error = NULL;
+
+ fd = open (path, O_RDONLY);
+ g_unix_fd_list_append (fd_list, fd, &error);
+ g_assert_no_error (error);
+ close (fd);
+
+ reply = g_dbus_message_new_method_reply (g_dbus_method_invocation_get_message (invocation));
+ g_dbus_message_set_unix_fd_list (reply, fd_list);
+ g_object_unref (invocation);
+
+ error = NULL;
+ g_dbus_connection_send_message (connection,
+ reply,
+ NULL, /* out_serial */
+ &error);
+ g_assert_no_error (error);
+ g_object_unref (reply);
+ }
else
{
g_assert_not_reached ();
@@ -464,6 +511,59 @@ test_peer (void)
g_assert (data.signal_received);
g_assert_cmpint (data.num_method_calls, ==, 2);
+ /* check for UNIX fd passing */
+#ifdef G_OS_UNIX
+ {
+ GDBusMessage *method_call_message;
+ GDBusMessage *method_reply_message;
+ GUnixFDList *fd_list;
+ gint fd;
+ gchar buf[1024];
+ gssize len;
+ gchar *buf2;
+ gsize len2;
+
+ 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)", "/etc/hosts"));
+ error = NULL;
+ method_reply_message = g_dbus_connection_send_message_with_reply_sync (c,
+ method_call_message,
+ -1,
+ NULL, /* out_serial */
+ NULL, /* cancellable */
+ &error);
+ g_assert_no_error (error);
+ g_assert (g_dbus_message_get_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);
+
+ memset (buf, '\0', sizeof (buf));
+ len = read (fd, buf, sizeof (buf) - 1);
+ close (fd);
+
+ error = NULL;
+ g_file_get_contents ("/etc/hosts",
+ &buf2,
+ &len2,
+ &error);
+ g_assert_no_error (error);
+ if (len2 > sizeof (buf))
+ buf2[sizeof (buf)] = '\0';
+ g_assert_cmpstr (buf, ==, buf2);
+ g_free (buf2);
+ }
+#endif /* G_OS_UNIX */
+
+
/* bring up a connection - don't accept it - this should fail.
*/
data.accept_connection = FALSE;
@@ -529,7 +629,7 @@ 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, ==, 3);
+ g_assert_cmpint (data.num_method_calls, ==, 4);
#if 0
/* TODO: THIS TEST DOESN'T WORK YET */