mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-26 15:36:14 +01:00
7eba41346e
- Make GCredentials instance and class structures private so it can't be subclassed and we don't have to worry about ABI compat issues. This also allows us to get rid of the GCredentialsPrivate struct. - Add a GCredentialsType enumeration that is used whenever exchanging pointers with the user. This allows us to support OSes with multiple native credential types. In particular, it allows supporting OSes where the native credential evolves or even changes over time. - Add g_socket_get_credentials() method. - Add tests for g_socket_get_credentials(). Right now this is in the GDBus peer-to-peer test case but we can change that later. - Move GTcpConnection into a separate gtk-doc page as was already half-done with GUnixConnection. Also finish the GUnixConnection move and ensure send_credentials() and receive_credentials() methods are in the docs. Also nuke comment about GTcpConnection being empty compared to its superclass. Signed-off-by: David Zeuthen <davidz@redhat.com>
1255 lines
43 KiB
C
1255 lines
43 KiB
C
/* GLib testing framework examples and tests
|
|
*
|
|
* Copyright (C) 2008-2010 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General
|
|
* Public License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*
|
|
* Author: David Zeuthen <davidz@redhat.com>
|
|
*/
|
|
|
|
#include <gio/gio.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
|
|
/* for open(2) */
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
/* for g_unlink() */
|
|
#include <glib/gstdio.h>
|
|
|
|
#include <gio/gunixsocketaddress.h>
|
|
#include <gio/gunixfdlist.h>
|
|
|
|
/* for struct ucred */
|
|
#ifdef __linux__
|
|
#define __USE_GNU
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#endif
|
|
|
|
#include "gdbus-tests.h"
|
|
|
|
|
|
#ifdef G_OS_UNIX
|
|
static gboolean is_unix = TRUE;
|
|
#else
|
|
static gboolean is_unix = FALSE;
|
|
#endif
|
|
|
|
static gchar *test_guid = NULL;
|
|
static GMainLoop *service_loop = NULL;
|
|
static GDBusServer *server = NULL;
|
|
static GMainLoop *loop = NULL;
|
|
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
/* Test that peer-to-peer connections work */
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
|
|
|
|
typedef struct
|
|
{
|
|
gboolean accept_connection;
|
|
gint num_connection_attempts;
|
|
GPtrArray *current_connections;
|
|
guint num_method_calls;
|
|
gboolean signal_received;
|
|
} PeerData;
|
|
|
|
static const gchar *test_interface_introspection_xml =
|
|
"<node>"
|
|
" <interface name='org.gtk.GDBus.PeerTestInterface'>"
|
|
" <method name='HelloPeer'>"
|
|
" <arg type='s' name='greeting' direction='in'/>"
|
|
" <arg type='s' name='response' direction='out'/>"
|
|
" </method>"
|
|
" <method name='EmitSignal'/>"
|
|
" <method name='EmitSignalWithNameSet'/>"
|
|
" <method name='OpenFile'>"
|
|
" <arg type='s' name='path' direction='in'/>"
|
|
" </method>"
|
|
" <signal name='PeerSignal'>"
|
|
" <arg type='s' name='a_string'/>"
|
|
" </signal>"
|
|
" <property type='s' name='PeerProperty' access='read'/>"
|
|
" </interface>"
|
|
"</node>";
|
|
static GDBusInterfaceInfo *test_interface_introspection_data = NULL;
|
|
|
|
static void
|
|
test_interface_method_call (GDBusConnection *connection,
|
|
const gchar *sender,
|
|
const gchar *object_path,
|
|
const gchar *interface_name,
|
|
const gchar *method_name,
|
|
GVariant *parameters,
|
|
GDBusMethodInvocation *invocation,
|
|
gpointer user_data)
|
|
{
|
|
PeerData *data = user_data;
|
|
|
|
data->num_method_calls++;
|
|
|
|
g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject");
|
|
g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface");
|
|
|
|
if (g_strcmp0 (method_name, "HelloPeer") == 0)
|
|
{
|
|
const gchar *greeting;
|
|
gchar *response;
|
|
|
|
g_variant_get (parameters, "(&s)", &greeting);
|
|
|
|
response = g_strdup_printf ("You greeted me with '%s'.",
|
|
greeting);
|
|
g_dbus_method_invocation_return_value (invocation,
|
|
g_variant_new ("(s)", response));
|
|
g_free (response);
|
|
}
|
|
else if (g_strcmp0 (method_name, "EmitSignal") == 0)
|
|
{
|
|
GError *error;
|
|
|
|
error = NULL;
|
|
g_dbus_connection_emit_signal (connection,
|
|
NULL,
|
|
"/org/gtk/GDBus/PeerTestObject",
|
|
"org.gtk.GDBus.PeerTestInterface",
|
|
"PeerSignal",
|
|
NULL,
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_dbus_method_invocation_return_value (invocation, NULL);
|
|
}
|
|
else if (g_strcmp0 (method_name, "EmitSignalWithNameSet") == 0)
|
|
{
|
|
GError *error;
|
|
gboolean ret;
|
|
GDBusMessage *message;
|
|
|
|
message = g_dbus_message_new_signal ("/org/gtk/GDBus/PeerTestObject",
|
|
"org.gtk.GDBus.PeerTestInterface",
|
|
"PeerSignalWithNameSet");
|
|
g_dbus_message_set_sender (message, ":1.42");
|
|
|
|
error = NULL;
|
|
ret = g_dbus_connection_send_message (connection, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
g_object_unref (message);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, NULL);
|
|
}
|
|
else if (g_strcmp0 (method_name, "OpenFile") == 0)
|
|
{
|
|
#ifdef G_OS_UNIX
|
|
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,
|
|
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
|
|
NULL, /* out_serial */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_object_unref (reply);
|
|
#else
|
|
g_dbus_method_invocation_return_dbus_error (invocation,
|
|
"org.gtk.GDBus.NotOnUnix",
|
|
"Your OS does not support file descriptor passing");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static GVariant *
|
|
test_interface_get_property (GDBusConnection *connection,
|
|
const gchar *sender,
|
|
const gchar *object_path,
|
|
const gchar *interface_name,
|
|
const gchar *property_name,
|
|
GError **error,
|
|
gpointer user_data)
|
|
{
|
|
g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject");
|
|
g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface");
|
|
g_assert_cmpstr (property_name, ==, "PeerProperty");
|
|
|
|
return g_variant_new_string ("ThePropertyValue");
|
|
}
|
|
|
|
|
|
static const GDBusInterfaceVTable test_interface_vtable =
|
|
{
|
|
test_interface_method_call,
|
|
test_interface_get_property,
|
|
NULL /* set_property */
|
|
};
|
|
|
|
static void
|
|
on_proxy_signal_received (GDBusProxy *proxy,
|
|
gchar *sender_name,
|
|
gchar *signal_name,
|
|
GVariant *parameters,
|
|
gpointer user_data)
|
|
{
|
|
PeerData *data = user_data;
|
|
|
|
data->signal_received = TRUE;
|
|
|
|
g_assert (sender_name == NULL);
|
|
g_assert_cmpstr (signal_name, ==, "PeerSignal");
|
|
g_main_loop_quit (loop);
|
|
}
|
|
|
|
static void
|
|
on_proxy_signal_received_with_name_set (GDBusProxy *proxy,
|
|
gchar *sender_name,
|
|
gchar *signal_name,
|
|
GVariant *parameters,
|
|
gpointer user_data)
|
|
{
|
|
PeerData *data = user_data;
|
|
|
|
data->signal_received = TRUE;
|
|
|
|
g_assert_cmpstr (sender_name, ==, ":1.42");
|
|
g_assert_cmpstr (signal_name, ==, "PeerSignalWithNameSet");
|
|
g_main_loop_quit (loop);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
|
|
static gboolean
|
|
on_authorize_authenticated_peer (GDBusAuthObserver *observer,
|
|
GIOStream *stream,
|
|
GCredentials *credentials,
|
|
gpointer user_data)
|
|
{
|
|
PeerData *data = user_data;
|
|
gboolean authorized;
|
|
|
|
data->num_connection_attempts++;
|
|
|
|
authorized = TRUE;
|
|
if (!data->accept_connection)
|
|
{
|
|
authorized = FALSE;
|
|
g_main_loop_quit (loop);
|
|
}
|
|
|
|
return authorized;
|
|
}
|
|
|
|
/* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */
|
|
static void
|
|
on_new_connection (GDBusServer *server,
|
|
GDBusConnection *connection,
|
|
gpointer user_data)
|
|
{
|
|
PeerData *data = user_data;
|
|
GError *error;
|
|
guint reg_id;
|
|
|
|
//g_print ("Client connected.\n"
|
|
// "Negotiated capabilities: unix-fd-passing=%d\n",
|
|
// g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING);
|
|
|
|
g_ptr_array_add (data->current_connections, g_object_ref (connection));
|
|
|
|
/* export object on the newly established connection */
|
|
error = NULL;
|
|
reg_id = g_dbus_connection_register_object (connection,
|
|
"/org/gtk/GDBus/PeerTestObject",
|
|
test_interface_introspection_data,
|
|
&test_interface_vtable,
|
|
data,
|
|
NULL, /* GDestroyNotify for data */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_assert (reg_id > 0);
|
|
|
|
g_main_loop_quit (loop);
|
|
}
|
|
|
|
static gpointer
|
|
service_thread_func (gpointer user_data)
|
|
{
|
|
PeerData *data = user_data;
|
|
GMainContext *service_context;
|
|
GDBusAuthObserver *observer;
|
|
GError *error;
|
|
|
|
service_context = g_main_context_new ();
|
|
g_main_context_push_thread_default (service_context);
|
|
|
|
error = NULL;
|
|
observer = g_dbus_auth_observer_new ();
|
|
server = g_dbus_server_new_sync (is_unix ? "unix:tmpdir=/tmp/gdbus-test-" : "nonce-tcp:",
|
|
G_DBUS_SERVER_FLAGS_NONE,
|
|
test_guid,
|
|
observer,
|
|
NULL, /* cancellable */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
|
|
g_signal_connect (server,
|
|
"new-connection",
|
|
G_CALLBACK (on_new_connection),
|
|
data);
|
|
g_signal_connect (observer,
|
|
"authorize-authenticated-peer",
|
|
G_CALLBACK (on_authorize_authenticated_peer),
|
|
data);
|
|
g_object_unref (observer);
|
|
|
|
g_dbus_server_start (server);
|
|
|
|
service_loop = g_main_loop_new (service_context, FALSE);
|
|
g_main_loop_run (service_loop);
|
|
|
|
g_main_context_pop_thread_default (service_context);
|
|
|
|
g_main_loop_unref (service_loop);
|
|
g_main_context_unref (service_context);
|
|
|
|
/* test code specifically unrefs the server - see below */
|
|
g_assert (server == NULL);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#if 0
|
|
static gboolean
|
|
on_incoming_connection (GSocketService *service,
|
|
GSocketConnection *socket_connection,
|
|
GObject *source_object,
|
|
gpointer user_data)
|
|
{
|
|
PeerData *data = user_data;
|
|
|
|
if (data->accept_connection)
|
|
{
|
|
GError *error;
|
|
guint reg_id;
|
|
GDBusConnection *connection;
|
|
|
|
error = NULL;
|
|
connection = g_dbus_connection_new_sync (G_IO_STREAM (socket_connection),
|
|
test_guid,
|
|
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,
|
|
NULL, /* cancellable */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
|
|
g_ptr_array_add (data->current_connections, connection);
|
|
|
|
/* export object on the newly established connection */
|
|
error = NULL;
|
|
reg_id = g_dbus_connection_register_object (connection,
|
|
"/org/gtk/GDBus/PeerTestObject",
|
|
&test_interface_introspection_data,
|
|
&test_interface_vtable,
|
|
data,
|
|
NULL, /* GDestroyNotify for data */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_assert (reg_id > 0);
|
|
|
|
}
|
|
else
|
|
{
|
|
/* don't do anything */
|
|
}
|
|
|
|
data->num_connection_attempts++;
|
|
|
|
g_main_loop_quit (loop);
|
|
|
|
/* stops other signal handlers from being invoked */
|
|
return TRUE;
|
|
}
|
|
|
|
static gpointer
|
|
service_thread_func (gpointer data)
|
|
{
|
|
GMainContext *service_context;
|
|
gchar *socket_path;
|
|
GSocketAddress *address;
|
|
GError *error;
|
|
|
|
service_context = g_main_context_new ();
|
|
g_main_context_push_thread_default (service_context);
|
|
|
|
socket_path = g_strdup_printf ("/tmp/gdbus-test-pid-%d", getpid ());
|
|
address = g_unix_socket_address_new (socket_path);
|
|
|
|
service = g_socket_service_new ();
|
|
error = NULL;
|
|
g_socket_listener_add_address (G_SOCKET_LISTENER (service),
|
|
address,
|
|
G_SOCKET_TYPE_STREAM,
|
|
G_SOCKET_PROTOCOL_DEFAULT,
|
|
NULL, /* source_object */
|
|
NULL, /* effective_address */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_signal_connect (service,
|
|
"incoming",
|
|
G_CALLBACK (on_incoming_connection),
|
|
data);
|
|
g_socket_service_start (service);
|
|
|
|
service_loop = g_main_loop_new (service_context, FALSE);
|
|
g_main_loop_run (service_loop);
|
|
|
|
g_main_context_pop_thread_default (service_context);
|
|
|
|
g_main_loop_unref (service_loop);
|
|
g_main_context_unref (service_context);
|
|
|
|
g_object_unref (address);
|
|
g_free (socket_path);
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
|
|
#if 0
|
|
static gboolean
|
|
check_connection (gpointer user_data)
|
|
{
|
|
PeerData *data = user_data;
|
|
guint n;
|
|
|
|
for (n = 0; n < data->current_connections->len; n++)
|
|
{
|
|
GDBusConnection *c;
|
|
GIOStream *stream;
|
|
|
|
c = G_DBUS_CONNECTION (data->current_connections->pdata[n]);
|
|
stream = g_dbus_connection_get_stream (c);
|
|
|
|
g_debug ("In check_connection for %d: connection %p, stream %p", n, c, stream);
|
|
g_debug ("closed = %d", g_io_stream_is_closed (stream));
|
|
|
|
GSocket *socket;
|
|
socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream));
|
|
g_debug ("socket_closed = %d", g_socket_is_closed (socket));
|
|
g_debug ("socket_condition_check = %d", g_socket_condition_check (socket, G_IO_IN|G_IO_OUT|G_IO_ERR|G_IO_HUP));
|
|
|
|
gchar buf[128];
|
|
GError *error;
|
|
gssize num_read;
|
|
error = NULL;
|
|
num_read = g_input_stream_read (g_io_stream_get_input_stream (stream),
|
|
buf,
|
|
128,
|
|
NULL,
|
|
&error);
|
|
if (num_read < 0)
|
|
{
|
|
g_debug ("error: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
else
|
|
{
|
|
g_debug ("no error, read %d bytes", (gint) num_read);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
on_do_disconnect_in_idle (gpointer data)
|
|
{
|
|
GDBusConnection *c = G_DBUS_CONNECTION (data);
|
|
g_debug ("GDC %p has ref_count %d", c, G_OBJECT (c)->ref_count);
|
|
g_dbus_connection_disconnect (c);
|
|
g_object_unref (c);
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
test_peer (void)
|
|
{
|
|
GDBusConnection *c;
|
|
GDBusConnection *c2;
|
|
GDBusProxy *proxy;
|
|
GError *error;
|
|
PeerData data;
|
|
GVariant *value;
|
|
GVariant *result;
|
|
const gchar *s;
|
|
GThread *service_thread;
|
|
gulong signal_handler_id;
|
|
|
|
memset (&data, '\0', sizeof (PeerData));
|
|
data.current_connections = g_ptr_array_new_with_free_func (g_object_unref);
|
|
|
|
/* first try to connect when there is no server */
|
|
error = NULL;
|
|
c = g_dbus_connection_new_for_address_sync (is_unix ? "unix:path=/tmp/gdbus-test-does-not-exist-pid" :
|
|
/* NOTE: Even if something is listening on port 12345 the connection
|
|
* will fail because the nonce file doesn't exist */
|
|
"nonce-tcp:host=localhost,port=12345,noncefile=this-does-not-exist-gdbus",
|
|
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
|
|
NULL, /* GDBusAuthObserver */
|
|
NULL, /* cancellable */
|
|
&error);
|
|
_g_assert_error_domain (error, G_IO_ERROR);
|
|
g_assert (!g_dbus_error_is_remote_error (error));
|
|
g_clear_error (&error);
|
|
g_assert (c == NULL);
|
|
|
|
/* bring up a server - we run the server in a different thread to avoid deadlocks */
|
|
error = NULL;
|
|
service_thread = g_thread_create (service_thread_func,
|
|
&data,
|
|
TRUE,
|
|
&error);
|
|
while (service_loop == NULL)
|
|
g_thread_yield ();
|
|
g_assert (server != NULL);
|
|
|
|
/* bring up a connection and accept it */
|
|
data.accept_connection = TRUE;
|
|
error = NULL;
|
|
c = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
|
|
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
|
|
NULL, /* GDBusAuthObserver */
|
|
NULL, /* cancellable */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_assert (c != NULL);
|
|
while (data.current_connections->len < 1)
|
|
g_main_loop_run (loop);
|
|
g_assert_cmpint (data.current_connections->len, ==, 1);
|
|
g_assert_cmpint (data.num_connection_attempts, ==, 1);
|
|
g_assert (g_dbus_connection_get_unique_name (c) == NULL);
|
|
g_assert_cmpstr (g_dbus_connection_get_guid (c), ==, test_guid);
|
|
|
|
/* check that we create a proxy, read properties, receive signals and invoke
|
|
* the HelloPeer() method. Since the server runs in another thread it's fine
|
|
* to use synchronous blocking API here.
|
|
*/
|
|
error = NULL;
|
|
proxy = g_dbus_proxy_new_sync (c,
|
|
G_DBUS_PROXY_FLAGS_NONE,
|
|
NULL,
|
|
NULL, /* bus_name */
|
|
"/org/gtk/GDBus/PeerTestObject",
|
|
"org.gtk.GDBus.PeerTestInterface",
|
|
NULL, /* GCancellable */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_assert (proxy != NULL);
|
|
error = NULL;
|
|
value = g_dbus_proxy_get_cached_property (proxy, "PeerProperty");
|
|
g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "ThePropertyValue");
|
|
|
|
/* try invoking a method */
|
|
error = NULL;
|
|
result = g_dbus_proxy_call_sync (proxy,
|
|
"HelloPeer",
|
|
g_variant_new ("(s)", "Hey Peer!"),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL, /* GCancellable */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_variant_get (result, "(&s)", &s);
|
|
g_assert_cmpstr (s, ==, "You greeted me with 'Hey Peer!'.");
|
|
g_variant_unref (result);
|
|
g_assert_cmpint (data.num_method_calls, ==, 1);
|
|
|
|
/* make the other peer emit a signal - catch it */
|
|
signal_handler_id = g_signal_connect (proxy,
|
|
"g-signal",
|
|
G_CALLBACK (on_proxy_signal_received),
|
|
&data);
|
|
g_assert (!data.signal_received);
|
|
g_dbus_proxy_call (proxy,
|
|
"EmitSignal",
|
|
NULL, /* no arguments */
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL, /* GCancellable */
|
|
NULL, /* GAsyncReadyCallback - we don't care about the result */
|
|
NULL); /* user_data */
|
|
g_main_loop_run (loop);
|
|
g_assert (data.signal_received);
|
|
g_assert_cmpint (data.num_method_calls, ==, 2);
|
|
g_signal_handler_disconnect (proxy, signal_handler_id);
|
|
|
|
/* Also ensure that messages with the sender header-field set gets
|
|
* delivered to the proxy - note that this doesn't really make sense
|
|
* e.g. names are meaning-less in a peer-to-peer case... but we
|
|
* support it because it makes sense in certain bridging
|
|
* applications - see e.g. #623815.
|
|
*/
|
|
signal_handler_id = g_signal_connect (proxy,
|
|
"g-signal",
|
|
G_CALLBACK (on_proxy_signal_received_with_name_set),
|
|
&data);
|
|
data.signal_received = FALSE;
|
|
g_dbus_proxy_call (proxy,
|
|
"EmitSignalWithNameSet",
|
|
NULL, /* no arguments */
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL, /* GCancellable */
|
|
NULL, /* GAsyncReadyCallback - we don't care about the result */
|
|
NULL); /* user_data */
|
|
g_main_loop_run (loop);
|
|
g_assert (data.signal_received);
|
|
g_assert_cmpint (data.num_method_calls, ==, 3);
|
|
g_signal_handler_disconnect (proxy, signal_handler_id);
|
|
|
|
/* 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,
|
|
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);
|
|
|
|
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);
|
|
}
|
|
#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);
|
|
#endif /* G_OS_UNIX */
|
|
|
|
/* Check that g_socket_get_credentials() work - this really should
|
|
* be in a GSocket-specific test suite but no such test suite exists
|
|
* right now.
|
|
*/
|
|
{
|
|
GSocket *socket;
|
|
GCredentials *credentials;
|
|
socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (g_dbus_connection_get_stream (c)));
|
|
g_assert (G_IS_SOCKET (socket));
|
|
error = NULL;
|
|
credentials = g_socket_get_credentials (socket, &error);
|
|
#ifdef __linux__
|
|
{
|
|
struct ucred *native_creds;
|
|
g_assert_no_error (error);
|
|
g_assert (G_IS_CREDENTIALS (credentials));
|
|
native_creds = g_credentials_get_native (credentials, G_CREDENTIALS_TYPE_LINUX_UCRED);
|
|
g_assert (native_creds != NULL);
|
|
g_assert (native_creds->uid == getuid ());
|
|
g_assert (native_creds->gid == getgid ());
|
|
g_assert (native_creds->pid == getpid ());
|
|
}
|
|
g_object_unref (credentials);
|
|
#else
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
|
|
g_assert (credentials == NULL);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* bring up a connection - don't accept it - this should fail
|
|
*/
|
|
data.accept_connection = FALSE;
|
|
error = NULL;
|
|
c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
|
|
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
|
|
NULL, /* GDBusAuthObserver */
|
|
NULL, /* cancellable */
|
|
&error);
|
|
_g_assert_error_domain (error, G_IO_ERROR);
|
|
g_assert (c2 == NULL);
|
|
|
|
#if 0
|
|
/* TODO: THIS TEST DOESN'T WORK YET */
|
|
|
|
/* bring up a connection - accept it.. then disconnect from the client side - check
|
|
* that the server side gets the disconnect signal.
|
|
*/
|
|
error = NULL;
|
|
data.accept_connection = TRUE;
|
|
c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
|
|
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
|
|
NULL, /* GDBusAuthObserver */
|
|
NULL, /* cancellable */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_assert (c2 != NULL);
|
|
g_assert (!g_dbus_connection_get_is_disconnected (c2));
|
|
while (data.num_connection_attempts < 3)
|
|
g_main_loop_run (loop);
|
|
g_assert_cmpint (data.current_connections->len, ==, 2);
|
|
g_assert_cmpint (data.num_connection_attempts, ==, 3);
|
|
g_assert (!g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1])));
|
|
g_idle_add (on_do_disconnect_in_idle, c2);
|
|
g_debug ("==================================================");
|
|
g_debug ("==================================================");
|
|
g_debug ("==================================================");
|
|
g_debug ("waiting for disconnect on connection %p, stream %p",
|
|
data.current_connections->pdata[1],
|
|
g_dbus_connection_get_stream (data.current_connections->pdata[1]));
|
|
|
|
g_timeout_add (2000, check_connection, &data);
|
|
//_g_assert_signal_received (G_DBUS_CONNECTION (data.current_connections->pdata[1]), "closed");
|
|
g_main_loop_run (loop);
|
|
g_assert (g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1])));
|
|
g_ptr_array_set_size (data.current_connections, 1); /* remove disconnected connection object */
|
|
#endif
|
|
|
|
/* unref the server and stop listening for new connections
|
|
*
|
|
* This won't bring down the established connections - check that c is still connected
|
|
* by invoking a method
|
|
*/
|
|
//g_socket_service_stop (service);
|
|
//g_object_unref (service);
|
|
g_dbus_server_stop (server);
|
|
g_object_unref (server);
|
|
server = NULL;
|
|
|
|
error = NULL;
|
|
result = g_dbus_proxy_call_sync (proxy,
|
|
"HelloPeer",
|
|
g_variant_new ("(s)", "Hey Again Peer!"),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL, /* GCancellable */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
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);
|
|
|
|
#if 0
|
|
/* TODO: THIS TEST DOESN'T WORK YET */
|
|
|
|
/* now disconnect from the server side - check that the client side gets the signal */
|
|
g_assert_cmpint (data.current_connections->len, ==, 1);
|
|
g_assert (G_DBUS_CONNECTION (data.current_connections->pdata[0]) != c);
|
|
g_dbus_connection_disconnect (G_DBUS_CONNECTION (data.current_connections->pdata[0]));
|
|
if (!g_dbus_connection_get_is_disconnected (c))
|
|
_g_assert_signal_received (c, "closed");
|
|
g_assert (g_dbus_connection_get_is_disconnected (c));
|
|
#endif
|
|
|
|
g_object_unref (c);
|
|
g_ptr_array_unref (data.current_connections);
|
|
g_object_unref (proxy);
|
|
|
|
g_main_loop_quit (service_loop);
|
|
g_thread_join (service_thread);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
|
|
typedef struct
|
|
{
|
|
GDBusServer *server;
|
|
GMainContext *context;
|
|
GMainLoop *loop;
|
|
|
|
GList *connections;
|
|
} DmpData;
|
|
|
|
static void
|
|
dmp_data_free (DmpData *data)
|
|
{
|
|
g_main_loop_unref (data->loop);
|
|
g_main_context_unref (data->context);
|
|
g_object_unref (data->server);
|
|
g_list_foreach (data->connections, (GFunc) g_object_unref, NULL);
|
|
g_list_free (data->connections);
|
|
g_free (data);
|
|
}
|
|
|
|
static void
|
|
dmp_on_method_call (GDBusConnection *connection,
|
|
const gchar *sender,
|
|
const gchar *object_path,
|
|
const gchar *interface_name,
|
|
const gchar *method_name,
|
|
GVariant *parameters,
|
|
GDBusMethodInvocation *invocation,
|
|
gpointer user_data)
|
|
{
|
|
//DmpData *data = user_data;
|
|
gint32 first;
|
|
gint32 second;
|
|
g_variant_get (parameters,
|
|
"(ii)",
|
|
&first,
|
|
&second);
|
|
g_dbus_method_invocation_return_value (invocation,
|
|
g_variant_new ("(i)", first + second));
|
|
}
|
|
|
|
static const GDBusInterfaceVTable dmp_interface_vtable =
|
|
{
|
|
dmp_on_method_call,
|
|
NULL, /* get_property */
|
|
NULL /* set_property */
|
|
};
|
|
|
|
|
|
/* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */
|
|
static void
|
|
dmp_on_new_connection (GDBusServer *server,
|
|
GDBusConnection *connection,
|
|
gpointer user_data)
|
|
{
|
|
DmpData *data = user_data;
|
|
GDBusNodeInfo *node;
|
|
GError *error;
|
|
|
|
/* accept the connection */
|
|
data->connections = g_list_prepend (data->connections, g_object_ref (connection));
|
|
|
|
error = NULL;
|
|
node = g_dbus_node_info_new_for_xml ("<node>"
|
|
" <interface name='org.gtk.GDBus.DmpInterface'>"
|
|
" <method name='AddPair'>"
|
|
" <arg type='i' name='first' direction='in'/>"
|
|
" <arg type='i' name='second' direction='in'/>"
|
|
" <arg type='i' name='sum' direction='out'/>"
|
|
" </method>"
|
|
" </interface>"
|
|
"</node>",
|
|
&error);
|
|
g_assert_no_error (error);
|
|
|
|
/* sleep 100ms before exporting an object - this is to test that
|
|
* G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING really works
|
|
* (GDBusServer uses this feature).
|
|
*/
|
|
usleep (100 * 1000);
|
|
|
|
/* export an object */
|
|
error = NULL;
|
|
g_dbus_connection_register_object (connection,
|
|
"/dmp/test",
|
|
node->interfaces[0],
|
|
&dmp_interface_vtable,
|
|
data,
|
|
NULL,
|
|
&error);
|
|
g_dbus_node_info_unref (node);
|
|
}
|
|
|
|
static gpointer
|
|
dmp_thread_func (gpointer user_data)
|
|
{
|
|
DmpData *data = user_data;
|
|
GError *error;
|
|
gchar *guid;
|
|
|
|
data->context = g_main_context_new ();
|
|
g_main_context_push_thread_default (data->context);
|
|
|
|
error = NULL;
|
|
guid = g_dbus_generate_guid ();
|
|
data->server = g_dbus_server_new_sync ("unix:tmpdir=/tmp/gdbus-test-",
|
|
G_DBUS_SERVER_FLAGS_NONE,
|
|
guid,
|
|
NULL, /* GDBusAuthObserver */
|
|
NULL, /* GCancellable */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_signal_connect (data->server,
|
|
"new-connection",
|
|
G_CALLBACK (dmp_on_new_connection),
|
|
data);
|
|
|
|
g_dbus_server_start (data->server);
|
|
|
|
data->loop = g_main_loop_new (data->context, FALSE);
|
|
g_main_loop_run (data->loop);
|
|
|
|
g_main_context_pop_thread_default (data->context);
|
|
|
|
g_free (guid);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
delayed_message_processing (void)
|
|
{
|
|
GError *error;
|
|
DmpData *data;
|
|
GThread *service_thread;
|
|
guint n;
|
|
|
|
data = g_new0 (DmpData, 1);
|
|
|
|
error = NULL;
|
|
service_thread = g_thread_create (dmp_thread_func,
|
|
data,
|
|
TRUE,
|
|
&error);
|
|
while (data->server == NULL || !g_dbus_server_is_active (data->server))
|
|
g_thread_yield ();
|
|
|
|
for (n = 0; n < 5; n++)
|
|
{
|
|
GDBusConnection *c;
|
|
GVariant *res;
|
|
gint32 val;
|
|
|
|
error = NULL;
|
|
c = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (data->server),
|
|
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
|
|
NULL, /* GDBusAuthObserver */
|
|
NULL, /* GCancellable */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
|
|
error = NULL;
|
|
res = g_dbus_connection_call_sync (c,
|
|
NULL, /* bus name */
|
|
"/dmp/test",
|
|
"org.gtk.GDBus.DmpInterface",
|
|
"AddPair",
|
|
g_variant_new ("(ii)", 2, n),
|
|
G_VARIANT_TYPE ("(i)"),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1, /* timeout_msec */
|
|
NULL, /* GCancellable */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_variant_get (res, "(i)", &val);
|
|
g_assert_cmpint (val, ==, 2 + n);
|
|
g_variant_unref (res);
|
|
g_object_unref (c);
|
|
}
|
|
|
|
g_main_loop_quit (data->loop);
|
|
g_thread_join (service_thread);
|
|
dmp_data_free (data);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
|
|
static gboolean
|
|
nonce_tcp_on_authorize_authenticated_peer (GDBusAuthObserver *observer,
|
|
GIOStream *stream,
|
|
GCredentials *credentials,
|
|
gpointer user_data)
|
|
{
|
|
PeerData *data = user_data;
|
|
gboolean authorized;
|
|
|
|
data->num_connection_attempts++;
|
|
|
|
authorized = TRUE;
|
|
if (!data->accept_connection)
|
|
{
|
|
authorized = FALSE;
|
|
g_main_loop_quit (loop);
|
|
}
|
|
|
|
return authorized;
|
|
}
|
|
|
|
/* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */
|
|
static void
|
|
nonce_tcp_on_new_connection (GDBusServer *server,
|
|
GDBusConnection *connection,
|
|
gpointer user_data)
|
|
{
|
|
PeerData *data = user_data;
|
|
|
|
g_ptr_array_add (data->current_connections, g_object_ref (connection));
|
|
|
|
g_main_loop_quit (loop);
|
|
}
|
|
|
|
static gpointer
|
|
nonce_tcp_service_thread_func (gpointer user_data)
|
|
{
|
|
PeerData *data = user_data;
|
|
GMainContext *service_context;
|
|
GDBusAuthObserver *observer;
|
|
GError *error;
|
|
|
|
service_context = g_main_context_new ();
|
|
g_main_context_push_thread_default (service_context);
|
|
|
|
error = NULL;
|
|
observer = g_dbus_auth_observer_new ();
|
|
server = g_dbus_server_new_sync ("nonce-tcp:",
|
|
G_DBUS_SERVER_FLAGS_NONE,
|
|
test_guid,
|
|
observer,
|
|
NULL, /* cancellable */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
|
|
g_signal_connect (server,
|
|
"new-connection",
|
|
G_CALLBACK (nonce_tcp_on_new_connection),
|
|
data);
|
|
g_signal_connect (observer,
|
|
"authorize-authenticated-peer",
|
|
G_CALLBACK (nonce_tcp_on_authorize_authenticated_peer),
|
|
data);
|
|
g_object_unref (observer);
|
|
|
|
g_dbus_server_start (server);
|
|
|
|
service_loop = g_main_loop_new (service_context, FALSE);
|
|
g_main_loop_run (service_loop);
|
|
|
|
g_main_context_pop_thread_default (service_context);
|
|
|
|
g_main_loop_unref (service_loop);
|
|
g_main_context_unref (service_context);
|
|
|
|
/* test code specifically unrefs the server - see below */
|
|
g_assert (server == NULL);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
test_nonce_tcp (void)
|
|
{
|
|
PeerData data;
|
|
GError *error;
|
|
GThread *service_thread;
|
|
GDBusConnection *c;
|
|
gchar *s;
|
|
gchar *nonce_file;
|
|
gboolean res;
|
|
const gchar *address;
|
|
|
|
memset (&data, '\0', sizeof (PeerData));
|
|
data.current_connections = g_ptr_array_new_with_free_func (g_object_unref);
|
|
|
|
error = NULL;
|
|
server = NULL;
|
|
service_loop = NULL;
|
|
service_thread = g_thread_create (nonce_tcp_service_thread_func,
|
|
&data,
|
|
TRUE,
|
|
&error);
|
|
while (service_loop == NULL)
|
|
g_thread_yield ();
|
|
g_assert (server != NULL);
|
|
|
|
|
|
/* bring up a connection and accept it */
|
|
data.accept_connection = TRUE;
|
|
error = NULL;
|
|
c = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
|
|
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
|
|
NULL, /* GDBusAuthObserver */
|
|
NULL, /* cancellable */
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_assert (c != NULL);
|
|
while (data.current_connections->len < 1)
|
|
g_main_loop_run (loop);
|
|
g_assert_cmpint (data.current_connections->len, ==, 1);
|
|
g_assert_cmpint (data.num_connection_attempts, ==, 1);
|
|
g_assert (g_dbus_connection_get_unique_name (c) == NULL);
|
|
g_assert_cmpstr (g_dbus_connection_get_guid (c), ==, test_guid);
|
|
g_object_unref (c);
|
|
|
|
/* now, try to subvert the nonce file (this assumes noncefile is the last key/value pair)
|
|
*/
|
|
|
|
address = g_dbus_server_get_client_address (server);
|
|
|
|
s = strstr (address, "noncefile=");
|
|
g_assert (s != NULL);
|
|
s += sizeof "noncefile=" - 1;
|
|
nonce_file = g_strdup (s);
|
|
|
|
/* First try invalid data in the nonce file - this will actually
|
|
* make the client send this and the server will reject it. The way
|
|
* it works is that if the nonce doesn't match, the server will
|
|
* simply close the connection. So, from the client point of view,
|
|
* we can see a variety of errors.
|
|
*/
|
|
error = NULL;
|
|
res = g_file_set_contents (nonce_file,
|
|
"0123456789012345",
|
|
-1,
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_assert (res);
|
|
c = g_dbus_connection_new_for_address_sync (address,
|
|
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
|
|
NULL, /* GDBusAuthObserver */
|
|
NULL, /* cancellable */
|
|
&error);
|
|
_g_assert_error_domain (error, G_IO_ERROR);
|
|
g_assert (c == NULL);
|
|
|
|
/* Then try with a nonce-file of incorrect length - this will make
|
|
* the client complain - we won't even try connecting to the server
|
|
* for this
|
|
*/
|
|
error = NULL;
|
|
res = g_file_set_contents (nonce_file,
|
|
"0123456789012345_",
|
|
-1,
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_assert (res);
|
|
c = g_dbus_connection_new_for_address_sync (address,
|
|
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
|
|
NULL, /* GDBusAuthObserver */
|
|
NULL, /* cancellable */
|
|
&error);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
|
|
g_assert (c == NULL);
|
|
|
|
/* Finally try with no nonce-file at all */
|
|
g_assert_cmpint (g_unlink (nonce_file), ==, 0);
|
|
error = NULL;
|
|
c = g_dbus_connection_new_for_address_sync (address,
|
|
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
|
|
NULL, /* GDBusAuthObserver */
|
|
NULL, /* cancellable */
|
|
&error);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
|
|
g_assert (c == NULL);
|
|
|
|
g_free (nonce_file);
|
|
|
|
g_dbus_server_stop (server);
|
|
g_object_unref (server);
|
|
server = NULL;
|
|
|
|
g_main_loop_quit (service_loop);
|
|
g_thread_join (service_thread);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
|
|
int
|
|
main (int argc,
|
|
char *argv[])
|
|
{
|
|
gint ret;
|
|
GDBusNodeInfo *introspection_data = NULL;
|
|
|
|
g_type_init ();
|
|
g_thread_init (NULL);
|
|
g_test_init (&argc, &argv, NULL);
|
|
|
|
introspection_data = g_dbus_node_info_new_for_xml (test_interface_introspection_xml, NULL);
|
|
g_assert (introspection_data != NULL);
|
|
test_interface_introspection_data = introspection_data->interfaces[0];
|
|
|
|
test_guid = g_dbus_generate_guid ();
|
|
|
|
/* all the tests rely on a shared main loop */
|
|
loop = g_main_loop_new (NULL, FALSE);
|
|
|
|
g_test_add_func ("/gdbus/peer-to-peer", test_peer);
|
|
g_test_add_func ("/gdbus/delayed-message-processing", delayed_message_processing);
|
|
g_test_add_func ("/gdbus/nonce-tcp", test_nonce_tcp);
|
|
|
|
ret = g_test_run();
|
|
|
|
g_main_loop_unref (loop);
|
|
g_free (test_guid);
|
|
g_dbus_node_info_unref (introspection_data);
|
|
|
|
return ret;
|
|
}
|