1 | /* GLib testing framework examples and tests |
2 | * |
3 | * Copyright (C) 2008-2010 Red Hat, Inc. |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2.1 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General |
16 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
17 | * |
18 | * Author: David Zeuthen <davidz@redhat.com> |
19 | */ |
20 | |
21 | #include "config.h" |
22 | |
23 | #include <gio/gio.h> |
24 | #include <unistd.h> |
25 | #include <string.h> |
26 | |
27 | /* for open(2) */ |
28 | #include <sys/types.h> |
29 | #include <sys/stat.h> |
30 | #include <fcntl.h> |
31 | #include <string.h> |
32 | |
33 | /* for g_unlink() */ |
34 | #include <glib/gstdio.h> |
35 | |
36 | #include <gio/gnetworking.h> |
37 | #include <gio/gunixsocketaddress.h> |
38 | #include <gio/gunixfdlist.h> |
39 | #include <gio/gcredentialsprivate.h> |
40 | |
41 | #ifdef G_OS_UNIX |
42 | #include <gio/gunixconnection.h> |
43 | #include <errno.h> |
44 | #endif |
45 | |
46 | #include "gdbus-tests.h" |
47 | |
48 | #include "gdbus-object-manager-example/objectmanager-gen.h" |
49 | |
50 | #ifdef G_OS_UNIX |
51 | static gboolean is_unix = TRUE; |
52 | #else |
53 | static gboolean is_unix = FALSE; |
54 | #endif |
55 | |
56 | static gchar *tmpdir = NULL; |
57 | static gchar *tmp_address = NULL; |
58 | static gchar *test_guid = NULL; |
59 | static GMutex service_loop_lock; |
60 | static GCond service_loop_cond; |
61 | static GMainLoop *service_loop = NULL; |
62 | static GDBusServer *server = NULL; |
63 | static GMainLoop *loop = NULL; |
64 | |
65 | /* ---------------------------------------------------------------------------------------------------- */ |
66 | /* Test that peer-to-peer connections work */ |
67 | /* ---------------------------------------------------------------------------------------------------- */ |
68 | |
69 | |
70 | typedef struct |
71 | { |
72 | gboolean accept_connection; |
73 | gint num_connection_attempts; |
74 | GPtrArray *current_connections; |
75 | guint num_method_calls; |
76 | gboolean signal_received; |
77 | } PeerData; |
78 | |
79 | /* This needs to be enough to usually take more than one write(), |
80 | * to reproduce |
81 | * <https://gitlab.gnome.org/GNOME/glib/-/issues/2074>. |
82 | * 1 MiB ought to be enough. */ |
83 | #define BIG_MESSAGE_ARRAY_SIZE (1024 * 1024) |
84 | |
85 | static const gchar *test_interface_introspection_xml = |
86 | "<node>" |
87 | " <interface name='org.gtk.GDBus.PeerTestInterface'>" |
88 | " <method name='HelloPeer'>" |
89 | " <arg type='s' name='greeting' direction='in'/>" |
90 | " <arg type='s' name='response' direction='out'/>" |
91 | " </method>" |
92 | " <method name='EmitSignal'/>" |
93 | " <method name='EmitSignalWithNameSet'/>" |
94 | " <method name='OpenFile'>" |
95 | " <arg type='s' name='path' direction='in'/>" |
96 | " </method>" |
97 | " <method name='OpenFileWithBigMessage'>" |
98 | " <arg type='s' name='path' direction='in'/>" |
99 | " <arg type='h' name='handle' direction='out'/>" |
100 | " <arg type='ay' name='junk' direction='out'/>" |
101 | " </method>" |
102 | " <signal name='PeerSignal'>" |
103 | " <arg type='s' name='a_string'/>" |
104 | " </signal>" |
105 | " <property type='s' name='PeerProperty' access='read'/>" |
106 | " </interface>" |
107 | "</node>" ; |
108 | static GDBusInterfaceInfo *test_interface_introspection_data = NULL; |
109 | |
110 | static void |
111 | test_interface_method_call (GDBusConnection *connection, |
112 | const gchar *sender, |
113 | const gchar *object_path, |
114 | const gchar *interface_name, |
115 | const gchar *method_name, |
116 | GVariant *parameters, |
117 | GDBusMethodInvocation *invocation, |
118 | gpointer user_data) |
119 | { |
120 | PeerData *data = user_data; |
121 | const GDBusMethodInfo *info; |
122 | |
123 | data->num_method_calls++; |
124 | |
125 | g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject" ); |
126 | g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface" ); |
127 | |
128 | info = g_dbus_method_invocation_get_method_info (invocation); |
129 | g_assert_cmpstr (info->name, ==, method_name); |
130 | |
131 | if (g_strcmp0 (str1: method_name, str2: "HelloPeer" ) == 0) |
132 | { |
133 | const gchar *greeting; |
134 | gchar *response; |
135 | |
136 | g_variant_get (value: parameters, format_string: "(&s)" , &greeting); |
137 | |
138 | response = g_strdup_printf (format: "You greeted me with '%s'." , |
139 | greeting); |
140 | g_dbus_method_invocation_return_value (invocation, |
141 | parameters: g_variant_new (format_string: "(s)" , response)); |
142 | g_free (mem: response); |
143 | } |
144 | else if (g_strcmp0 (str1: method_name, str2: "EmitSignal" ) == 0) |
145 | { |
146 | GError *error; |
147 | |
148 | error = NULL; |
149 | g_dbus_connection_emit_signal (connection, |
150 | NULL, |
151 | object_path: "/org/gtk/GDBus/PeerTestObject" , |
152 | interface_name: "org.gtk.GDBus.PeerTestInterface" , |
153 | signal_name: "PeerSignal" , |
154 | NULL, |
155 | error: &error); |
156 | g_assert_no_error (error); |
157 | g_dbus_method_invocation_return_value (invocation, NULL); |
158 | } |
159 | else if (g_strcmp0 (str1: method_name, str2: "EmitSignalWithNameSet" ) == 0) |
160 | { |
161 | GError *error; |
162 | gboolean ret; |
163 | GDBusMessage *message; |
164 | |
165 | message = g_dbus_message_new_signal (path: "/org/gtk/GDBus/PeerTestObject" , |
166 | interface_: "org.gtk.GDBus.PeerTestInterface" , |
167 | signal: "PeerSignalWithNameSet" ); |
168 | g_dbus_message_set_sender (message, value: ":1.42" ); |
169 | |
170 | error = NULL; |
171 | ret = g_dbus_connection_send_message (connection, message, flags: G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, error: &error); |
172 | g_assert_no_error (error); |
173 | g_assert (ret); |
174 | g_object_unref (object: message); |
175 | |
176 | g_dbus_method_invocation_return_value (invocation, NULL); |
177 | } |
178 | else if (g_strcmp0 (str1: method_name, str2: "OpenFile" ) == 0 || |
179 | g_strcmp0 (str1: method_name, str2: "OpenFileWithBigMessage" ) == 0) |
180 | { |
181 | #ifdef G_OS_UNIX |
182 | const gchar *path; |
183 | GDBusMessage *reply; |
184 | GError *error; |
185 | gint fd; |
186 | GUnixFDList *fd_list; |
187 | |
188 | g_variant_get (value: parameters, format_string: "(&s)" , &path); |
189 | |
190 | fd_list = g_unix_fd_list_new (); |
191 | |
192 | error = NULL; |
193 | |
194 | fd = g_open (file: path, O_RDONLY, 0); |
195 | g_assert (fd != -1); |
196 | g_unix_fd_list_append (list: fd_list, fd, error: &error); |
197 | g_assert_no_error (error); |
198 | close (fd: fd); |
199 | |
200 | reply = g_dbus_message_new_method_reply (method_call_message: g_dbus_method_invocation_get_message (invocation)); |
201 | g_dbus_message_set_unix_fd_list (message: reply, fd_list); |
202 | g_object_unref (object: fd_list); |
203 | g_object_unref (object: invocation); |
204 | |
205 | if (g_strcmp0 (str1: method_name, str2: "OpenFileWithBigMessage" ) == 0) |
206 | { |
207 | char *junk; |
208 | |
209 | junk = g_new0 (char, BIG_MESSAGE_ARRAY_SIZE); |
210 | g_dbus_message_set_body (message: reply, |
211 | body: g_variant_new (format_string: "(h@ay)" , |
212 | 0, |
213 | g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, |
214 | elements: junk, |
215 | BIG_MESSAGE_ARRAY_SIZE, |
216 | element_size: 1))); |
217 | g_free (mem: junk); |
218 | } |
219 | |
220 | error = NULL; |
221 | g_dbus_connection_send_message (connection, |
222 | message: reply, |
223 | flags: G_DBUS_SEND_MESSAGE_FLAGS_NONE, |
224 | NULL, /* out_serial */ |
225 | error: &error); |
226 | g_assert_no_error (error); |
227 | g_object_unref (object: reply); |
228 | #else |
229 | g_dbus_method_invocation_return_dbus_error (invocation, |
230 | "org.gtk.GDBus.NotOnUnix" , |
231 | "Your OS does not support file descriptor passing" ); |
232 | #endif |
233 | } |
234 | else |
235 | { |
236 | g_assert_not_reached (); |
237 | } |
238 | } |
239 | |
240 | static GVariant * |
241 | test_interface_get_property (GDBusConnection *connection, |
242 | const gchar *sender, |
243 | const gchar *object_path, |
244 | const gchar *interface_name, |
245 | const gchar *property_name, |
246 | GError **error, |
247 | gpointer user_data) |
248 | { |
249 | g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject" ); |
250 | g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface" ); |
251 | g_assert_cmpstr (property_name, ==, "PeerProperty" ); |
252 | |
253 | return g_variant_new_string (string: "ThePropertyValue" ); |
254 | } |
255 | |
256 | |
257 | static const GDBusInterfaceVTable test_interface_vtable = |
258 | { |
259 | test_interface_method_call, |
260 | test_interface_get_property, |
261 | NULL /* set_property */ |
262 | }; |
263 | |
264 | static void |
265 | on_proxy_signal_received (GDBusProxy *proxy, |
266 | gchar *sender_name, |
267 | gchar *signal_name, |
268 | GVariant *parameters, |
269 | gpointer user_data) |
270 | { |
271 | PeerData *data = user_data; |
272 | |
273 | data->signal_received = TRUE; |
274 | |
275 | g_assert (sender_name == NULL); |
276 | g_assert_cmpstr (signal_name, ==, "PeerSignal" ); |
277 | g_main_loop_quit (loop); |
278 | } |
279 | |
280 | static void |
281 | on_proxy_signal_received_with_name_set (GDBusProxy *proxy, |
282 | gchar *sender_name, |
283 | gchar *signal_name, |
284 | GVariant *parameters, |
285 | gpointer user_data) |
286 | { |
287 | PeerData *data = user_data; |
288 | |
289 | data->signal_received = TRUE; |
290 | |
291 | g_assert_cmpstr (sender_name, ==, ":1.42" ); |
292 | g_assert_cmpstr (signal_name, ==, "PeerSignalWithNameSet" ); |
293 | g_main_loop_quit (loop); |
294 | } |
295 | |
296 | /* ---------------------------------------------------------------------------------------------------- */ |
297 | |
298 | static void |
299 | setup_test_address (void) |
300 | { |
301 | if (is_unix) |
302 | { |
303 | g_test_message (format: "Testing with unix:dir address" ); |
304 | tmpdir = g_dir_make_tmp (tmpl: "gdbus-test-XXXXXX" , NULL); |
305 | tmp_address = g_strdup_printf (format: "unix:dir=%s" , tmpdir); |
306 | } |
307 | else |
308 | tmp_address = g_strdup (str: "nonce-tcp:host=127.0.0.1" ); |
309 | } |
310 | |
311 | #ifdef G_OS_UNIX |
312 | static void |
313 | setup_tmpdir_test_address (void) |
314 | { |
315 | g_test_message (format: "Testing with unix:tmpdir address" ); |
316 | tmpdir = g_dir_make_tmp (tmpl: "gdbus-test-XXXXXX" , NULL); |
317 | tmp_address = g_strdup_printf (format: "unix:tmpdir=%s" , tmpdir); |
318 | } |
319 | |
320 | static void |
321 | setup_path_test_address (void) |
322 | { |
323 | g_test_message (format: "Testing with unix:path address" ); |
324 | tmpdir = g_dir_make_tmp (tmpl: "gdbus-test-XXXXXX" , NULL); |
325 | tmp_address = g_strdup_printf (format: "unix:path=%s/gdbus-peer-socket" , tmpdir); |
326 | } |
327 | #endif |
328 | |
329 | static void |
330 | teardown_test_address (void) |
331 | { |
332 | g_free (mem: tmp_address); |
333 | if (tmpdir) |
334 | { |
335 | /* Ensuring the rmdir succeeds also ensures any sockets created on the |
336 | * filesystem are also deleted. |
337 | */ |
338 | g_assert_cmpstr (g_rmdir (tmpdir) == 0 ? "OK" : g_strerror (errno), |
339 | ==, "OK" ); |
340 | g_clear_pointer (&tmpdir, g_free); |
341 | } |
342 | } |
343 | |
344 | /* ---------------------------------------------------------------------------------------------------- */ |
345 | |
346 | static gboolean |
347 | on_authorize_authenticated_peer (GDBusAuthObserver *observer, |
348 | GIOStream *stream, |
349 | GCredentials *credentials, |
350 | gpointer user_data) |
351 | { |
352 | PeerData *data = user_data; |
353 | gboolean authorized; |
354 | |
355 | data->num_connection_attempts++; |
356 | |
357 | authorized = TRUE; |
358 | if (!data->accept_connection) |
359 | { |
360 | authorized = FALSE; |
361 | g_main_loop_quit (loop); |
362 | } |
363 | |
364 | return authorized; |
365 | } |
366 | |
367 | /* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */ |
368 | static gboolean |
369 | on_new_connection (GDBusServer *server, |
370 | GDBusConnection *connection, |
371 | gpointer user_data) |
372 | { |
373 | PeerData *data = user_data; |
374 | GError *error = NULL; |
375 | guint reg_id; |
376 | |
377 | //g_printerr ("Client connected.\n" |
378 | // "Negotiated capabilities: unix-fd-passing=%d\n", |
379 | // g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); |
380 | |
381 | g_ptr_array_add (array: data->current_connections, g_object_ref (connection)); |
382 | |
383 | #if G_CREDENTIALS_SUPPORTED |
384 | { |
385 | GCredentials *credentials; |
386 | |
387 | credentials = g_dbus_connection_get_peer_credentials (connection); |
388 | |
389 | g_assert (credentials != NULL); |
390 | g_assert_cmpuint (g_credentials_get_unix_user (credentials, NULL), ==, |
391 | getuid ()); |
392 | #if G_CREDENTIALS_HAS_PID |
393 | g_assert_cmpint (g_credentials_get_unix_pid (credentials, &error), ==, |
394 | getpid ()); |
395 | g_assert_no_error (error); |
396 | #else |
397 | g_assert_cmpint (g_credentials_get_unix_pid (credentials, &error), ==, -1); |
398 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); |
399 | g_clear_error (&error); |
400 | #endif |
401 | } |
402 | #endif |
403 | |
404 | /* export object on the newly established connection */ |
405 | reg_id = g_dbus_connection_register_object (connection, |
406 | object_path: "/org/gtk/GDBus/PeerTestObject" , |
407 | interface_info: test_interface_introspection_data, |
408 | vtable: &test_interface_vtable, |
409 | user_data: data, |
410 | NULL, /* GDestroyNotify for data */ |
411 | error: &error); |
412 | g_assert_no_error (error); |
413 | g_assert (reg_id > 0); |
414 | |
415 | g_main_loop_quit (loop); |
416 | |
417 | return TRUE; |
418 | } |
419 | |
420 | /* We don't tell the main thread about the new GDBusServer until it has |
421 | * had a chance to start listening. */ |
422 | static gboolean |
423 | idle_in_service_loop (gpointer loop) |
424 | { |
425 | g_assert (service_loop == NULL); |
426 | g_mutex_lock (mutex: &service_loop_lock); |
427 | service_loop = loop; |
428 | g_cond_broadcast (cond: &service_loop_cond); |
429 | g_mutex_unlock (mutex: &service_loop_lock); |
430 | |
431 | return G_SOURCE_REMOVE; |
432 | } |
433 | |
434 | static void |
435 | run_service_loop (GMainContext *service_context) |
436 | { |
437 | GMainLoop *loop; |
438 | GSource *source; |
439 | |
440 | g_assert (service_loop == NULL); |
441 | |
442 | loop = g_main_loop_new (context: service_context, FALSE); |
443 | source = g_idle_source_new (); |
444 | g_source_set_callback (source, func: idle_in_service_loop, data: loop, NULL); |
445 | g_source_attach (source, context: service_context); |
446 | g_source_unref (source); |
447 | g_main_loop_run (loop); |
448 | } |
449 | |
450 | static void |
451 | teardown_service_loop (void) |
452 | { |
453 | g_mutex_lock (mutex: &service_loop_lock); |
454 | g_clear_pointer (&service_loop, g_main_loop_unref); |
455 | g_mutex_unlock (mutex: &service_loop_lock); |
456 | } |
457 | |
458 | static void |
459 | await_service_loop (void) |
460 | { |
461 | g_mutex_lock (mutex: &service_loop_lock); |
462 | while (service_loop == NULL) |
463 | g_cond_wait (cond: &service_loop_cond, mutex: &service_loop_lock); |
464 | g_mutex_unlock (mutex: &service_loop_lock); |
465 | } |
466 | |
467 | static gpointer |
468 | service_thread_func (gpointer user_data) |
469 | { |
470 | PeerData *data = user_data; |
471 | GMainContext *service_context; |
472 | GDBusAuthObserver *observer, *o; |
473 | GError *error; |
474 | GDBusServerFlags f; |
475 | gchar *a, *g; |
476 | gboolean b; |
477 | |
478 | service_context = g_main_context_new (); |
479 | g_main_context_push_thread_default (context: service_context); |
480 | |
481 | error = NULL; |
482 | observer = g_dbus_auth_observer_new (); |
483 | server = g_dbus_server_new_sync (address: tmp_address, |
484 | flags: G_DBUS_SERVER_FLAGS_NONE, |
485 | guid: test_guid, |
486 | observer, |
487 | NULL, /* cancellable */ |
488 | error: &error); |
489 | g_assert_no_error (error); |
490 | |
491 | g_signal_connect (server, |
492 | "new-connection" , |
493 | G_CALLBACK (on_new_connection), |
494 | data); |
495 | g_signal_connect (observer, |
496 | "authorize-authenticated-peer" , |
497 | G_CALLBACK (on_authorize_authenticated_peer), |
498 | data); |
499 | |
500 | g_assert_cmpint (g_dbus_server_get_flags (server), ==, G_DBUS_SERVER_FLAGS_NONE); |
501 | g_assert_cmpstr (g_dbus_server_get_guid (server), ==, test_guid); |
502 | g_object_get (object: server, |
503 | first_property_name: "flags" , &f, |
504 | "address" , &a, |
505 | "guid" , &g, |
506 | "active" , &b, |
507 | "authentication-observer" , &o, |
508 | NULL); |
509 | g_assert_cmpint (f, ==, G_DBUS_SERVER_FLAGS_NONE); |
510 | g_assert_cmpstr (a, ==, tmp_address); |
511 | g_assert_cmpstr (g, ==, test_guid); |
512 | g_assert (!b); |
513 | g_assert (o == observer); |
514 | g_free (mem: a); |
515 | g_free (mem: g); |
516 | g_object_unref (object: o); |
517 | |
518 | g_object_unref (object: observer); |
519 | |
520 | g_dbus_server_start (server); |
521 | |
522 | run_service_loop (service_context); |
523 | |
524 | g_main_context_pop_thread_default (context: service_context); |
525 | |
526 | teardown_service_loop (); |
527 | g_main_context_unref (context: service_context); |
528 | |
529 | /* test code specifically unrefs the server - see below */ |
530 | g_assert (server == NULL); |
531 | |
532 | return NULL; |
533 | } |
534 | |
535 | #if 0 |
536 | static gboolean |
537 | on_incoming_connection (GSocketService *service, |
538 | GSocketConnection *socket_connection, |
539 | GObject *source_object, |
540 | gpointer user_data) |
541 | { |
542 | PeerData *data = user_data; |
543 | |
544 | if (data->accept_connection) |
545 | { |
546 | GError *error; |
547 | guint reg_id; |
548 | GDBusConnection *connection; |
549 | |
550 | error = NULL; |
551 | connection = g_dbus_connection_new_sync (G_IO_STREAM (socket_connection), |
552 | test_guid, |
553 | G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER, |
554 | NULL, /* cancellable */ |
555 | &error); |
556 | g_assert_no_error (error); |
557 | |
558 | g_ptr_array_add (data->current_connections, connection); |
559 | |
560 | /* export object on the newly established connection */ |
561 | error = NULL; |
562 | reg_id = g_dbus_connection_register_object (connection, |
563 | "/org/gtk/GDBus/PeerTestObject" , |
564 | &test_interface_introspection_data, |
565 | &test_interface_vtable, |
566 | data, |
567 | NULL, /* GDestroyNotify for data */ |
568 | &error); |
569 | g_assert_no_error (error); |
570 | g_assert (reg_id > 0); |
571 | |
572 | } |
573 | else |
574 | { |
575 | /* don't do anything */ |
576 | } |
577 | |
578 | data->num_connection_attempts++; |
579 | |
580 | g_main_loop_quit (loop); |
581 | |
582 | /* stops other signal handlers from being invoked */ |
583 | return TRUE; |
584 | } |
585 | |
586 | static gpointer |
587 | service_thread_func (gpointer data) |
588 | { |
589 | GMainContext *service_context; |
590 | gchar *socket_path; |
591 | GSocketAddress *address; |
592 | GError *error; |
593 | |
594 | service_context = g_main_context_new (); |
595 | g_main_context_push_thread_default (service_context); |
596 | |
597 | socket_path = g_strdup_printf ("/tmp/gdbus-test-pid-%d" , getpid ()); |
598 | address = g_unix_socket_address_new (socket_path); |
599 | |
600 | service = g_socket_service_new (); |
601 | error = NULL; |
602 | g_socket_listener_add_address (G_SOCKET_LISTENER (service), |
603 | address, |
604 | G_SOCKET_TYPE_STREAM, |
605 | G_SOCKET_PROTOCOL_DEFAULT, |
606 | NULL, /* source_object */ |
607 | NULL, /* effective_address */ |
608 | &error); |
609 | g_assert_no_error (error); |
610 | g_signal_connect (service, |
611 | "incoming" , |
612 | G_CALLBACK (on_incoming_connection), |
613 | data); |
614 | g_socket_service_start (service); |
615 | |
616 | run_service_loop (service_context); |
617 | |
618 | g_main_context_pop_thread_default (service_context); |
619 | |
620 | teardown_service_loop (); |
621 | g_main_context_unref (service_context); |
622 | |
623 | g_object_unref (address); |
624 | g_free (socket_path); |
625 | return NULL; |
626 | } |
627 | #endif |
628 | |
629 | /* ---------------------------------------------------------------------------------------------------- */ |
630 | |
631 | #if 0 |
632 | static gboolean |
633 | check_connection (gpointer user_data) |
634 | { |
635 | PeerData *data = user_data; |
636 | guint n; |
637 | |
638 | for (n = 0; n < data->current_connections->len; n++) |
639 | { |
640 | GDBusConnection *c; |
641 | GIOStream *stream; |
642 | |
643 | c = G_DBUS_CONNECTION (data->current_connections->pdata[n]); |
644 | stream = g_dbus_connection_get_stream (c); |
645 | |
646 | g_debug ("In check_connection for %d: connection %p, stream %p" , n, c, stream); |
647 | g_debug ("closed = %d" , g_io_stream_is_closed (stream)); |
648 | |
649 | GSocket *socket; |
650 | socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream)); |
651 | g_debug ("socket_closed = %d" , g_socket_is_closed (socket)); |
652 | g_debug ("socket_condition_check = %d" , g_socket_condition_check (socket, G_IO_IN|G_IO_OUT|G_IO_ERR|G_IO_HUP)); |
653 | |
654 | gchar buf[128]; |
655 | GError *error; |
656 | gssize num_read; |
657 | error = NULL; |
658 | num_read = g_input_stream_read (g_io_stream_get_input_stream (stream), |
659 | buf, |
660 | 128, |
661 | NULL, |
662 | &error); |
663 | if (num_read < 0) |
664 | { |
665 | g_debug ("error: %s" , error->message); |
666 | g_error_free (error); |
667 | } |
668 | else |
669 | { |
670 | g_debug ("no error, read %d bytes" , (gint) num_read); |
671 | } |
672 | } |
673 | |
674 | return G_SOURCE_REMOVE; |
675 | } |
676 | |
677 | static gboolean |
678 | on_do_disconnect_in_idle (gpointer data) |
679 | { |
680 | GDBusConnection *c = G_DBUS_CONNECTION (data); |
681 | g_debug ("GDC %p has ref_count %d" , c, G_OBJECT (c)->ref_count); |
682 | g_dbus_connection_disconnect (c); |
683 | g_object_unref (c); |
684 | return G_SOURCE_REMOVE; |
685 | } |
686 | #endif |
687 | |
688 | #ifdef G_OS_UNIX |
689 | static gchar * |
690 | read_all_from_fd (gint fd, gsize *out_len, GError **error) |
691 | { |
692 | GString *str; |
693 | gchar buf[64]; |
694 | gssize num_read; |
695 | |
696 | str = g_string_new (NULL); |
697 | |
698 | do |
699 | { |
700 | int errsv; |
701 | |
702 | num_read = read (fd: fd, buf: buf, nbytes: sizeof (buf)); |
703 | errsv = errno; |
704 | if (num_read == -1) |
705 | { |
706 | if (errsv == EAGAIN || errsv == EWOULDBLOCK) |
707 | continue; |
708 | g_set_error (err: error, |
709 | G_IO_ERROR, |
710 | code: g_io_error_from_errno (err_no: errsv), |
711 | format: "Failed reading %d bytes into offset %d: %s" , |
712 | (gint) sizeof (buf), |
713 | (gint) str->len, |
714 | g_strerror (errnum: errsv)); |
715 | goto error; |
716 | } |
717 | else if (num_read > 0) |
718 | { |
719 | g_string_append_len (string: str, val: buf, len: num_read); |
720 | } |
721 | else if (num_read == 0) |
722 | { |
723 | break; |
724 | } |
725 | } |
726 | while (TRUE); |
727 | |
728 | if (out_len != NULL) |
729 | *out_len = str->len; |
730 | return g_string_free (string: str, FALSE); |
731 | |
732 | error: |
733 | if (out_len != NULL) |
734 | *out_len = 0; |
735 | g_string_free (string: str, TRUE); |
736 | return NULL; |
737 | } |
738 | #endif |
739 | |
740 | static void |
741 | do_test_peer (void) |
742 | { |
743 | GDBusConnection *c; |
744 | GDBusConnection *c2; |
745 | GDBusProxy *proxy; |
746 | GError *error; |
747 | PeerData data; |
748 | GVariant *value; |
749 | GVariant *result; |
750 | const gchar *s; |
751 | GThread *service_thread; |
752 | gulong signal_handler_id; |
753 | gsize i; |
754 | |
755 | memset (s: &data, c: '\0', n: sizeof (PeerData)); |
756 | data.current_connections = g_ptr_array_new_with_free_func (element_free_func: g_object_unref); |
757 | |
758 | /* first try to connect when there is no server */ |
759 | error = NULL; |
760 | c = g_dbus_connection_new_for_address_sync (address: is_unix ? "unix:path=/tmp/gdbus-test-does-not-exist-pid" : |
761 | /* NOTE: Even if something is listening on port 12345 the connection |
762 | * will fail because the nonce file doesn't exist */ |
763 | "nonce-tcp:host=127.0.0.1,port=12345,noncefile=this-does-not-exist-gdbus" , |
764 | flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, |
765 | NULL, /* GDBusAuthObserver */ |
766 | NULL, /* cancellable */ |
767 | error: &error); |
768 | _g_assert_error_domain (error, G_IO_ERROR); |
769 | g_assert (!g_dbus_error_is_remote_error (error)); |
770 | g_clear_error (err: &error); |
771 | g_assert (c == NULL); |
772 | |
773 | /* bring up a server - we run the server in a different thread to avoid deadlocks */ |
774 | service_thread = g_thread_new (name: "test_peer" , |
775 | func: service_thread_func, |
776 | data: &data); |
777 | await_service_loop (); |
778 | g_assert (server != NULL); |
779 | |
780 | /* bring up a connection and accept it */ |
781 | data.accept_connection = TRUE; |
782 | error = NULL; |
783 | c = g_dbus_connection_new_for_address_sync (address: g_dbus_server_get_client_address (server), |
784 | flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, |
785 | NULL, /* GDBusAuthObserver */ |
786 | NULL, /* cancellable */ |
787 | error: &error); |
788 | g_assert_no_error (error); |
789 | g_assert (c != NULL); |
790 | while (data.current_connections->len < 1) |
791 | g_main_loop_run (loop); |
792 | g_assert_cmpint (data.current_connections->len, ==, 1); |
793 | g_assert_cmpint (data.num_connection_attempts, ==, 1); |
794 | g_assert (g_dbus_connection_get_unique_name (c) == NULL); |
795 | g_assert_cmpstr (g_dbus_connection_get_guid (c), ==, test_guid); |
796 | |
797 | /* check that we create a proxy, read properties, receive signals and invoke |
798 | * the HelloPeer() method. Since the server runs in another thread it's fine |
799 | * to use synchronous blocking API here. |
800 | */ |
801 | error = NULL; |
802 | proxy = g_dbus_proxy_new_sync (connection: c, |
803 | flags: G_DBUS_PROXY_FLAGS_NONE, |
804 | NULL, |
805 | NULL, /* bus_name */ |
806 | object_path: "/org/gtk/GDBus/PeerTestObject" , |
807 | interface_name: "org.gtk.GDBus.PeerTestInterface" , |
808 | NULL, /* GCancellable */ |
809 | error: &error); |
810 | g_assert_no_error (error); |
811 | g_assert (proxy != NULL); |
812 | error = NULL; |
813 | value = g_dbus_proxy_get_cached_property (proxy, property_name: "PeerProperty" ); |
814 | g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "ThePropertyValue" ); |
815 | |
816 | /* try invoking a method */ |
817 | error = NULL; |
818 | result = g_dbus_proxy_call_sync (proxy, |
819 | method_name: "HelloPeer" , |
820 | parameters: g_variant_new (format_string: "(s)" , "Hey Peer!" ), |
821 | flags: G_DBUS_CALL_FLAGS_NONE, |
822 | timeout_msec: -1, |
823 | NULL, /* GCancellable */ |
824 | error: &error); |
825 | g_assert_no_error (error); |
826 | g_variant_get (value: result, format_string: "(&s)" , &s); |
827 | g_assert_cmpstr (s, ==, "You greeted me with 'Hey Peer!'." ); |
828 | g_variant_unref (value: result); |
829 | g_assert_cmpint (data.num_method_calls, ==, 1); |
830 | |
831 | /* make the other peer emit a signal - catch it */ |
832 | signal_handler_id = g_signal_connect (proxy, |
833 | "g-signal" , |
834 | G_CALLBACK (on_proxy_signal_received), |
835 | &data); |
836 | g_assert (!data.signal_received); |
837 | g_dbus_proxy_call (proxy, |
838 | method_name: "EmitSignal" , |
839 | NULL, /* no arguments */ |
840 | flags: G_DBUS_CALL_FLAGS_NONE, |
841 | timeout_msec: -1, |
842 | NULL, /* GCancellable */ |
843 | NULL, /* GAsyncReadyCallback - we don't care about the result */ |
844 | NULL); /* user_data */ |
845 | g_main_loop_run (loop); |
846 | g_assert (data.signal_received); |
847 | g_assert_cmpint (data.num_method_calls, ==, 2); |
848 | g_signal_handler_disconnect (instance: proxy, handler_id: signal_handler_id); |
849 | |
850 | /* Also ensure that messages with the sender header-field set gets |
851 | * delivered to the proxy - note that this doesn't really make sense |
852 | * e.g. names are meaning-less in a peer-to-peer case... but we |
853 | * support it because it makes sense in certain bridging |
854 | * applications - see e.g. #623815. |
855 | */ |
856 | signal_handler_id = g_signal_connect (proxy, |
857 | "g-signal" , |
858 | G_CALLBACK (on_proxy_signal_received_with_name_set), |
859 | &data); |
860 | data.signal_received = FALSE; |
861 | g_dbus_proxy_call (proxy, |
862 | method_name: "EmitSignalWithNameSet" , |
863 | NULL, /* no arguments */ |
864 | flags: G_DBUS_CALL_FLAGS_NONE, |
865 | timeout_msec: -1, |
866 | NULL, /* GCancellable */ |
867 | NULL, /* GAsyncReadyCallback - we don't care about the result */ |
868 | NULL); /* user_data */ |
869 | g_main_loop_run (loop); |
870 | g_assert (data.signal_received); |
871 | g_assert_cmpint (data.num_method_calls, ==, 3); |
872 | g_signal_handler_disconnect (instance: proxy, handler_id: signal_handler_id); |
873 | |
874 | /* |
875 | * Check for UNIX fd passing. |
876 | * |
877 | * The first time through, we use a very simple method call. Note that |
878 | * because this does not have a G_VARIANT_TYPE_HANDLE in the message body |
879 | * to refer to the fd, it is a GDBus-specific idiom that would not |
880 | * interoperate with libdbus or sd-bus |
881 | * (see <https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1726>). |
882 | * |
883 | * The second time, we call a method that returns a fd attached to a |
884 | * large message, to reproduce |
885 | * <https://gitlab.gnome.org/GNOME/glib/-/issues/2074>. It also happens |
886 | * to follow the more usual pattern for D-Bus messages containing a |
887 | * G_VARIANT_TYPE_HANDLE to refer to attached fds. |
888 | */ |
889 | for (i = 0; i < 2; i++) |
890 | { |
891 | #ifdef G_OS_UNIX |
892 | GDBusMessage *method_call_message; |
893 | GDBusMessage *method_reply_message; |
894 | GUnixFDList *fd_list; |
895 | gint fd; |
896 | gchar *buf; |
897 | gsize len; |
898 | gchar *buf2; |
899 | gsize len2; |
900 | const char *testfile = g_test_get_filename (file_type: G_TEST_DIST, first_path: "file.c" , NULL); |
901 | const char *method = "OpenFile" ; |
902 | GVariant *body; |
903 | |
904 | if (i == 1) |
905 | method = "OpenFileWithBigMessage" ; |
906 | |
907 | method_call_message = g_dbus_message_new_method_call (NULL, /* name */ |
908 | path: "/org/gtk/GDBus/PeerTestObject" , |
909 | interface_: "org.gtk.GDBus.PeerTestInterface" , |
910 | method); |
911 | g_dbus_message_set_body (message: method_call_message, body: g_variant_new (format_string: "(s)" , testfile)); |
912 | error = NULL; |
913 | method_reply_message = g_dbus_connection_send_message_with_reply_sync (connection: c, |
914 | message: method_call_message, |
915 | flags: G_DBUS_SEND_MESSAGE_FLAGS_NONE, |
916 | timeout_msec: -1, |
917 | NULL, /* out_serial */ |
918 | NULL, /* cancellable */ |
919 | error: &error); |
920 | g_assert_no_error (error); |
921 | g_assert (g_dbus_message_get_message_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_METHOD_RETURN); |
922 | |
923 | body = g_dbus_message_get_body (message: method_reply_message); |
924 | |
925 | if (i == 1) |
926 | { |
927 | gint32 handle = -1; |
928 | GVariant *junk = NULL; |
929 | |
930 | g_assert_cmpstr (g_variant_get_type_string (body), ==, "(hay)" ); |
931 | g_variant_get (value: body, format_string: "(h@ay)" , &handle, &junk); |
932 | g_assert_cmpint (handle, ==, 0); |
933 | g_assert_cmpuint (g_variant_n_children (junk), ==, BIG_MESSAGE_ARRAY_SIZE); |
934 | g_variant_unref (value: junk); |
935 | } |
936 | else |
937 | { |
938 | g_assert_null (body); |
939 | } |
940 | |
941 | fd_list = g_dbus_message_get_unix_fd_list (message: method_reply_message); |
942 | g_assert (fd_list != NULL); |
943 | g_assert_cmpint (g_unix_fd_list_get_length (fd_list), ==, 1); |
944 | error = NULL; |
945 | fd = g_unix_fd_list_get (list: fd_list, index_: 0, error: &error); |
946 | g_assert_no_error (error); |
947 | g_object_unref (object: method_call_message); |
948 | g_object_unref (object: method_reply_message); |
949 | |
950 | error = NULL; |
951 | len = 0; |
952 | buf = read_all_from_fd (fd, out_len: &len, error: &error); |
953 | g_assert_no_error (error); |
954 | g_assert (buf != NULL); |
955 | close (fd: fd); |
956 | |
957 | error = NULL; |
958 | g_file_get_contents (filename: testfile, |
959 | contents: &buf2, |
960 | length: &len2, |
961 | error: &error); |
962 | g_assert_no_error (error); |
963 | g_assert_cmpmem (buf, len, buf2, len2); |
964 | g_free (mem: buf2); |
965 | g_free (mem: buf); |
966 | #else |
967 | /* We do the same number of iterations on non-Unix, so that |
968 | * the method call count will match. In this case we use |
969 | * OpenFile both times, because the difference between this |
970 | * and OpenFileWithBigMessage is only relevant on Unix. */ |
971 | error = NULL; |
972 | result = g_dbus_proxy_call_sync (proxy, |
973 | "OpenFile" , |
974 | g_variant_new ("(s)" , "boo" ), |
975 | G_DBUS_CALL_FLAGS_NONE, |
976 | -1, |
977 | NULL, /* GCancellable */ |
978 | &error); |
979 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); |
980 | g_assert (result == NULL); |
981 | g_error_free (error); |
982 | #endif /* G_OS_UNIX */ |
983 | } |
984 | |
985 | /* Check that g_socket_get_credentials() work - (though this really |
986 | * should be in socket.c) |
987 | */ |
988 | { |
989 | GSocket *socket; |
990 | GCredentials *credentials; |
991 | socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (g_dbus_connection_get_stream (c))); |
992 | g_assert (G_IS_SOCKET (socket)); |
993 | error = NULL; |
994 | credentials = g_socket_get_credentials (socket, error: &error); |
995 | |
996 | #if G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED |
997 | g_assert_no_error (error); |
998 | g_assert (G_IS_CREDENTIALS (credentials)); |
999 | |
1000 | g_assert_cmpuint (g_credentials_get_unix_user (credentials, NULL), ==, |
1001 | getuid ()); |
1002 | #if G_CREDENTIALS_HAS_PID |
1003 | g_assert_cmpint (g_credentials_get_unix_pid (credentials, &error), ==, |
1004 | getpid ()); |
1005 | g_assert_no_error (error); |
1006 | #else |
1007 | g_assert_cmpint (g_credentials_get_unix_pid (credentials, &error), ==, -1); |
1008 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); |
1009 | g_clear_error (&error); |
1010 | #endif |
1011 | g_object_unref (object: credentials); |
1012 | #else |
1013 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); |
1014 | g_assert (credentials == NULL); |
1015 | #endif |
1016 | } |
1017 | |
1018 | |
1019 | /* bring up a connection - don't accept it - this should fail |
1020 | */ |
1021 | data.accept_connection = FALSE; |
1022 | error = NULL; |
1023 | c2 = g_dbus_connection_new_for_address_sync (address: g_dbus_server_get_client_address (server), |
1024 | flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, |
1025 | NULL, /* GDBusAuthObserver */ |
1026 | NULL, /* cancellable */ |
1027 | error: &error); |
1028 | _g_assert_error_domain (error, G_IO_ERROR); |
1029 | g_error_free (error); |
1030 | g_assert (c2 == NULL); |
1031 | |
1032 | #if 0 |
1033 | /* TODO: THIS TEST DOESN'T WORK YET */ |
1034 | |
1035 | /* bring up a connection - accept it.. then disconnect from the client side - check |
1036 | * that the server side gets the disconnect signal. |
1037 | */ |
1038 | error = NULL; |
1039 | data.accept_connection = TRUE; |
1040 | c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server), |
1041 | G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, |
1042 | NULL, /* GDBusAuthObserver */ |
1043 | NULL, /* cancellable */ |
1044 | &error); |
1045 | g_assert_no_error (error); |
1046 | g_assert (c2 != NULL); |
1047 | g_assert (!g_dbus_connection_get_is_disconnected (c2)); |
1048 | while (data.num_connection_attempts < 3) |
1049 | g_main_loop_run (loop); |
1050 | g_assert_cmpint (data.current_connections->len, ==, 2); |
1051 | g_assert_cmpint (data.num_connection_attempts, ==, 3); |
1052 | g_assert (!g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1]))); |
1053 | g_idle_add (on_do_disconnect_in_idle, c2); |
1054 | g_debug ("==================================================" ); |
1055 | g_debug ("==================================================" ); |
1056 | g_debug ("==================================================" ); |
1057 | g_debug ("waiting for disconnect on connection %p, stream %p" , |
1058 | data.current_connections->pdata[1], |
1059 | g_dbus_connection_get_stream (data.current_connections->pdata[1])); |
1060 | |
1061 | g_timeout_add (2000, check_connection, &data); |
1062 | //_g_assert_signal_received (G_DBUS_CONNECTION (data.current_connections->pdata[1]), "closed"); |
1063 | g_main_loop_run (loop); |
1064 | g_assert (g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1]))); |
1065 | g_ptr_array_set_size (data.current_connections, 1); /* remove disconnected connection object */ |
1066 | #endif |
1067 | |
1068 | /* unref the server and stop listening for new connections |
1069 | * |
1070 | * This won't bring down the established connections - check that c is still connected |
1071 | * by invoking a method |
1072 | */ |
1073 | //g_socket_service_stop (service); |
1074 | //g_object_unref (service); |
1075 | g_dbus_server_stop (server); |
1076 | g_object_unref (object: server); |
1077 | server = NULL; |
1078 | |
1079 | error = NULL; |
1080 | result = g_dbus_proxy_call_sync (proxy, |
1081 | method_name: "HelloPeer" , |
1082 | parameters: g_variant_new (format_string: "(s)" , "Hey Again Peer!" ), |
1083 | flags: G_DBUS_CALL_FLAGS_NONE, |
1084 | timeout_msec: -1, |
1085 | NULL, /* GCancellable */ |
1086 | error: &error); |
1087 | g_assert_no_error (error); |
1088 | g_variant_get (value: result, format_string: "(&s)" , &s); |
1089 | g_assert_cmpstr (s, ==, "You greeted me with 'Hey Again Peer!'." ); |
1090 | g_variant_unref (value: result); |
1091 | g_assert_cmpint (data.num_method_calls, ==, 6); |
1092 | |
1093 | #if 0 |
1094 | /* TODO: THIS TEST DOESN'T WORK YET */ |
1095 | |
1096 | /* now disconnect from the server side - check that the client side gets the signal */ |
1097 | g_assert_cmpint (data.current_connections->len, ==, 1); |
1098 | g_assert (G_DBUS_CONNECTION (data.current_connections->pdata[0]) != c); |
1099 | g_dbus_connection_disconnect (G_DBUS_CONNECTION (data.current_connections->pdata[0])); |
1100 | if (!g_dbus_connection_get_is_disconnected (c)) |
1101 | _g_assert_signal_received (c, "closed" ); |
1102 | g_assert (g_dbus_connection_get_is_disconnected (c)); |
1103 | #endif |
1104 | |
1105 | g_object_unref (object: c); |
1106 | g_ptr_array_unref (array: data.current_connections); |
1107 | g_object_unref (object: proxy); |
1108 | |
1109 | g_main_loop_quit (loop: service_loop); |
1110 | g_thread_join (thread: service_thread); |
1111 | } |
1112 | |
1113 | static void |
1114 | test_peer (void) |
1115 | { |
1116 | test_guid = g_dbus_generate_guid (); |
1117 | loop = g_main_loop_new (NULL, FALSE); |
1118 | |
1119 | /* Run this test multiple times using different address formats to ensure |
1120 | * they all work. |
1121 | */ |
1122 | setup_test_address (); |
1123 | do_test_peer (); |
1124 | teardown_test_address (); |
1125 | |
1126 | #ifdef G_OS_UNIX |
1127 | setup_tmpdir_test_address (); |
1128 | do_test_peer (); |
1129 | teardown_test_address (); |
1130 | |
1131 | setup_path_test_address (); |
1132 | do_test_peer (); |
1133 | teardown_test_address (); |
1134 | #endif |
1135 | |
1136 | g_main_loop_unref (loop); |
1137 | g_free (mem: test_guid); |
1138 | } |
1139 | |
1140 | /* ---------------------------------------------------------------------------------------------------- */ |
1141 | |
1142 | #define VALID_GUID "0123456789abcdef0123456789abcdef" |
1143 | |
1144 | static void |
1145 | test_peer_invalid_server (void) |
1146 | { |
1147 | GDBusServer *server; |
1148 | |
1149 | if (!g_test_undefined ()) |
1150 | { |
1151 | g_test_skip (msg: "Not exercising programming errors" ); |
1152 | return; |
1153 | } |
1154 | |
1155 | if (g_test_subprocess ()) |
1156 | { |
1157 | /* This assumes we are not going to run out of GDBusServerFlags |
1158 | * any time soon */ |
1159 | server = g_dbus_server_new_sync (address: "tcp:" , flags: (GDBusServerFlags) (1 << 30), |
1160 | VALID_GUID, |
1161 | NULL, NULL, NULL); |
1162 | g_assert_null (server); |
1163 | } |
1164 | else |
1165 | { |
1166 | g_test_trap_subprocess (NULL, usec_timeout: 0, test_flags: 0); |
1167 | g_test_trap_assert_failed (); |
1168 | g_test_trap_assert_stderr ("*CRITICAL*G_DBUS_SERVER_FLAGS_ALL*" ); |
1169 | } |
1170 | } |
1171 | |
1172 | static void |
1173 | test_peer_invalid_conn_stream_sync (void) |
1174 | { |
1175 | GSocket *sock; |
1176 | GSocketConnection *socket_conn; |
1177 | GIOStream *iostream; |
1178 | GDBusConnection *conn; |
1179 | |
1180 | if (!g_test_undefined ()) |
1181 | { |
1182 | g_test_skip (msg: "Not exercising programming errors" ); |
1183 | return; |
1184 | } |
1185 | |
1186 | sock = g_socket_new (family: G_SOCKET_FAMILY_IPV4, |
1187 | type: G_SOCKET_TYPE_STREAM, |
1188 | protocol: G_SOCKET_PROTOCOL_TCP, |
1189 | NULL); |
1190 | |
1191 | if (sock == NULL) |
1192 | { |
1193 | g_test_skip (msg: "TCP not available?" ); |
1194 | return; |
1195 | } |
1196 | |
1197 | socket_conn = g_socket_connection_factory_create_connection (socket: sock); |
1198 | g_assert_nonnull (socket_conn); |
1199 | iostream = G_IO_STREAM (socket_conn); |
1200 | g_assert_nonnull (iostream); |
1201 | |
1202 | if (g_test_subprocess ()) |
1203 | { |
1204 | /* This assumes we are not going to run out of GDBusConnectionFlags |
1205 | * any time soon */ |
1206 | conn = g_dbus_connection_new_sync (stream: iostream, VALID_GUID, |
1207 | flags: (GDBusConnectionFlags) (1 << 30), |
1208 | NULL, NULL, NULL); |
1209 | g_assert_null (conn); |
1210 | } |
1211 | else |
1212 | { |
1213 | g_test_trap_subprocess (NULL, usec_timeout: 0, test_flags: 0); |
1214 | g_test_trap_assert_failed (); |
1215 | g_test_trap_assert_stderr ("*CRITICAL*G_DBUS_CONNECTION_FLAGS_ALL*" ); |
1216 | } |
1217 | |
1218 | g_clear_object (&sock); |
1219 | g_clear_object (&socket_conn); |
1220 | } |
1221 | |
1222 | static void |
1223 | test_peer_invalid_conn_stream_async (void) |
1224 | { |
1225 | GSocket *sock; |
1226 | GSocketConnection *socket_conn; |
1227 | GIOStream *iostream; |
1228 | |
1229 | if (!g_test_undefined ()) |
1230 | { |
1231 | g_test_skip (msg: "Not exercising programming errors" ); |
1232 | return; |
1233 | } |
1234 | |
1235 | sock = g_socket_new (family: G_SOCKET_FAMILY_IPV4, |
1236 | type: G_SOCKET_TYPE_STREAM, |
1237 | protocol: G_SOCKET_PROTOCOL_TCP, |
1238 | NULL); |
1239 | |
1240 | if (sock == NULL) |
1241 | { |
1242 | g_test_skip (msg: "TCP not available?" ); |
1243 | return; |
1244 | } |
1245 | |
1246 | socket_conn = g_socket_connection_factory_create_connection (socket: sock); |
1247 | g_assert_nonnull (socket_conn); |
1248 | iostream = G_IO_STREAM (socket_conn); |
1249 | g_assert_nonnull (iostream); |
1250 | |
1251 | if (g_test_subprocess ()) |
1252 | { |
1253 | g_dbus_connection_new (stream: iostream, VALID_GUID, |
1254 | flags: (GDBusConnectionFlags) (1 << 30), |
1255 | NULL, NULL, NULL, NULL); |
1256 | } |
1257 | else |
1258 | { |
1259 | g_test_trap_subprocess (NULL, usec_timeout: 0, test_flags: 0); |
1260 | g_test_trap_assert_failed (); |
1261 | g_test_trap_assert_stderr ("*CRITICAL*G_DBUS_CONNECTION_FLAGS_ALL*" ); |
1262 | } |
1263 | |
1264 | g_clear_object (&sock); |
1265 | g_clear_object (&socket_conn); |
1266 | } |
1267 | |
1268 | static void |
1269 | test_peer_invalid_conn_addr_sync (void) |
1270 | { |
1271 | GDBusConnection *conn; |
1272 | |
1273 | if (!g_test_undefined ()) |
1274 | { |
1275 | g_test_skip (msg: "Not exercising programming errors" ); |
1276 | return; |
1277 | } |
1278 | |
1279 | if (g_test_subprocess ()) |
1280 | { |
1281 | conn = g_dbus_connection_new_for_address_sync (address: "tcp:" , |
1282 | flags: (GDBusConnectionFlags) (1 << 30), |
1283 | NULL, NULL, NULL); |
1284 | g_assert_null (conn); |
1285 | } |
1286 | else |
1287 | { |
1288 | g_test_trap_subprocess (NULL, usec_timeout: 0, test_flags: 0); |
1289 | g_test_trap_assert_failed (); |
1290 | g_test_trap_assert_stderr ("*CRITICAL*G_DBUS_CONNECTION_FLAGS_ALL*" ); |
1291 | } |
1292 | } |
1293 | |
1294 | static void |
1295 | test_peer_invalid_conn_addr_async (void) |
1296 | { |
1297 | if (!g_test_undefined ()) |
1298 | { |
1299 | g_test_skip (msg: "Not exercising programming errors" ); |
1300 | return; |
1301 | } |
1302 | |
1303 | if (g_test_subprocess ()) |
1304 | { |
1305 | g_dbus_connection_new_for_address (address: "tcp:" , |
1306 | flags: (GDBusConnectionFlags) (1 << 30), |
1307 | NULL, NULL, NULL, NULL); |
1308 | } |
1309 | else |
1310 | { |
1311 | g_test_trap_subprocess (NULL, usec_timeout: 0, test_flags: 0); |
1312 | g_test_trap_assert_failed (); |
1313 | g_test_trap_assert_stderr ("*CRITICAL*G_DBUS_CONNECTION_FLAGS_ALL*" ); |
1314 | } |
1315 | } |
1316 | |
1317 | /* ---------------------------------------------------------------------------------------------------- */ |
1318 | |
1319 | static void |
1320 | test_peer_signals (void) |
1321 | { |
1322 | GDBusConnection *c; |
1323 | GDBusProxy *proxy; |
1324 | GError *error = NULL; |
1325 | PeerData data; |
1326 | GThread *service_thread; |
1327 | |
1328 | g_test_bug (bug_uri_snippet: "https://gitlab.gnome.org/GNOME/glib/issues/1620" ); |
1329 | |
1330 | test_guid = g_dbus_generate_guid (); |
1331 | loop = g_main_loop_new (NULL, FALSE); |
1332 | |
1333 | setup_test_address (); |
1334 | memset (s: &data, c: '\0', n: sizeof (PeerData)); |
1335 | data.current_connections = g_ptr_array_new_with_free_func (element_free_func: g_object_unref); |
1336 | |
1337 | /* bring up a server - we run the server in a different thread to avoid deadlocks */ |
1338 | service_thread = g_thread_new (name: "test_peer" , |
1339 | func: service_thread_func, |
1340 | data: &data); |
1341 | await_service_loop (); |
1342 | g_assert_nonnull (server); |
1343 | |
1344 | /* bring up a connection and accept it */ |
1345 | data.accept_connection = TRUE; |
1346 | c = g_dbus_connection_new_for_address_sync (address: g_dbus_server_get_client_address (server), |
1347 | flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, |
1348 | NULL, /* GDBusAuthObserver */ |
1349 | NULL, /* cancellable */ |
1350 | error: &error); |
1351 | g_assert_no_error (error); |
1352 | g_assert_nonnull (c); |
1353 | while (data.current_connections->len < 1) |
1354 | g_main_loop_run (loop); |
1355 | g_assert_cmpint (data.current_connections->len, ==, 1); |
1356 | g_assert_cmpint (data.num_connection_attempts, ==, 1); |
1357 | g_assert_null (g_dbus_connection_get_unique_name (c)); |
1358 | g_assert_cmpstr (g_dbus_connection_get_guid (c), ==, test_guid); |
1359 | |
1360 | /* Check that we can create a proxy with a non-NULL bus name, even though it's |
1361 | * irrelevant in the non-message-bus case. Since the server runs in another |
1362 | * thread it's fine to use synchronous blocking API here. |
1363 | */ |
1364 | proxy = g_dbus_proxy_new_sync (connection: c, |
1365 | flags: G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | |
1366 | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, |
1367 | NULL, |
1368 | name: ":1.1" , /* bus_name */ |
1369 | object_path: "/org/gtk/GDBus/PeerTestObject" , |
1370 | interface_name: "org.gtk.GDBus.PeerTestInterface" , |
1371 | NULL, /* GCancellable */ |
1372 | error: &error); |
1373 | g_assert_no_error (error); |
1374 | g_assert_nonnull (proxy); |
1375 | |
1376 | /* unref the server and stop listening for new connections */ |
1377 | g_dbus_server_stop (server); |
1378 | g_clear_object (&server); |
1379 | |
1380 | g_object_unref (object: c); |
1381 | g_ptr_array_unref (array: data.current_connections); |
1382 | g_object_unref (object: proxy); |
1383 | |
1384 | g_main_loop_quit (loop: service_loop); |
1385 | g_thread_join (thread: service_thread); |
1386 | |
1387 | teardown_test_address (); |
1388 | |
1389 | g_main_loop_unref (loop); |
1390 | g_free (mem: test_guid); |
1391 | } |
1392 | |
1393 | /* ---------------------------------------------------------------------------------------------------- */ |
1394 | |
1395 | typedef struct |
1396 | { |
1397 | GDBusServer *server; |
1398 | GMainContext *context; |
1399 | GMainLoop *loop; |
1400 | |
1401 | GList *connections; |
1402 | } DmpData; |
1403 | |
1404 | static void |
1405 | dmp_data_free (DmpData *data) |
1406 | { |
1407 | g_main_loop_unref (loop: data->loop); |
1408 | g_main_context_unref (context: data->context); |
1409 | g_object_unref (object: data->server); |
1410 | g_list_free_full (list: data->connections, free_func: g_object_unref); |
1411 | g_free (mem: data); |
1412 | } |
1413 | |
1414 | static void |
1415 | dmp_on_method_call (GDBusConnection *connection, |
1416 | const gchar *sender, |
1417 | const gchar *object_path, |
1418 | const gchar *interface_name, |
1419 | const gchar *method_name, |
1420 | GVariant *parameters, |
1421 | GDBusMethodInvocation *invocation, |
1422 | gpointer user_data) |
1423 | { |
1424 | //DmpData *data = user_data; |
1425 | gint32 first; |
1426 | gint32 second; |
1427 | g_variant_get (value: parameters, |
1428 | format_string: "(ii)" , |
1429 | &first, |
1430 | &second); |
1431 | g_dbus_method_invocation_return_value (invocation, |
1432 | parameters: g_variant_new (format_string: "(i)" , first + second)); |
1433 | } |
1434 | |
1435 | static const GDBusInterfaceVTable dmp_interface_vtable = |
1436 | { |
1437 | dmp_on_method_call, |
1438 | NULL, /* get_property */ |
1439 | NULL /* set_property */ |
1440 | }; |
1441 | |
1442 | |
1443 | /* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */ |
1444 | static gboolean |
1445 | dmp_on_new_connection (GDBusServer *server, |
1446 | GDBusConnection *connection, |
1447 | gpointer user_data) |
1448 | { |
1449 | DmpData *data = user_data; |
1450 | GDBusNodeInfo *node; |
1451 | GError *error; |
1452 | |
1453 | /* accept the connection */ |
1454 | data->connections = g_list_prepend (list: data->connections, g_object_ref (connection)); |
1455 | |
1456 | error = NULL; |
1457 | node = g_dbus_node_info_new_for_xml (xml_data: "<node>" |
1458 | " <interface name='org.gtk.GDBus.DmpInterface'>" |
1459 | " <method name='AddPair'>" |
1460 | " <arg type='i' name='first' direction='in'/>" |
1461 | " <arg type='i' name='second' direction='in'/>" |
1462 | " <arg type='i' name='sum' direction='out'/>" |
1463 | " </method>" |
1464 | " </interface>" |
1465 | "</node>" , |
1466 | error: &error); |
1467 | g_assert_no_error (error); |
1468 | |
1469 | /* sleep 100ms before exporting an object - this is to test that |
1470 | * G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING really works |
1471 | * (GDBusServer uses this feature). |
1472 | */ |
1473 | usleep (useconds: 100 * 1000); |
1474 | |
1475 | /* export an object */ |
1476 | error = NULL; |
1477 | g_dbus_connection_register_object (connection, |
1478 | object_path: "/dmp/test" , |
1479 | interface_info: node->interfaces[0], |
1480 | vtable: &dmp_interface_vtable, |
1481 | user_data: data, |
1482 | NULL, |
1483 | error: &error); |
1484 | g_dbus_node_info_unref (info: node); |
1485 | |
1486 | return TRUE; |
1487 | } |
1488 | |
1489 | static gpointer |
1490 | dmp_thread_func (gpointer user_data) |
1491 | { |
1492 | DmpData *data = user_data; |
1493 | GError *error; |
1494 | gchar *guid; |
1495 | |
1496 | data->context = g_main_context_new (); |
1497 | g_main_context_push_thread_default (context: data->context); |
1498 | |
1499 | error = NULL; |
1500 | guid = g_dbus_generate_guid (); |
1501 | data->server = g_dbus_server_new_sync (address: tmp_address, |
1502 | flags: G_DBUS_SERVER_FLAGS_NONE, |
1503 | guid, |
1504 | NULL, /* GDBusAuthObserver */ |
1505 | NULL, /* GCancellable */ |
1506 | error: &error); |
1507 | g_assert_no_error (error); |
1508 | g_signal_connect (data->server, |
1509 | "new-connection" , |
1510 | G_CALLBACK (dmp_on_new_connection), |
1511 | data); |
1512 | |
1513 | g_dbus_server_start (server: data->server); |
1514 | |
1515 | data->loop = g_main_loop_new (context: data->context, FALSE); |
1516 | g_main_loop_run (loop: data->loop); |
1517 | |
1518 | g_dbus_server_stop (server: data->server); |
1519 | g_main_context_pop_thread_default (context: data->context); |
1520 | |
1521 | g_free (mem: guid); |
1522 | return NULL; |
1523 | } |
1524 | |
1525 | static void |
1526 | delayed_message_processing (void) |
1527 | { |
1528 | GError *error; |
1529 | DmpData *data; |
1530 | GThread *service_thread; |
1531 | guint n; |
1532 | |
1533 | test_guid = g_dbus_generate_guid (); |
1534 | loop = g_main_loop_new (NULL, FALSE); |
1535 | |
1536 | setup_test_address (); |
1537 | |
1538 | data = g_new0 (DmpData, 1); |
1539 | |
1540 | service_thread = g_thread_new (name: "dmp" , |
1541 | func: dmp_thread_func, |
1542 | data); |
1543 | while (data->server == NULL || !g_dbus_server_is_active (server: data->server)) |
1544 | g_thread_yield (); |
1545 | |
1546 | for (n = 0; n < 5; n++) |
1547 | { |
1548 | GDBusConnection *c; |
1549 | GVariant *res; |
1550 | gint32 val; |
1551 | |
1552 | error = NULL; |
1553 | c = g_dbus_connection_new_for_address_sync (address: g_dbus_server_get_client_address (server: data->server), |
1554 | flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, |
1555 | NULL, /* GDBusAuthObserver */ |
1556 | NULL, /* GCancellable */ |
1557 | error: &error); |
1558 | g_assert_no_error (error); |
1559 | |
1560 | error = NULL; |
1561 | res = g_dbus_connection_call_sync (connection: c, |
1562 | NULL, /* bus name */ |
1563 | object_path: "/dmp/test" , |
1564 | interface_name: "org.gtk.GDBus.DmpInterface" , |
1565 | method_name: "AddPair" , |
1566 | parameters: g_variant_new (format_string: "(ii)" , 2, n), |
1567 | G_VARIANT_TYPE ("(i)" ), |
1568 | flags: G_DBUS_CALL_FLAGS_NONE, |
1569 | timeout_msec: -1, /* timeout_msec */ |
1570 | NULL, /* GCancellable */ |
1571 | error: &error); |
1572 | g_assert_no_error (error); |
1573 | g_variant_get (value: res, format_string: "(i)" , &val); |
1574 | g_assert_cmpint (val, ==, 2 + n); |
1575 | g_variant_unref (value: res); |
1576 | g_object_unref (object: c); |
1577 | } |
1578 | |
1579 | g_main_loop_quit (loop: data->loop); |
1580 | g_thread_join (thread: service_thread); |
1581 | dmp_data_free (data); |
1582 | teardown_test_address (); |
1583 | |
1584 | g_main_loop_unref (loop); |
1585 | g_free (mem: test_guid); |
1586 | } |
1587 | |
1588 | /* ---------------------------------------------------------------------------------------------------- */ |
1589 | |
1590 | static gboolean |
1591 | nonce_tcp_on_authorize_authenticated_peer (GDBusAuthObserver *observer, |
1592 | GIOStream *stream, |
1593 | GCredentials *credentials, |
1594 | gpointer user_data) |
1595 | { |
1596 | PeerData *data = user_data; |
1597 | gboolean authorized; |
1598 | |
1599 | data->num_connection_attempts++; |
1600 | |
1601 | authorized = TRUE; |
1602 | if (!data->accept_connection) |
1603 | { |
1604 | authorized = FALSE; |
1605 | g_main_loop_quit (loop); |
1606 | } |
1607 | |
1608 | return authorized; |
1609 | } |
1610 | |
1611 | /* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */ |
1612 | static gboolean |
1613 | nonce_tcp_on_new_connection (GDBusServer *server, |
1614 | GDBusConnection *connection, |
1615 | gpointer user_data) |
1616 | { |
1617 | PeerData *data = user_data; |
1618 | |
1619 | g_ptr_array_add (array: data->current_connections, g_object_ref (connection)); |
1620 | |
1621 | g_main_loop_quit (loop); |
1622 | |
1623 | return TRUE; |
1624 | } |
1625 | |
1626 | static gpointer |
1627 | nonce_tcp_service_thread_func (gpointer user_data) |
1628 | { |
1629 | PeerData *data = user_data; |
1630 | GMainContext *service_context; |
1631 | GDBusAuthObserver *observer; |
1632 | GError *error; |
1633 | |
1634 | service_context = g_main_context_new (); |
1635 | g_main_context_push_thread_default (context: service_context); |
1636 | |
1637 | error = NULL; |
1638 | observer = g_dbus_auth_observer_new (); |
1639 | server = g_dbus_server_new_sync (address: "nonce-tcp:host=127.0.0.1" , |
1640 | flags: G_DBUS_SERVER_FLAGS_NONE, |
1641 | guid: test_guid, |
1642 | observer, |
1643 | NULL, /* cancellable */ |
1644 | error: &error); |
1645 | g_assert_no_error (error); |
1646 | |
1647 | g_signal_connect (server, |
1648 | "new-connection" , |
1649 | G_CALLBACK (nonce_tcp_on_new_connection), |
1650 | data); |
1651 | g_signal_connect (observer, |
1652 | "authorize-authenticated-peer" , |
1653 | G_CALLBACK (nonce_tcp_on_authorize_authenticated_peer), |
1654 | data); |
1655 | g_object_unref (object: observer); |
1656 | |
1657 | g_dbus_server_start (server); |
1658 | |
1659 | run_service_loop (service_context); |
1660 | |
1661 | g_main_context_pop_thread_default (context: service_context); |
1662 | |
1663 | teardown_service_loop (); |
1664 | g_main_context_unref (context: service_context); |
1665 | |
1666 | /* test code specifically unrefs the server - see below */ |
1667 | g_assert (server == NULL); |
1668 | |
1669 | return NULL; |
1670 | } |
1671 | |
1672 | static void |
1673 | test_nonce_tcp (void) |
1674 | { |
1675 | PeerData data; |
1676 | GError *error; |
1677 | GThread *service_thread; |
1678 | GDBusConnection *c; |
1679 | gchar *s; |
1680 | gchar *nonce_file; |
1681 | gboolean res; |
1682 | const gchar *address; |
1683 | |
1684 | test_guid = g_dbus_generate_guid (); |
1685 | loop = g_main_loop_new (NULL, FALSE); |
1686 | |
1687 | memset (s: &data, c: '\0', n: sizeof (PeerData)); |
1688 | data.current_connections = g_ptr_array_new_with_free_func (element_free_func: g_object_unref); |
1689 | |
1690 | error = NULL; |
1691 | server = NULL; |
1692 | service_thread = g_thread_new (name: "nonce-tcp-service" , |
1693 | func: nonce_tcp_service_thread_func, |
1694 | data: &data); |
1695 | await_service_loop (); |
1696 | g_assert (server != NULL); |
1697 | |
1698 | /* bring up a connection and accept it */ |
1699 | data.accept_connection = TRUE; |
1700 | error = NULL; |
1701 | c = g_dbus_connection_new_for_address_sync (address: g_dbus_server_get_client_address (server), |
1702 | flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, |
1703 | NULL, /* GDBusAuthObserver */ |
1704 | NULL, /* cancellable */ |
1705 | error: &error); |
1706 | g_assert_no_error (error); |
1707 | g_assert (c != NULL); |
1708 | while (data.current_connections->len < 1) |
1709 | g_thread_yield (); |
1710 | g_assert_cmpint (data.current_connections->len, ==, 1); |
1711 | g_assert_cmpint (data.num_connection_attempts, ==, 1); |
1712 | g_assert (g_dbus_connection_get_unique_name (c) == NULL); |
1713 | g_assert_cmpstr (g_dbus_connection_get_guid (c), ==, test_guid); |
1714 | g_object_unref (object: c); |
1715 | |
1716 | /* now, try to subvert the nonce file (this assumes noncefile is the last key/value pair) |
1717 | */ |
1718 | |
1719 | address = g_dbus_server_get_client_address (server); |
1720 | |
1721 | s = strstr (haystack: address, needle: "noncefile=" ); |
1722 | g_assert (s != NULL); |
1723 | s += sizeof "noncefile=" - 1; |
1724 | nonce_file = g_strdup (str: s); |
1725 | |
1726 | /* First try invalid data in the nonce file - this will actually |
1727 | * make the client send this and the server will reject it. The way |
1728 | * it works is that if the nonce doesn't match, the server will |
1729 | * simply close the connection. So, from the client point of view, |
1730 | * we can see a variety of errors. |
1731 | */ |
1732 | error = NULL; |
1733 | res = g_file_set_contents (filename: nonce_file, |
1734 | contents: "0123456789012345" , |
1735 | length: -1, |
1736 | error: &error); |
1737 | g_assert_no_error (error); |
1738 | g_assert (res); |
1739 | c = g_dbus_connection_new_for_address_sync (address, |
1740 | flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, |
1741 | NULL, /* GDBusAuthObserver */ |
1742 | NULL, /* cancellable */ |
1743 | error: &error); |
1744 | _g_assert_error_domain (error, G_IO_ERROR); |
1745 | g_error_free (error); |
1746 | g_assert (c == NULL); |
1747 | |
1748 | /* Then try with a nonce-file of incorrect length - this will make |
1749 | * the client complain - we won't even try connecting to the server |
1750 | * for this |
1751 | */ |
1752 | error = NULL; |
1753 | res = g_file_set_contents (filename: nonce_file, |
1754 | contents: "0123456789012345_" , |
1755 | length: -1, |
1756 | error: &error); |
1757 | g_assert_no_error (error); |
1758 | g_assert (res); |
1759 | c = g_dbus_connection_new_for_address_sync (address, |
1760 | flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, |
1761 | NULL, /* GDBusAuthObserver */ |
1762 | NULL, /* cancellable */ |
1763 | error: &error); |
1764 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); |
1765 | g_error_free (error); |
1766 | g_assert (c == NULL); |
1767 | |
1768 | /* Finally try with no nonce-file at all */ |
1769 | g_assert_cmpint (g_unlink (nonce_file), ==, 0); |
1770 | error = NULL; |
1771 | c = g_dbus_connection_new_for_address_sync (address, |
1772 | flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, |
1773 | NULL, /* GDBusAuthObserver */ |
1774 | NULL, /* cancellable */ |
1775 | error: &error); |
1776 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); |
1777 | g_error_free (error); |
1778 | g_assert (c == NULL); |
1779 | |
1780 | /* Recreate the nonce-file so we can ensure the server deletes it when stopped. */ |
1781 | g_assert_cmpint (g_creat (nonce_file, 0600), !=, -1); |
1782 | |
1783 | g_dbus_server_stop (server); |
1784 | g_object_unref (object: server); |
1785 | server = NULL; |
1786 | |
1787 | g_assert_false (g_file_test (nonce_file, G_FILE_TEST_EXISTS)); |
1788 | g_free (mem: nonce_file); |
1789 | |
1790 | g_main_loop_quit (loop: service_loop); |
1791 | g_thread_join (thread: service_thread); |
1792 | |
1793 | g_ptr_array_unref (array: data.current_connections); |
1794 | |
1795 | g_main_loop_unref (loop); |
1796 | g_free (mem: test_guid); |
1797 | } |
1798 | |
1799 | static void |
1800 | test_credentials (void) |
1801 | { |
1802 | GCredentials *c1, *c2; |
1803 | GError *error; |
1804 | gchar *desc; |
1805 | |
1806 | c1 = g_credentials_new (); |
1807 | c2 = g_credentials_new (); |
1808 | |
1809 | error = NULL; |
1810 | if (g_credentials_set_unix_user (credentials: c2, uid: getuid (), error: &error)) |
1811 | g_assert_no_error (error); |
1812 | |
1813 | g_clear_error (err: &error); |
1814 | g_assert (g_credentials_is_same_user (c1, c2, &error)); |
1815 | g_assert_no_error (error); |
1816 | |
1817 | desc = g_credentials_to_string (credentials: c1); |
1818 | g_assert (desc != NULL); |
1819 | g_free (mem: desc); |
1820 | |
1821 | g_object_unref (object: c1); |
1822 | g_object_unref (object: c2); |
1823 | } |
1824 | |
1825 | /* ---------------------------------------------------------------------------------------------------- */ |
1826 | |
1827 | static gboolean |
1828 | tcp_anonymous_on_new_connection (GDBusServer *server, |
1829 | GDBusConnection *connection, |
1830 | gpointer user_data) |
1831 | { |
1832 | gboolean *seen_connection = user_data; |
1833 | *seen_connection = TRUE; |
1834 | return TRUE; |
1835 | } |
1836 | |
1837 | static gpointer |
1838 | tcp_anonymous_service_thread_func (gpointer user_data) |
1839 | { |
1840 | gboolean *seen_connection = user_data; |
1841 | GMainContext *service_context; |
1842 | GError *error; |
1843 | |
1844 | service_context = g_main_context_new (); |
1845 | g_main_context_push_thread_default (context: service_context); |
1846 | |
1847 | error = NULL; |
1848 | server = g_dbus_server_new_sync (address: "tcp:host=127.0.0.1" , |
1849 | flags: G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS, |
1850 | guid: test_guid, |
1851 | NULL, /* GDBusObserver* */ |
1852 | NULL, /* GCancellable* */ |
1853 | error: &error); |
1854 | g_assert_no_error (error); |
1855 | |
1856 | g_signal_connect (server, |
1857 | "new-connection" , |
1858 | G_CALLBACK (tcp_anonymous_on_new_connection), |
1859 | seen_connection); |
1860 | |
1861 | g_dbus_server_start (server); |
1862 | |
1863 | run_service_loop (service_context); |
1864 | |
1865 | g_main_context_pop_thread_default (context: service_context); |
1866 | |
1867 | teardown_service_loop (); |
1868 | g_main_context_unref (context: service_context); |
1869 | |
1870 | return NULL; |
1871 | } |
1872 | |
1873 | static void |
1874 | test_tcp_anonymous (void) |
1875 | { |
1876 | gboolean seen_connection; |
1877 | GThread *service_thread; |
1878 | GDBusConnection *connection; |
1879 | GError *error; |
1880 | |
1881 | test_guid = g_dbus_generate_guid (); |
1882 | loop = g_main_loop_new (NULL, FALSE); |
1883 | |
1884 | seen_connection = FALSE; |
1885 | service_thread = g_thread_new (name: "tcp-anon-service" , |
1886 | func: tcp_anonymous_service_thread_func, |
1887 | data: &seen_connection); |
1888 | await_service_loop (); |
1889 | g_assert (server != NULL); |
1890 | |
1891 | error = NULL; |
1892 | connection = g_dbus_connection_new_for_address_sync (address: g_dbus_server_get_client_address (server), |
1893 | flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, |
1894 | NULL, /* GDBusAuthObserver* */ |
1895 | NULL, /* GCancellable */ |
1896 | error: &error); |
1897 | g_assert_no_error (error); |
1898 | g_assert (connection != NULL); |
1899 | |
1900 | while (!seen_connection) |
1901 | g_thread_yield (); |
1902 | |
1903 | g_object_unref (object: connection); |
1904 | |
1905 | g_main_loop_quit (loop: service_loop); |
1906 | g_dbus_server_stop (server); |
1907 | g_object_unref (object: server); |
1908 | server = NULL; |
1909 | |
1910 | g_thread_join (thread: service_thread); |
1911 | |
1912 | g_main_loop_unref (loop); |
1913 | g_free (mem: test_guid); |
1914 | } |
1915 | |
1916 | /* ---------------------------------------------------------------------------------------------------- */ |
1917 | |
1918 | static GDBusServer *codegen_server = NULL; |
1919 | |
1920 | static gboolean |
1921 | codegen_on_animal_poke (ExampleAnimal *animal, |
1922 | GDBusMethodInvocation *invocation, |
1923 | gboolean make_sad, |
1924 | gboolean make_happy, |
1925 | gpointer user_data) |
1926 | { |
1927 | if ((make_sad && make_happy) || (!make_sad && !make_happy)) |
1928 | { |
1929 | g_main_loop_quit (loop: service_loop); |
1930 | |
1931 | g_dbus_method_invocation_return_dbus_error (invocation, |
1932 | error_name: "org.gtk.GDBus.Examples.ObjectManager.Error.Failed" , |
1933 | error_message: "Exactly one of make_sad or make_happy must be TRUE" ); |
1934 | goto out; |
1935 | } |
1936 | |
1937 | if (make_sad) |
1938 | { |
1939 | if (g_strcmp0 (str1: example_animal_get_mood (animal), str2: "Sad" ) == 0) |
1940 | { |
1941 | g_dbus_method_invocation_return_dbus_error (invocation, |
1942 | error_name: "org.gtk.GDBus.Examples.ObjectManager.Error.SadAnimalIsSad" , |
1943 | error_message: "Sad animal is already sad" ); |
1944 | goto out; |
1945 | } |
1946 | |
1947 | example_animal_set_mood (animal, "Sad" ); |
1948 | example_animal_complete_poke (animal, invocation); |
1949 | goto out; |
1950 | } |
1951 | |
1952 | if (make_happy) |
1953 | { |
1954 | if (g_strcmp0 (str1: example_animal_get_mood (animal), str2: "Happy" ) == 0) |
1955 | { |
1956 | g_dbus_method_invocation_return_dbus_error (invocation, |
1957 | error_name: "org.gtk.GDBus.Examples.ObjectManager.Error.HappyAnimalIsHappy" , |
1958 | error_message: "Happy animal is already happy" ); |
1959 | goto out; |
1960 | } |
1961 | |
1962 | example_animal_set_mood (animal, "Happy" ); |
1963 | example_animal_complete_poke (animal, invocation); |
1964 | goto out; |
1965 | } |
1966 | |
1967 | g_assert_not_reached (); |
1968 | |
1969 | out: |
1970 | return G_DBUS_METHOD_INVOCATION_HANDLED; |
1971 | } |
1972 | |
1973 | /* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */ |
1974 | static gboolean |
1975 | codegen_on_new_connection (GDBusServer *server, |
1976 | GDBusConnection *connection, |
1977 | gpointer user_data) |
1978 | { |
1979 | ExampleAnimal *animal = user_data; |
1980 | GError *error = NULL; |
1981 | |
1982 | /* g_printerr ("Client connected.\n" */ |
1983 | /* "Negotiated capabilities: unix-fd-passing=%d\n", */ |
1984 | /* g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); */ |
1985 | |
1986 | g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (animal), connection, |
1987 | "/Example/Animals/000" , &error); |
1988 | g_assert_no_error (error); |
1989 | |
1990 | return TRUE; |
1991 | } |
1992 | |
1993 | static gpointer |
1994 | codegen_service_thread_func (gpointer user_data) |
1995 | { |
1996 | GMainContext *service_context; |
1997 | ExampleAnimal *animal; |
1998 | GError *error = NULL; |
1999 | |
2000 | service_context = g_main_context_new (); |
2001 | g_main_context_push_thread_default (context: service_context); |
2002 | |
2003 | /* Create the animal in the right thread context */ |
2004 | animal = example_animal_skeleton_new (); |
2005 | |
2006 | /* Handle Poke() D-Bus method invocations on the .Animal interface */ |
2007 | g_signal_connect (animal, "handle-poke" , |
2008 | G_CALLBACK (codegen_on_animal_poke), |
2009 | NULL); /* user_data */ |
2010 | |
2011 | codegen_server = g_dbus_server_new_sync (address: tmp_address, |
2012 | flags: G_DBUS_SERVER_FLAGS_NONE, |
2013 | guid: test_guid, |
2014 | NULL, /* observer */ |
2015 | NULL, /* cancellable */ |
2016 | error: &error); |
2017 | g_assert_no_error (error); |
2018 | g_dbus_server_start (server: codegen_server); |
2019 | |
2020 | g_signal_connect (codegen_server, "new-connection" , |
2021 | G_CALLBACK (codegen_on_new_connection), |
2022 | animal); |
2023 | |
2024 | run_service_loop (service_context); |
2025 | |
2026 | g_object_unref (object: animal); |
2027 | |
2028 | g_main_context_pop_thread_default (context: service_context); |
2029 | |
2030 | teardown_service_loop (); |
2031 | g_main_context_unref (context: service_context); |
2032 | |
2033 | g_dbus_server_stop (server: codegen_server); |
2034 | g_object_unref (object: codegen_server); |
2035 | codegen_server = NULL; |
2036 | |
2037 | return NULL; |
2038 | } |
2039 | |
2040 | |
2041 | static gboolean |
2042 | codegen_quit_mainloop_timeout (gpointer data) |
2043 | { |
2044 | g_main_loop_quit (loop); |
2045 | return G_SOURCE_REMOVE; |
2046 | } |
2047 | |
2048 | static void |
2049 | codegen_test_peer (void) |
2050 | { |
2051 | GDBusConnection *connection; |
2052 | ExampleAnimal *animal1, *animal2; |
2053 | GThread *service_thread; |
2054 | GError *error = NULL; |
2055 | GVariant *value; |
2056 | const gchar *s; |
2057 | |
2058 | test_guid = g_dbus_generate_guid (); |
2059 | loop = g_main_loop_new (NULL, FALSE); |
2060 | |
2061 | setup_test_address (); |
2062 | |
2063 | /* bring up a server - we run the server in a different thread to avoid deadlocks */ |
2064 | service_thread = g_thread_new (name: "codegen_test_peer" , |
2065 | func: codegen_service_thread_func, |
2066 | NULL); |
2067 | await_service_loop (); |
2068 | g_assert (codegen_server != NULL); |
2069 | |
2070 | /* Get an animal 1 ... */ |
2071 | connection = g_dbus_connection_new_for_address_sync (address: g_dbus_server_get_client_address (server: codegen_server), |
2072 | flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, |
2073 | NULL, /* GDBusAuthObserver */ |
2074 | NULL, /* cancellable */ |
2075 | error: &error); |
2076 | g_assert_no_error (error); |
2077 | g_assert (connection != NULL); |
2078 | |
2079 | animal1 = example_animal_proxy_new_sync (connection, 0, NULL, |
2080 | "/Example/Animals/000" , NULL, &error); |
2081 | g_assert_no_error (error); |
2082 | g_assert (animal1 != NULL); |
2083 | g_object_unref (object: connection); |
2084 | |
2085 | /* Get animal 2 ... */ |
2086 | connection = g_dbus_connection_new_for_address_sync (address: g_dbus_server_get_client_address (server: codegen_server), |
2087 | flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, |
2088 | NULL, /* GDBusAuthObserver */ |
2089 | NULL, /* cancellable */ |
2090 | error: &error); |
2091 | g_assert_no_error (error); |
2092 | g_assert (connection != NULL); |
2093 | |
2094 | animal2 = example_animal_proxy_new_sync (connection, 0, NULL, |
2095 | "/Example/Animals/000" , NULL, &error); |
2096 | g_assert_no_error (error); |
2097 | g_assert (animal2 != NULL); |
2098 | g_object_unref (object: connection); |
2099 | |
2100 | /* Make animal sad via animal1 */ |
2101 | example_animal_call_poke_sync (animal1, TRUE, FALSE, NULL, &error); |
2102 | g_assert_no_error (error); |
2103 | |
2104 | /* Poke server and make sure animal is updated */ |
2105 | value = g_dbus_proxy_call_sync (G_DBUS_PROXY (animal1), |
2106 | "org.freedesktop.DBus.Peer.Ping" , |
2107 | NULL, G_DBUS_CALL_FLAGS_NONE, -1, |
2108 | NULL, &error); |
2109 | g_assert_no_error (error); |
2110 | g_assert (value != NULL); |
2111 | g_variant_unref (value); |
2112 | |
2113 | /* Give the proxies a chance to refresh in the default main loop */ |
2114 | g_timeout_add (interval: 100, function: codegen_quit_mainloop_timeout, NULL); |
2115 | g_main_loop_run (loop); |
2116 | |
2117 | /* Assert animals are sad */ |
2118 | g_assert_cmpstr (example_animal_get_mood (animal1), ==, "Sad" ); |
2119 | g_assert_cmpstr (example_animal_get_mood (animal2), ==, "Sad" ); |
2120 | |
2121 | /* Make animal happy via animal2 */ |
2122 | example_animal_call_poke_sync (animal2, FALSE, TRUE, NULL, &error); |
2123 | g_assert_no_error (error); |
2124 | |
2125 | /* Some random unrelated call, just to get some test coverage */ |
2126 | value = g_dbus_proxy_call_sync (G_DBUS_PROXY (animal2), |
2127 | "org.freedesktop.DBus.Peer.GetMachineId" , |
2128 | NULL, G_DBUS_CALL_FLAGS_NONE, -1, |
2129 | NULL, &error); |
2130 | g_assert_no_error (error); |
2131 | g_variant_get (value, format_string: "(&s)" , &s); |
2132 | g_test_message (format: "Machine ID: %s" , s); |
2133 | /* It's valid for machine-id inside containers to be empty, so we |
2134 | * need to test for that possibility |
2135 | */ |
2136 | g_assert ((s == NULL || *s == '\0') || g_dbus_is_guid (s)); |
2137 | g_variant_unref (value); |
2138 | |
2139 | /* Poke server and make sure animal is updated */ |
2140 | value = g_dbus_proxy_call_sync (G_DBUS_PROXY (animal2), |
2141 | "org.freedesktop.DBus.Peer.Ping" , |
2142 | NULL, G_DBUS_CALL_FLAGS_NONE, -1, |
2143 | NULL, &error); |
2144 | g_assert_no_error (error); |
2145 | g_assert (value != NULL); |
2146 | g_variant_unref (value); |
2147 | |
2148 | /* Give the proxies a chance to refresh in the default main loop */ |
2149 | g_timeout_add (interval: 1000, function: codegen_quit_mainloop_timeout, NULL); |
2150 | g_main_loop_run (loop); |
2151 | |
2152 | /* Assert animals are happy */ |
2153 | g_assert_cmpstr (example_animal_get_mood (animal1), ==, "Happy" ); |
2154 | g_assert_cmpstr (example_animal_get_mood (animal2), ==, "Happy" ); |
2155 | |
2156 | /* This final call making the animal happy and sad will cause |
2157 | * the server to quit, when the server quits we dont get property |
2158 | * change notifications anyway because those are done from an idle handler |
2159 | */ |
2160 | example_animal_call_poke_sync (animal2, TRUE, TRUE, NULL, &error); |
2161 | g_clear_error (err: &error); |
2162 | |
2163 | g_object_unref (object: animal1); |
2164 | g_object_unref (object: animal2); |
2165 | g_thread_join (thread: service_thread); |
2166 | |
2167 | teardown_test_address (); |
2168 | |
2169 | g_main_loop_unref (loop); |
2170 | g_free (mem: test_guid); |
2171 | } |
2172 | |
2173 | /* ---------------------------------------------------------------------------------------------------- */ |
2174 | |
2175 | |
2176 | int |
2177 | main (int argc, |
2178 | char *argv[]) |
2179 | { |
2180 | gint ret; |
2181 | GDBusNodeInfo *introspection_data = NULL; |
2182 | |
2183 | g_test_init (argc: &argc, argv: &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); |
2184 | |
2185 | introspection_data = g_dbus_node_info_new_for_xml (xml_data: test_interface_introspection_xml, NULL); |
2186 | g_assert (introspection_data != NULL); |
2187 | test_interface_introspection_data = introspection_data->interfaces[0]; |
2188 | |
2189 | g_test_add_func (testpath: "/gdbus/peer-to-peer" , test_func: test_peer); |
2190 | g_test_add_func (testpath: "/gdbus/peer-to-peer/invalid/server" , |
2191 | test_func: test_peer_invalid_server); |
2192 | g_test_add_func (testpath: "/gdbus/peer-to-peer/invalid/conn/stream/async" , |
2193 | test_func: test_peer_invalid_conn_stream_async); |
2194 | g_test_add_func (testpath: "/gdbus/peer-to-peer/invalid/conn/stream/sync" , |
2195 | test_func: test_peer_invalid_conn_stream_sync); |
2196 | g_test_add_func (testpath: "/gdbus/peer-to-peer/invalid/conn/addr/async" , |
2197 | test_func: test_peer_invalid_conn_addr_async); |
2198 | g_test_add_func (testpath: "/gdbus/peer-to-peer/invalid/conn/addr/sync" , |
2199 | test_func: test_peer_invalid_conn_addr_sync); |
2200 | g_test_add_func (testpath: "/gdbus/peer-to-peer/signals" , test_func: test_peer_signals); |
2201 | g_test_add_func (testpath: "/gdbus/delayed-message-processing" , test_func: delayed_message_processing); |
2202 | g_test_add_func (testpath: "/gdbus/nonce-tcp" , test_func: test_nonce_tcp); |
2203 | |
2204 | g_test_add_func (testpath: "/gdbus/tcp-anonymous" , test_func: test_tcp_anonymous); |
2205 | g_test_add_func (testpath: "/gdbus/credentials" , test_func: test_credentials); |
2206 | g_test_add_func (testpath: "/gdbus/codegen-peer-to-peer" , test_func: codegen_test_peer); |
2207 | |
2208 | ret = g_test_run (); |
2209 | |
2210 | g_dbus_node_info_unref (info: introspection_data); |
2211 | |
2212 | return ret; |
2213 | } |
2214 | |