gdbus-non-socket test: avoid use of a GMainContext across a fork
[platform/upstream/glib.git] / gio / tests / gdbus-non-socket.c
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 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, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: David Zeuthen <davidz@redhat.com>
21  */
22
23 #include <gio/gio.h>
24 #include <unistd.h>
25 #include <string.h>
26
27 #include <stdlib.h>
28
29 #ifdef G_OS_UNIX
30 #include <gio/gunixinputstream.h>
31 #include <gio/gunixoutputstream.h>
32 #include <gio/gunixconnection.h>
33 #endif
34
35 #include "gdbus-tests.h"
36
37 static GMainLoop *loop = NULL;
38
39 /* ---------------------------------------------------------------------------------------------------- */
40 #ifdef G_OS_UNIX
41
42 #define MY_TYPE_IO_STREAM  (my_io_stream_get_type ())
43 #define MY_IO_STREAM(o)    (G_TYPE_CHECK_INSTANCE_CAST ((o), MY_TYPE_IO_STREAM, MyIOStream))
44 #define MY_IS_IO_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MY_TYPE_IO_STREAM))
45
46 typedef struct
47 {
48   GIOStream parent_instance;
49   GInputStream *input_stream;
50   GOutputStream *output_stream;
51 } MyIOStream;
52
53 typedef struct
54 {
55   GIOStreamClass parent_class;
56 } MyIOStreamClass;
57
58 static GType my_io_stream_get_type (void) G_GNUC_CONST;
59
60 G_DEFINE_TYPE (MyIOStream, my_io_stream, G_TYPE_IO_STREAM);
61
62 static void
63 my_io_stream_finalize (GObject *object)
64 {
65   MyIOStream *stream = MY_IO_STREAM (object);
66   g_object_unref (stream->input_stream);
67   g_object_unref (stream->output_stream);
68   G_OBJECT_CLASS (my_io_stream_parent_class)->finalize (object);
69 }
70
71 static void
72 my_io_stream_init (MyIOStream *stream)
73 {
74 }
75
76 static GInputStream *
77 my_io_stream_get_input_stream (GIOStream *_stream)
78 {
79   MyIOStream *stream = MY_IO_STREAM (_stream);
80   return stream->input_stream;
81 }
82
83 static GOutputStream *
84 my_io_stream_get_output_stream (GIOStream *_stream)
85 {
86   MyIOStream *stream = MY_IO_STREAM (_stream);
87   return stream->output_stream;
88 }
89
90 static void
91 my_io_stream_class_init (MyIOStreamClass *klass)
92 {
93   GObjectClass *gobject_class;
94   GIOStreamClass *giostream_class;
95
96   gobject_class = G_OBJECT_CLASS (klass);
97   gobject_class->finalize = my_io_stream_finalize;
98
99   giostream_class = G_IO_STREAM_CLASS (klass);
100   giostream_class->get_input_stream  = my_io_stream_get_input_stream;
101   giostream_class->get_output_stream = my_io_stream_get_output_stream;
102 }
103
104 static GIOStream *
105 my_io_stream_new (GInputStream  *input_stream,
106                   GOutputStream *output_stream)
107 {
108   MyIOStream *stream;
109   g_return_val_if_fail (G_IS_INPUT_STREAM (input_stream), NULL);
110   g_return_val_if_fail (G_IS_OUTPUT_STREAM (output_stream), NULL);
111   stream = MY_IO_STREAM (g_object_new (MY_TYPE_IO_STREAM, NULL));
112   stream->input_stream = g_object_ref (input_stream);
113   stream->output_stream = g_object_ref (output_stream);
114   return G_IO_STREAM (stream);
115 }
116
117 static GIOStream *
118 my_io_stream_new_for_fds (gint fd_in, gint fd_out)
119 {
120   GIOStream *stream;
121   GInputStream  *input_stream;
122   GOutputStream *output_stream;
123
124   input_stream = g_unix_input_stream_new (fd_in, TRUE);
125   output_stream = g_unix_output_stream_new (fd_out, TRUE);
126   stream = my_io_stream_new (input_stream, output_stream);
127   g_object_unref (input_stream);
128   g_object_unref (output_stream);
129   return stream;
130 }
131
132 /* ---------------------------------------------------------------------------------------------------- */
133
134 static const GDBusArgInfo pokee_method_poke_out_arg0 = {
135   -1,   /* ref_count */
136   "result",
137   "s",
138   NULL  /* annotations */
139 };
140
141 static const GDBusArgInfo *pokee_method_poke_out_args[2] = {
142   &pokee_method_poke_out_arg0,
143   NULL,
144 };
145
146 static const GDBusArgInfo pokee_method_poke_in_arg0 = {
147   -1,   /* ref_count */
148   "value",
149   "s",
150   NULL  /* annotations */
151 };
152
153 static const GDBusArgInfo *pokee_method_poke_in_args[2] = {
154   &pokee_method_poke_in_arg0,
155   NULL,
156 };
157
158 static const GDBusMethodInfo pokee_method_poke = {
159   -1,   /* ref_count */
160   "Poke",
161   (GDBusArgInfo**) pokee_method_poke_in_args,
162   (GDBusArgInfo**) pokee_method_poke_out_args,
163   NULL  /* annotations */
164 };
165
166 static const GDBusMethodInfo *pokee_methods[2] = {
167   &pokee_method_poke,
168   NULL
169 };
170
171 static const GDBusInterfaceInfo pokee_object_info = {
172   -1,  /* ref_count */
173   "org.gtk.GDBus.Pokee",
174   (GDBusMethodInfo**) pokee_methods,
175   NULL, /* signals */
176   NULL, /* properties */
177   NULL  /* annotations */
178 };
179
180 static void
181 pokee_method_call (GDBusConnection       *connection,
182                    const gchar           *sender,
183                    const gchar           *object_path,
184                    const gchar           *interface_name,
185                    const gchar           *method_name,
186                    GVariant              *parameters,
187                    GDBusMethodInvocation *invocation,
188                    gpointer               user_data)
189 {
190   const gchar *str;
191   gchar *ret;
192
193   g_assert_cmpstr (method_name, ==, "Poke");
194
195   g_variant_get (parameters, "(&s)", &str);
196   ret = g_strdup_printf ("You poked me with: `%s'", str);
197   g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", ret));
198   g_free (ret);
199 }
200
201 static const GDBusInterfaceVTable pokee_vtable = {
202   pokee_method_call,
203   NULL, /* get_property */
204   NULL  /* set_property */
205 };
206
207 /* Processes:
208  *
209  * parent
210  * \- first child (via fork()) is the pokee
211  * \- second child (via g_test_trap_fork()) is the poker
212  *
213  * The second child only exists to avoid sharing a main context between several
214  * second-children if we run a test resembling this one multiple times.
215  * See https://bugzilla.gnome.org/show_bug.cgi?id=658999 for why that's bad.
216  */
217 static void
218 test_non_socket (void)
219 {
220   gint in_pipes[2];
221   gint out_pipes[2];
222   GIOStream *stream;
223   GDBusConnection *connection;
224   GError *error;
225   gchar *guid;
226   pid_t first_child;
227   gint read_fd;
228   gint write_fd;
229   GVariant *ret;
230   const gchar *str;
231
232   g_assert_cmpint (pipe (in_pipes), ==, 0);
233   g_assert_cmpint (pipe (out_pipes), ==, 0);
234
235   switch ((first_child = fork ()))
236     {
237     case -1:
238       g_assert_not_reached ();
239       break;
240
241     case 0:
242       /* first child */
243
244       /* we shouldn't do this in the parent, because we shouldn't use a
245        * GMainContext both before and after fork
246        */
247       loop = g_main_loop_new (NULL, FALSE);
248
249       read_fd  =  in_pipes[0];
250       write_fd = out_pipes[1];
251       g_assert_cmpint (close ( in_pipes[1]), ==, 0); /* close unused write end */
252       g_assert_cmpint (close (out_pipes[0]), ==, 0); /* close unused read end */
253       stream = my_io_stream_new_for_fds (read_fd, write_fd);
254       guid = g_dbus_generate_guid ();
255       error = NULL;
256       /* We need to delay message processing to avoid the race
257        * described in
258        *
259        *  https://bugzilla.gnome.org/show_bug.cgi?id=627188
260        *
261        * This is because (early) dispatching is done on the IO thread
262        * (method_call() isn't called until we're in the right thread
263        * though) so in rare cases the parent sends the message before
264        * we (the first child) register the object
265        */
266       connection = g_dbus_connection_new_sync (stream,
267                                                guid,
268                                                G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER |
269                                                G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING,
270                                                NULL, /* GDBusAuthObserver */
271                                                NULL,
272                                                &error);
273       g_free (guid);
274       g_assert_no_error (error);
275       g_object_unref (stream);
276
277       /* make sure we exit along with the parent */
278       g_dbus_connection_set_exit_on_close (connection, TRUE);
279
280       error = NULL;
281       g_dbus_connection_register_object (connection,
282                                          "/pokee",
283                                          (GDBusInterfaceInfo *) &pokee_object_info,
284                                          &pokee_vtable,
285                                          NULL, /* user_data */
286                                          NULL, /* user_data_free_func */
287                                          &error);
288       g_assert_no_error (error);
289
290       /* and now start message processing */
291       g_dbus_connection_start_message_processing (connection);
292
293       g_main_loop_run (loop);
294
295       g_assert_not_reached ();
296       break;
297
298     default:
299       /* parent continues below */
300       break;
301     }
302
303   if (!g_test_trap_fork (0, 0))
304     {
305       /* parent */
306       g_test_trap_assert_passed ();
307       g_assert_cmpint (kill (first_child, SIGTERM), ==, 0);
308       return;
309     }
310
311   /* second child */
312
313   /* we shouldn't do this in the parent, because we shouldn't use a
314    * GMainContext both before and after fork
315    */
316   loop = g_main_loop_new (NULL, FALSE);
317
318   read_fd  = out_pipes[0];
319   write_fd =  in_pipes[1];
320   g_assert_cmpint (close (out_pipes[1]), ==, 0); /* close unused write end */
321   g_assert_cmpint (close ( in_pipes[0]), ==, 0); /* close unused read end */
322   stream = my_io_stream_new_for_fds (read_fd, write_fd);
323   error = NULL;
324   connection = g_dbus_connection_new_sync (stream,
325                                            NULL, /* guid */
326                                            G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
327                                            NULL, /* GDBusAuthObserver */
328                                            NULL,
329                                            &error);
330   g_assert_no_error (error);
331   g_object_unref (stream);
332
333   /* poke the first child */
334   error = NULL;
335   ret = g_dbus_connection_call_sync (connection,
336                                      NULL, /* name */
337                                      "/pokee",
338                                      "org.gtk.GDBus.Pokee",
339                                      "Poke",
340                                      g_variant_new ("(s)", "I am the POKER!"),
341                                      G_VARIANT_TYPE ("(s)"), /* return type */
342                                      G_DBUS_CALL_FLAGS_NONE,
343                                      -1,
344                                      NULL, /* cancellable */
345                                      &error);
346   g_assert_no_error (error);
347   g_variant_get (ret, "(&s)", &str);
348   g_assert_cmpstr (str, ==, "You poked me with: `I am the POKER!'");
349   g_variant_unref (ret);
350
351   g_object_unref (connection);
352   g_main_loop_unref (loop);
353   exit (0);
354 }
355
356 #else /* G_OS_UNIX */
357
358 static void
359 test_non_socket (void)
360 {
361   /* TODO: test this with e.g. GWin32InputStream/GWin32OutputStream */
362 }
363 #endif
364
365 /* ---------------------------------------------------------------------------------------------------- */
366
367 int
368 main (int   argc,
369       char *argv[])
370 {
371   gint ret;
372
373   g_type_init ();
374   g_test_init (&argc, &argv, NULL);
375
376   g_test_add_func ("/gdbus/non-socket", test_non_socket);
377
378   ret = g_test_run();
379
380   return ret;
381 }