X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Ftests%2Fgdbus-peer.c;h=6c6f3b06bf8de8e5234243fbc25b32c678e512d1;hb=2a53b4d0e2c98a14aedf31e38f0ad1fb2e8fe26f;hp=4016b5ed8664d072d338555e83bab1b3c6959517;hpb=87fa3a6e75132f68edfbd457632332c52c2048ba;p=platform%2Fupstream%2Fglib.git diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index 4016b5e..6c6f3b0 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -13,13 +13,13 @@ * 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. + * Public License along with this library; if not, see . * * Author: David Zeuthen */ +#include "config.h" + #include #include #include @@ -28,12 +28,24 @@ #include #include #include +#include +/* for g_unlink() */ +#include + +#include #include #include +#include + +#ifdef G_OS_UNIX +#include +#include +#endif #include "gdbus-tests.h" +#include "gdbus-object-manager-example/gdbus-example-objectmanager-generated.h" #ifdef G_OS_UNIX static gboolean is_unix = TRUE; @@ -41,7 +53,10 @@ static gboolean is_unix = TRUE; static gboolean is_unix = FALSE; #endif +static gchar *tmp_address = NULL; static gchar *test_guid = NULL; +static GMutex service_loop_lock; +static GCond service_loop_cond; static GMainLoop *service_loop = NULL; static GDBusServer *server = NULL; static GMainLoop *loop = NULL; @@ -68,6 +83,7 @@ static const gchar *test_interface_introspection_xml = " " " " " " + " " " " " " " " @@ -77,7 +93,7 @@ static const gchar *test_interface_introspection_xml = " " " " ""; -static const GDBusInterfaceInfo *test_interface_introspection_data = NULL; +static GDBusInterfaceInfo *test_interface_introspection_data = NULL; static void test_interface_method_call (GDBusConnection *connection, @@ -90,12 +106,16 @@ test_interface_method_call (GDBusConnection *connection, gpointer user_data) { PeerData *data = user_data; + const GDBusMethodInfo *info; data->num_method_calls++; g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject"); g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface"); + info = g_dbus_method_invocation_get_method_info (invocation); + g_assert_cmpstr (info->name, ==, method_name); + if (g_strcmp0 (method_name, "HelloPeer") == 0) { const gchar *greeting; @@ -124,6 +144,25 @@ 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, "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 @@ -139,18 +178,21 @@ test_interface_method_call (GDBusConnection *connection, error = NULL; - fd = open (path, O_RDONLY); + fd = g_open (path, O_RDONLY, 0); + g_assert (fd != -1); 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 (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); @@ -207,6 +249,22 @@ on_proxy_signal_received (GDBusProxy *proxy, 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 @@ -231,7 +289,7 @@ on_authorize_authenticated_peer (GDBusAuthObserver *observer, } /* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */ -static void +static gboolean on_new_connection (GDBusServer *server, GDBusConnection *connection, gpointer user_data) @@ -246,6 +304,20 @@ on_new_connection (GDBusServer *server, g_ptr_array_add (data->current_connections, g_object_ref (connection)); +#if G_CREDENTIALS_SUPPORTED + { + GCredentials *credentials; + + credentials = g_dbus_connection_get_peer_credentials (connection); + + g_assert (credentials != NULL); + g_assert_cmpuint (g_credentials_get_unix_user (credentials, NULL), ==, + getuid ()); + g_assert_cmpuint (g_credentials_get_unix_pid (credentials, NULL), ==, + getpid ()); + } +#endif + /* export object on the newly established connection */ error = NULL; reg_id = g_dbus_connection_register_object (connection, @@ -259,6 +331,35 @@ on_new_connection (GDBusServer *server, g_assert (reg_id > 0); g_main_loop_quit (loop); + + return TRUE; +} + +static void +create_service_loop (GMainContext *service_context) +{ + g_assert (service_loop == NULL); + g_mutex_lock (&service_loop_lock); + service_loop = g_main_loop_new (service_context, FALSE); + g_cond_broadcast (&service_loop_cond); + g_mutex_unlock (&service_loop_lock); +} + +static void +teardown_service_loop (void) +{ + g_mutex_lock (&service_loop_lock); + g_clear_pointer (&service_loop, g_main_loop_unref); + g_mutex_unlock (&service_loop_lock); +} + +static void +await_service_loop (void) +{ + g_mutex_lock (&service_loop_lock); + while (service_loop == NULL) + g_cond_wait (&service_loop_cond, &service_loop_lock); + g_mutex_unlock (&service_loop_lock); } static gpointer @@ -266,15 +367,18 @@ service_thread_func (gpointer user_data) { PeerData *data = user_data; GMainContext *service_context; - GDBusAuthObserver *observer; + GDBusAuthObserver *observer, *o; GError *error; + GDBusServerFlags f; + gchar *a, *g; + gboolean b; 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:", + server = g_dbus_server_new_sync (tmp_address, G_DBUS_SERVER_FLAGS_NONE, test_guid, observer, @@ -290,16 +394,35 @@ service_thread_func (gpointer user_data) "authorize-authenticated-peer", G_CALLBACK (on_authorize_authenticated_peer), data); + + g_assert_cmpint (g_dbus_server_get_flags (server), ==, G_DBUS_SERVER_FLAGS_NONE); + g_assert_cmpstr (g_dbus_server_get_guid (server), ==, test_guid); + g_object_get (server, + "flags", &f, + "address", &a, + "guid", &g, + "active", &b, + "authentication-observer", &o, + NULL); + g_assert_cmpint (f, ==, G_DBUS_SERVER_FLAGS_NONE); + g_assert_cmpstr (a, ==, tmp_address); + g_assert_cmpstr (g, ==, test_guid); + g_assert (!b); + g_assert (o == observer); + g_free (a); + g_free (g); + g_object_unref (o); + g_object_unref (observer); g_dbus_server_start (server); - service_loop = g_main_loop_new (service_context, FALSE); + create_service_loop (service_context); g_main_loop_run (service_loop); g_main_context_pop_thread_default (service_context); - g_main_loop_unref (service_loop); + teardown_service_loop (); g_main_context_unref (service_context); /* test code specifically unrefs the server - see below */ @@ -389,12 +512,12 @@ service_thread_func (gpointer data) data); g_socket_service_start (service); - service_loop = g_main_loop_new (service_context, FALSE); + create_service_loop (service_context); g_main_loop_run (service_loop); g_main_context_pop_thread_default (service_context); - g_main_loop_unref (service_loop); + teardown_service_loop (); g_main_context_unref (service_context); g_object_unref (address); @@ -462,6 +585,55 @@ on_do_disconnect_in_idle (gpointer data) } #endif +#ifdef G_OS_UNIX +static gchar * +read_all_from_fd (gint fd, gsize *out_len, GError **error) +{ + GString *str; + gchar buf[64]; + gssize num_read; + + str = g_string_new (NULL); + + do + { + num_read = read (fd, buf, sizeof (buf)); + if (num_read == -1) + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + continue; + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + "Failed reading %d bytes into offset %d: %s", + (gint) sizeof (buf), + (gint) str->len, + strerror (errno)); + goto error; + } + else if (num_read > 0) + { + g_string_append_len (str, buf, num_read); + } + else if (num_read == 0) + { + break; + } + } + while (TRUE); + + if (out_len != NULL) + *out_len = str->len; + return g_string_free (str, FALSE); + + error: + if (out_len != NULL) + out_len = 0; + g_string_free (str, TRUE); + return NULL; +} +#endif + static void test_peer (void) { @@ -474,6 +646,7 @@ test_peer (void) 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); @@ -494,13 +667,10 @@ test_peer (void) 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 (); + service_thread = g_thread_new ("test_peer", + service_thread_func, + &data); + await_service_loop (); g_assert (server != NULL); /* bring up a connection and accept it */ @@ -555,10 +725,10 @@ test_peer (void) g_assert_cmpint (data.num_method_calls, ==, 1); /* make the other peer emit a signal - catch it */ - g_signal_connect (proxy, - "g-signal", - G_CALLBACK (on_proxy_signal_received), - &data); + 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", @@ -571,6 +741,31 @@ test_peer (void) 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 @@ -579,19 +774,21 @@ test_peer (void) GDBusMessage *method_reply_message; GUnixFDList *fd_list; gint fd; - gchar buf[1024]; - gssize len; + 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)", "/etc/hosts")); + 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 */ @@ -607,20 +804,23 @@ test_peer (void) g_object_unref (method_call_message); g_object_unref (method_reply_message); - memset (buf, '\0', sizeof (buf)); - len = read (fd, buf, sizeof (buf) - 1); + 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 ("/etc/hosts", + g_file_get_contents (testfile, &buf2, &len2, &error); g_assert_no_error (error); - if (len2 > sizeof (buf)) - buf2[sizeof (buf)] = '\0'; - g_assert_cmpstr (buf, ==, buf2); + g_assert_cmpint (len, ==, len2); + g_assert (memcmp (buf, buf2, len) == 0); g_free (buf2); + g_free (buf); } #else error = NULL; @@ -636,6 +836,31 @@ test_peer (void) g_error_free (error); #endif /* G_OS_UNIX */ + /* Check that g_socket_get_credentials() work - (though this really + * should be in socket.c) + */ + { + 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); + +#if G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED + g_assert_no_error (error); + g_assert (G_IS_CREDENTIALS (credentials)); + + g_assert_cmpuint (g_credentials_get_unix_user (credentials, NULL), ==, + getuid ()); + g_assert_cmpuint (g_credentials_get_unix_pid (credentials, NULL), ==, + getpid ()); +#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 */ @@ -647,6 +872,7 @@ test_peer (void) NULL, /* cancellable */ &error); _g_assert_error_domain (error, G_IO_ERROR); + g_error_free (error); g_assert (c2 == NULL); #if 0 @@ -708,7 +934,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, ==, 4); + g_assert_cmpint (data.num_method_calls, ==, 5); #if 0 /* TODO: THIS TEST DOESN'T WORK YET */ @@ -747,8 +973,7 @@ 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_list_free_full (data->connections, g_object_unref); g_free (data); } @@ -782,7 +1007,7 @@ static const GDBusInterfaceVTable dmp_interface_vtable = /* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */ -static void +static gboolean dmp_on_new_connection (GDBusServer *server, GDBusConnection *connection, gpointer user_data) @@ -823,6 +1048,8 @@ dmp_on_new_connection (GDBusServer *server, NULL, &error); g_dbus_node_info_unref (node); + + return TRUE; } static gpointer @@ -837,7 +1064,7 @@ dmp_thread_func (gpointer user_data) error = NULL; guid = g_dbus_generate_guid (); - data->server = g_dbus_server_new_sync ("nonce-tcp:", + data->server = g_dbus_server_new_sync (tmp_address, G_DBUS_SERVER_FLAGS_NONE, guid, NULL, /* GDBusAuthObserver */ @@ -870,11 +1097,9 @@ delayed_message_processing (void) data = g_new0 (DmpData, 1); - error = NULL; - service_thread = g_thread_create (dmp_thread_func, - data, - TRUE, - &error); + service_thread = g_thread_new ("dmp", + dmp_thread_func, + data); while (data->server == NULL || !g_dbus_server_is_active (data->server)) g_thread_yield (); @@ -918,15 +1143,570 @@ delayed_message_processing (void) /* ---------------------------------------------------------------------------------------------------- */ +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 gboolean +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); + + return TRUE; +} + +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); + + create_service_loop (service_context); + g_main_loop_run (service_loop); + + g_main_context_pop_thread_default (service_context); + + teardown_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_thread = g_thread_new ("nonce-tcp-service", + nonce_tcp_service_thread_func, + &data); + await_service_loop (); + 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_thread_yield (); + 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_error_free (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_error_free (error); + 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_error_free (error); + 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); +} + +static void +test_credentials (void) +{ + GCredentials *c1, *c2; + GError *error; + gchar *desc; + + c1 = g_credentials_new (); + c2 = g_credentials_new (); + + error = NULL; + if (g_credentials_set_unix_user (c2, getuid (), &error)) + g_assert_no_error (error); + + g_clear_error (&error); + g_assert (g_credentials_is_same_user (c1, c2, &error)); + g_assert_no_error (error); + + desc = g_credentials_to_string (c1); + g_assert (desc != NULL); + g_free (desc); + + g_object_unref (c1); + g_object_unref (c2); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +tcp_anonymous_on_new_connection (GDBusServer *server, + GDBusConnection *connection, + gpointer user_data) +{ + gboolean *seen_connection = user_data; + *seen_connection = TRUE; + return TRUE; +} + +static gpointer +tcp_anonymous_service_thread_func (gpointer user_data) +{ + gboolean *seen_connection = user_data; + GMainContext *service_context; + GError *error; + + service_context = g_main_context_new (); + g_main_context_push_thread_default (service_context); + + error = NULL; + server = g_dbus_server_new_sync ("tcp:", + G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS, + test_guid, + NULL, /* GDBusObserver* */ + NULL, /* GCancellable* */ + &error); + g_assert_no_error (error); + + g_signal_connect (server, + "new-connection", + G_CALLBACK (tcp_anonymous_on_new_connection), + seen_connection); + + g_dbus_server_start (server); + + create_service_loop (service_context); + g_main_loop_run (service_loop); + + g_main_context_pop_thread_default (service_context); + + teardown_service_loop (); + g_main_context_unref (service_context); + + return NULL; +} + +static void +test_tcp_anonymous (void) +{ + gboolean seen_connection; + GThread *service_thread; + GDBusConnection *connection; + GError *error; + + seen_connection = FALSE; + service_thread = g_thread_new ("tcp-anon-service", + tcp_anonymous_service_thread_func, + &seen_connection); + await_service_loop (); + g_assert (server != NULL); + + error = NULL; + connection = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server), + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* GDBusAuthObserver* */ + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_assert (connection != NULL); + + while (!seen_connection) + g_thread_yield (); + + g_object_unref (connection); + + g_main_loop_quit (service_loop); + g_dbus_server_stop (server); + g_object_unref (server); + server = NULL; + + g_thread_join (service_thread); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusServer *codegen_server = NULL; + +static gboolean +codegen_on_animal_poke (ExampleAnimal *animal, + GDBusMethodInvocation *invocation, + gboolean make_sad, + gboolean make_happy, + gpointer user_data) +{ + if ((make_sad && make_happy) || (!make_sad && !make_happy)) + { + g_main_loop_quit (service_loop); + + g_dbus_method_invocation_return_dbus_error (invocation, + "org.gtk.GDBus.Examples.ObjectManager.Error.Failed", + "Exactly one of make_sad or make_happy must be TRUE"); + goto out; + } + + if (make_sad) + { + if (g_strcmp0 (example_animal_get_mood (animal), "Sad") == 0) + { + g_dbus_method_invocation_return_dbus_error (invocation, + "org.gtk.GDBus.Examples.ObjectManager.Error.SadAnimalIsSad", + "Sad animal is already sad"); + goto out; + } + + example_animal_set_mood (animal, "Sad"); + example_animal_complete_poke (animal, invocation); + goto out; + } + + if (make_happy) + { + if (g_strcmp0 (example_animal_get_mood (animal), "Happy") == 0) + { + g_dbus_method_invocation_return_dbus_error (invocation, + "org.gtk.GDBus.Examples.ObjectManager.Error.HappyAnimalIsHappy", + "Happy animal is already happy"); + goto out; + } + + example_animal_set_mood (animal, "Happy"); + example_animal_complete_poke (animal, invocation); + goto out; + } + + g_assert_not_reached (); + + out: + return TRUE; /* to indicate that the method was handled */ +} + +/* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */ +static gboolean +codegen_on_new_connection (GDBusServer *server, + GDBusConnection *connection, + gpointer user_data) +{ + ExampleAnimal *animal = user_data; + GError *error = NULL; + + /* 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_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (animal), connection, + "/Example/Animals/000", &error); + g_assert_no_error (error); + + return TRUE; +} + +static gpointer +codegen_service_thread_func (gpointer user_data) +{ + GMainContext *service_context; + ExampleAnimal *animal; + GError *error = NULL; + + service_context = g_main_context_new (); + g_main_context_push_thread_default (service_context); + + /* Create the animal in the right thread context */ + animal = example_animal_skeleton_new (); + + /* Handle Poke() D-Bus method invocations on the .Animal interface */ + g_signal_connect (animal, "handle-poke", + G_CALLBACK (codegen_on_animal_poke), + NULL); /* user_data */ + + codegen_server = g_dbus_server_new_sync (tmp_address, + G_DBUS_SERVER_FLAGS_NONE, + test_guid, + NULL, /* observer */ + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + g_dbus_server_start (codegen_server); + + g_signal_connect (codegen_server, "new-connection", + G_CALLBACK (codegen_on_new_connection), + animal); + + create_service_loop (service_context); + g_main_loop_run (service_loop); + + g_object_unref (animal); + + g_main_context_pop_thread_default (service_context); + + teardown_service_loop (); + g_main_context_unref (service_context); + + g_dbus_server_stop (codegen_server); + g_object_unref (codegen_server); + codegen_server = NULL; + + return NULL; +} + + +static gboolean +codegen_quit_mainloop_timeout (gpointer data) +{ + g_main_loop_quit (loop); + return FALSE; +} + +static void +codegen_test_peer (void) +{ + GDBusConnection *connection; + ExampleAnimal *animal1, *animal2; + GThread *service_thread; + GError *error = NULL; + GVariant *value; + const gchar *s; + + /* bring up a server - we run the server in a different thread to avoid deadlocks */ + service_thread = g_thread_new ("codegen_test_peer", + codegen_service_thread_func, + NULL); + await_service_loop (); + g_assert (codegen_server != NULL); + + /* Get an animal 1 ... */ + connection = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (codegen_server), + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* GDBusAuthObserver */ + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + g_assert (connection != NULL); + + animal1 = example_animal_proxy_new_sync (connection, 0, NULL, + "/Example/Animals/000", NULL, &error); + g_assert_no_error (error); + g_assert (animal1 != NULL); + g_object_unref (connection); + + /* Get animal 2 ... */ + connection = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (codegen_server), + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* GDBusAuthObserver */ + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + g_assert (connection != NULL); + + animal2 = example_animal_proxy_new_sync (connection, 0, NULL, + "/Example/Animals/000", NULL, &error); + g_assert_no_error (error); + g_assert (animal2 != NULL); + g_object_unref (connection); + + /* Make animal sad via animal1 */ + example_animal_call_poke_sync (animal1, TRUE, FALSE, NULL, &error); + g_assert_no_error (error); + + /* Poke server and make sure animal is updated */ + value = g_dbus_proxy_call_sync (G_DBUS_PROXY (animal1), + "org.freedesktop.DBus.Peer.Ping", + NULL, G_DBUS_CALL_FLAGS_NONE, -1, + NULL, &error); + g_assert_no_error (error); + g_assert (value != NULL); + g_variant_unref (value); + + /* Give the proxies a chance to refresh in the defaul main loop */ + g_timeout_add (100, codegen_quit_mainloop_timeout, NULL); + g_main_loop_run (loop); + + /* Assert animals are sad */ + g_assert_cmpstr (example_animal_get_mood (animal1), ==, "Sad"); + g_assert_cmpstr (example_animal_get_mood (animal2), ==, "Sad"); + + /* Make animal happy via animal2 */ + example_animal_call_poke_sync (animal2, FALSE, TRUE, NULL, &error); + g_assert_no_error (error); + + /* Some random unrelated call, just to get some test coverage */ + value = g_dbus_proxy_call_sync (G_DBUS_PROXY (animal2), + "org.freedesktop.DBus.Peer.GetMachineId", + NULL, G_DBUS_CALL_FLAGS_NONE, -1, + NULL, &error); + g_assert_no_error (error); + g_variant_get (value, "(&s)", &s); + g_assert (g_dbus_is_guid (s)); + g_variant_unref (value); + + /* Poke server and make sure animal is updated */ + value = g_dbus_proxy_call_sync (G_DBUS_PROXY (animal2), + "org.freedesktop.DBus.Peer.Ping", + NULL, G_DBUS_CALL_FLAGS_NONE, -1, + NULL, &error); + g_assert_no_error (error); + g_assert (value != NULL); + g_variant_unref (value); + + /* Give the proxies a chance to refresh in the defaul main loop */ + g_timeout_add (1000, codegen_quit_mainloop_timeout, NULL); + g_main_loop_run (loop); + + /* Assert animals are happy */ + g_assert_cmpstr (example_animal_get_mood (animal1), ==, "Happy"); + g_assert_cmpstr (example_animal_get_mood (animal2), ==, "Happy"); + + /* This final call making the animal happy and sad will cause + * the server to quit, when the server quits we dont get property + * change notifications anyway because those are done from an idle handler + */ + example_animal_call_poke_sync (animal2, TRUE, TRUE, NULL, &error); + + g_object_unref (animal1); + g_object_unref (animal2); + g_thread_join (service_thread); +} + +/* ---------------------------------------------------------------------------------------------------- */ + + int main (int argc, char *argv[]) { gint ret; GDBusNodeInfo *introspection_data = NULL; + gchar *tmpdir = 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); @@ -935,17 +1715,42 @@ main (int argc, test_guid = g_dbus_generate_guid (); + if (is_unix) + { + if (g_unix_socket_address_abstract_names_supported ()) + tmp_address = g_strdup ("unix:tmpdir=/tmp/gdbus-test-"); + else + { + tmpdir = g_dir_make_tmp ("gdbus-test-XXXXXX", NULL); + tmp_address = g_strdup_printf ("unix:tmpdir=%s", tmpdir); + } + } + else + tmp_address = g_strdup ("nonce-tcp:"); + /* 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); + + g_test_add_func ("/gdbus/tcp-anonymous", test_tcp_anonymous); + g_test_add_func ("/gdbus/credentials", test_credentials); + g_test_add_func ("/gdbus/codegen-peer-to-peer", codegen_test_peer); ret = g_test_run(); g_main_loop_unref (loop); g_free (test_guid); g_dbus_node_info_unref (introspection_data); + if (is_unix) + g_free (tmp_address); + if (tmpdir) + { + g_rmdir (tmpdir); + g_free (tmpdir); + } return ret; }