[lib-opt] library optimalization and clean-up
[platform/upstream/dbus.git] / test / dbus-daemon-eavesdrop.c
1 /* Integration tests for the eavesdrop=true|false keyword in DBusMatchRule
2  *
3  * Author: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk>
4  * Based on: tests/dbus-daemon.c by Simon McVittie
5  * Copyright © 2010-2011 Nokia Corporation
6  *
7  * Permission is hereby granted, free of charge, to any person
8  * obtaining a copy of this software and associated documentation files
9  * (the "Software"), to deal in the Software without restriction,
10  * including without limitation the rights to use, copy, modify, merge,
11  * publish, distribute, sublicense, and/or sell copies of the Software,
12  * and to permit persons to whom the Software is furnished to do so,
13  * subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  */
27
28 #include <config.h>
29
30 #include <glib.h>
31
32 #include <dbus/dbus.h>
33
34 #include <string.h>
35
36 #ifdef DBUS_WIN
37 # include <io.h>
38 # include <windows.h>
39 #else
40 # include <signal.h>
41 # include <unistd.h>
42 #endif
43
44 #include "test-utils.h"
45
46 #define SENDER_NAME "test.eavesdrop.sender"
47 #define SENDER_PATH "/test/eavesdrop/sender"
48 #define SENDER_IFACE SENDER_NAME
49 #define SENDER_SIGNAL_NAME "Signal"
50 #define SENDER_STOPPER_NAME "Stopper"
51
52 /* This rule is equivalent to the one added to a proxy connecting to
53  * SENDER_NAME+SENDER_IFACE, plus restricting on signal name.
54  * Being more restrictive, if the connection receives what we need, for sure
55  * the original proxy rule will match it */
56 #define RECEIVER_RULE "sender='" SENDER_NAME "'," \
57   "interface='" SENDER_IFACE "'," \
58   "type='signal'," \
59   "member='" SENDER_SIGNAL_NAME "'"
60 #define POLITELISTENER_RULE RECEIVER_RULE
61 #define EAVESDROPPER_RULE RECEIVER_RULE ",eavesdrop=true"
62
63 #define STOPPER_RULE "sender='" SENDER_NAME \
64   "',interface='" SENDER_IFACE "',type='signal',member='" SENDER_STOPPER_NAME "'"
65
66 /* a connection received a signal to whom? */
67 typedef enum {
68   NONE_YET = 0,
69   TO_ME,
70   TO_OTHER,
71   BROADCAST,
72 } SignalDst;
73
74 typedef struct {
75     TestMainContext *ctx;
76     DBusError e;
77     GError *ge;
78
79     GPid daemon_pid;
80
81     /* eavedrop keyword tests */
82     DBusConnection *sender;
83     DBusConnection *receiver;
84     SignalDst receiver_dst;
85     dbus_bool_t receiver_got_stopper;
86     DBusConnection *eavesdropper;
87     SignalDst eavesdropper_dst;
88     dbus_bool_t eavesdropper_got_stopper;
89     DBusConnection *politelistener;
90     SignalDst politelistener_dst;
91     dbus_bool_t politelistener_got_stopper;
92 } Fixture;
93
94 #define assert_no_error(e) _assert_no_error (e, __FILE__, __LINE__)
95 static void
96 _assert_no_error (const DBusError *e,
97     const char *file,
98     int line)
99 {
100   if (G_UNLIKELY (dbus_error_is_set (e)))
101     g_error ("%s:%d: expected success but got error: %s: %s",
102         file, line, e->name, e->message);
103 }
104
105 static gchar *
106 spawn_dbus_daemon (gchar *binary,
107     gchar *configuration,
108     GPid *daemon_pid)
109 {
110   GError *error = NULL;
111   GString *address;
112   gint address_fd;
113   gchar *argv[] = {
114       binary,
115       configuration,
116       "--nofork",
117       "--print-address=1", /* stdout */
118       NULL
119   };
120
121   g_spawn_async_with_pipes (NULL, /* working directory */
122       argv,
123       NULL, /* envp */
124       G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
125       NULL, /* child_setup */
126       NULL, /* user data */
127       daemon_pid,
128       NULL, /* child's stdin = /dev/null */
129       &address_fd,
130       NULL, /* child's stderr = our stderr */
131       &error);
132   g_assert_no_error (error);
133
134   address = g_string_new (NULL);
135
136   /* polling until the dbus-daemon writes out its address is a bit stupid,
137    * but at least it's simple, unlike dbus-launch... in principle we could
138    * use select() here, but life's too short */
139   while (1)
140     {
141       gssize bytes;
142       gchar buf[4096];
143       gchar *newline;
144
145       bytes = read (address_fd, buf, sizeof (buf));
146
147       if (bytes > 0)
148         g_string_append_len (address, buf, bytes);
149
150       newline = strchr (address->str, '\n');
151
152       if (newline != NULL)
153         {
154           g_string_truncate (address, newline - address->str);
155           break;
156         }
157
158       g_usleep (G_USEC_PER_SEC / 10);
159     }
160
161   return g_string_free (address, FALSE);
162 }
163
164 static DBusConnection *
165 connect_to_bus (Fixture *f,
166     const gchar *address)
167 {
168   DBusConnection *conn;
169   DBusError error = DBUS_ERROR_INIT;
170   dbus_bool_t ok;
171
172   conn = dbus_connection_open_private (address, &error);
173   assert_no_error (&error);
174   g_assert (conn != NULL);
175
176   ok = dbus_bus_register (conn, &error);
177   assert_no_error (&error);
178   g_assert (ok);
179   g_assert (dbus_bus_get_unique_name (conn) != NULL);
180
181   test_connection_setup (f->ctx, conn);
182   return conn;
183 }
184
185 /* send a unicast signal to <self> to ensure that no other connection
186  * listening is the actual recipient for the signal */
187 static DBusHandlerResult
188 sender_send_unicast_to_sender (Fixture *f)
189 {
190   DBusMessage *signal;
191
192   signal = dbus_message_new_signal (SENDER_PATH, SENDER_IFACE,
193       SENDER_SIGNAL_NAME);
194   dbus_message_set_destination (signal, dbus_bus_get_unique_name (f->sender));
195
196   if (signal == NULL)
197     g_error ("OOM");
198
199   if (!dbus_connection_send (f->sender, signal, NULL))
200     g_error ("OOM");
201
202   dbus_message_unref (signal);
203
204   return DBUS_HANDLER_RESULT_HANDLED;
205 }
206
207 /* send a unicast signal to <receiver>, making <politelistener> and
208  * <eavesdropper> not a actual recipient for it */
209 static DBusHandlerResult
210 sender_send_unicast_to_receiver (Fixture *f)
211 {
212   DBusMessage *signal;
213
214   signal = dbus_message_new_signal (SENDER_PATH, SENDER_IFACE, SENDER_SIGNAL_NAME);
215   dbus_message_set_destination (signal, dbus_bus_get_unique_name (f->receiver));
216
217   if (signal == NULL)
218     g_error ("OOM");
219
220   if (!dbus_connection_send (f->sender, signal, NULL))
221     g_error ("OOM");
222
223   dbus_message_unref (signal);
224
225   return DBUS_HANDLER_RESULT_HANDLED;
226 }
227
228 static DBusHandlerResult
229 sender_send_broadcast (Fixture *f)
230 {
231   DBusMessage *signal;
232
233   signal = dbus_message_new_signal (SENDER_PATH, SENDER_IFACE, SENDER_SIGNAL_NAME);
234   dbus_message_set_destination (signal, NULL);
235
236   if (signal == NULL)
237     g_error ("OOM");
238
239   if (!dbus_connection_send (f->sender, signal, NULL))
240     g_error ("OOM");
241
242   dbus_message_unref (signal);
243
244   return DBUS_HANDLER_RESULT_HANDLED;
245 }
246
247 /* Send special broadcast signal to indicate that the connections can "stop"
248  * listening and check their results.
249  * DBus does not re-order messages, so when the three connections have received
250  * this signal, we are sure that any message sent before it has also been
251  * dispatched. */
252 static DBusHandlerResult
253 sender_send_stopper (Fixture *f)
254 {
255   DBusMessage *signal;
256
257   signal = dbus_message_new_signal (SENDER_PATH, SENDER_IFACE, SENDER_STOPPER_NAME);
258   dbus_message_set_destination (signal, NULL);
259
260   if (signal == NULL)
261     g_error ("OOM");
262
263   if (!dbus_connection_send (f->sender, signal, NULL))
264     g_error ("OOM");
265
266   dbus_message_unref (signal);
267
268   return DBUS_HANDLER_RESULT_HANDLED;
269 }
270
271 /* Ignore NameAcquired, then depending on the signal received:
272  * - updates f-><conn>_dst based on the destination of the message
273  * - asserts that <conn> received the stop signal
274  */
275 static DBusHandlerResult
276 signal_filter (DBusConnection *connection,
277     DBusMessage *message,
278     void *user_data)
279 {
280   Fixture *f = user_data;
281   SignalDst *dst = NULL;
282   DBusConnection **conn;
283   dbus_bool_t *got_stopper;
284
285   if (0 == strcmp (dbus_message_get_member (message), "NameAcquired"))
286     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
287
288   if (connection == f->receiver)
289     {
290       dst = &(f->receiver_dst);
291       conn = &(f->receiver);
292       got_stopper = &(f->receiver_got_stopper);
293     }
294   else if (connection == f->eavesdropper)
295     {
296       dst = &(f->eavesdropper_dst);
297       conn = &(f->eavesdropper);
298       got_stopper = &(f->eavesdropper_got_stopper);
299     }
300   else if (connection == f->politelistener)
301     {
302       dst = &(f->politelistener_dst);
303       conn = &(f->politelistener);
304       got_stopper = &(f->politelistener_got_stopper);
305     }
306   else
307     {
308       g_error ("connection not matching");
309     }
310
311   if (0 == strcmp (dbus_message_get_member (message), SENDER_SIGNAL_NAME))
312     {
313       if (dbus_message_get_destination (message) == NULL)
314         *dst = BROADCAST;
315       else if (0 == strcmp (dbus_message_get_destination (message), dbus_bus_get_unique_name (*conn)))
316         *dst = TO_ME;
317       else /* if (dbus_message_get_destination (message) != NULL) */
318         *dst = TO_OTHER;
319     }
320   else if (0 == strcmp (dbus_message_get_member (message), SENDER_STOPPER_NAME))
321     {
322       *got_stopper = TRUE;
323     }
324   else
325     {
326       g_error ("got unknown member from message: %s",
327           dbus_message_get_member (message));
328     }
329
330   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
331 }
332
333 static void
334 add_receiver_filter (Fixture *f)
335 {
336   DBusError e = DBUS_ERROR_INIT;
337
338   dbus_bus_add_match (f->receiver, RECEIVER_RULE, &e);
339   assert_no_error (&e);
340   dbus_bus_add_match (f->receiver, STOPPER_RULE, &e);
341   assert_no_error (&e);
342
343   if (!dbus_connection_add_filter (f->receiver,
344         signal_filter, f, NULL))
345     g_error ("OOM");
346 }
347
348 static void
349 add_eavesdropper_filter (Fixture *f)
350 {
351   DBusError e = DBUS_ERROR_INIT;
352
353   dbus_bus_add_match (f->eavesdropper, EAVESDROPPER_RULE, &e);
354   assert_no_error (&e);
355   dbus_bus_add_match (f->eavesdropper, STOPPER_RULE, &e);
356   assert_no_error (&e);
357
358   if (!dbus_connection_add_filter (f->eavesdropper,
359         signal_filter, f, NULL))
360     g_error ("OOM");
361 }
362
363 static void
364 add_politelistener_filter (Fixture *f)
365 {
366   DBusError e = DBUS_ERROR_INIT;
367
368   dbus_bus_add_match (f->politelistener, POLITELISTENER_RULE, &e);
369   assert_no_error (&e);
370   dbus_bus_add_match (f->politelistener, STOPPER_RULE, &e);
371   assert_no_error (&e);
372
373   if (!dbus_connection_add_filter (f->politelistener,
374         signal_filter, f, NULL))
375     g_error ("OOM");
376 }
377
378 static void
379 setup (Fixture *f,
380     gconstpointer context G_GNUC_UNUSED)
381 {
382   gchar *dbus_daemon;
383   gchar *config;
384   gchar *address;
385
386   f->ctx = test_main_context_get ();
387
388   f->ge = NULL;
389   dbus_error_init (&f->e);
390
391   dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON"));
392
393   if (dbus_daemon == NULL)
394     dbus_daemon = g_strdup ("dbus-daemon");
395
396   if (g_getenv ("DBUS_TEST_SYSCONFDIR") != NULL)
397     {
398       config = g_strdup_printf ("--config-file=%s/dbus-1/session.conf",
399           g_getenv ("DBUS_TEST_SYSCONFDIR"));
400     }
401   else if (g_getenv ("DBUS_TEST_DATA") != NULL)
402     {
403       config = g_strdup_printf (
404           "--config-file=%s/valid-config-files/session.conf",
405           g_getenv ("DBUS_TEST_DATA"));
406     }
407   else
408     {
409       config = g_strdup ("--session");
410     }
411
412   address = spawn_dbus_daemon (dbus_daemon, config, &f->daemon_pid);
413
414   g_free (dbus_daemon);
415   g_free (config);
416
417   f->sender = connect_to_bus (f, address);
418   dbus_bus_request_name (f->sender, SENDER_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE,
419       &(f->e));
420   f->receiver = connect_to_bus (f, address);
421   f->eavesdropper = connect_to_bus (f, address);
422   f->politelistener = connect_to_bus (f, address);
423   add_receiver_filter (f);
424   add_politelistener_filter (f);
425   add_eavesdropper_filter (f);
426
427   g_free (address);
428 }
429
430 static void
431 test_eavesdrop_broadcast (Fixture *f,
432     gconstpointer context G_GNUC_UNUSED)
433 {
434   sender_send_broadcast (f);
435   sender_send_stopper (f);
436
437   while (!f->receiver_got_stopper ||
438       !f->politelistener_got_stopper ||
439       !f->eavesdropper_got_stopper)
440     test_main_context_iterate (f->ctx, TRUE);
441
442   /* all the three connection can receive a broadcast */
443   g_assert_cmpint (f->receiver_dst, ==, BROADCAST);
444   g_assert_cmpint (f->politelistener_dst, ==, BROADCAST);
445   g_assert_cmpint (f->eavesdropper_dst, ==, BROADCAST);
446 }
447
448 /* a way to say that none of the listening connection are destination of the
449  * signal */
450 static void
451 test_eavesdrop_unicast_to_sender (Fixture *f,
452     gconstpointer context G_GNUC_UNUSED)
453 {
454   sender_send_unicast_to_sender (f);
455   sender_send_stopper (f);
456
457   while (!f->receiver_got_stopper ||
458       !f->politelistener_got_stopper ||
459       !f->eavesdropper_got_stopper)
460     test_main_context_iterate (f->ctx, TRUE);
461
462   /* not directed to it and not broadcasted, they cannot receive it */
463   g_assert_cmpint (f->receiver_dst, ==, NONE_YET);
464   g_assert_cmpint (f->politelistener_dst, ==, NONE_YET);
465   /* eavesdrop=true, it will receive the signal even though it's not directed
466    * to it */
467   g_assert_cmpint (f->eavesdropper_dst, ==, TO_OTHER);
468 }
469
470 static void
471 test_eavesdrop_unicast_to_receiver (Fixture *f,
472     gconstpointer context G_GNUC_UNUSED)
473 {
474   sender_send_unicast_to_receiver (f);
475   sender_send_stopper (f);
476
477   while (!f->receiver_got_stopper ||
478       !f->politelistener_got_stopper ||
479       !f->eavesdropper_got_stopper)
480     test_main_context_iterate (f->ctx, TRUE);
481
482   /* direct to him */
483   g_assert_cmpint (f->receiver_dst, ==, TO_ME);
484   /* not directed to it and not broadcasted, it cannot receive it */
485   g_assert_cmpint (f->politelistener_dst, ==, NONE_YET);
486   /* eavesdrop=true, it will receive the signal even though it's not directed
487    * to it */
488   g_assert_cmpint (f->eavesdropper_dst, ==, TO_OTHER);
489 }
490
491 static void
492 teardown (Fixture *f,
493     gconstpointer context G_GNUC_UNUSED)
494 {
495   dbus_error_free (&f->e);
496   g_clear_error (&f->ge);
497
498   if (f->sender != NULL)
499     {
500       dbus_connection_close (f->sender);
501       dbus_connection_unref (f->sender);
502       f->sender = NULL;
503     }
504
505   if (f->receiver != NULL)
506     {
507       dbus_connection_remove_filter (f->receiver,
508           signal_filter, f);
509
510       dbus_connection_close (f->receiver);
511       dbus_connection_unref (f->receiver);
512       f->receiver = NULL;
513     }
514
515   if (f->politelistener != NULL)
516     {
517       dbus_connection_remove_filter (f->politelistener,
518           signal_filter, f);
519
520       dbus_connection_close (f->politelistener);
521       dbus_connection_unref (f->politelistener);
522       f->politelistener = NULL;
523     }
524
525   if (f->eavesdropper != NULL)
526     {
527       dbus_connection_remove_filter (f->eavesdropper,
528           signal_filter, f);
529
530       dbus_connection_close (f->eavesdropper);
531       dbus_connection_unref (f->eavesdropper);
532       f->eavesdropper = NULL;
533     }
534
535 #ifdef DBUS_WIN
536   TerminateProcess (f->daemon_pid, 1);
537 #else
538   kill (f->daemon_pid, SIGTERM);
539 #endif
540
541   g_spawn_close_pid (f->daemon_pid);
542
543   test_main_context_unref (f->ctx);
544 }
545
546 int
547 main (int argc,
548     char **argv)
549 {
550   g_test_init (&argc, &argv, NULL);
551   g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
552
553   g_test_add ("/eavedrop/match_keyword/broadcast", Fixture, NULL,
554       setup, test_eavesdrop_broadcast, teardown);
555   g_test_add ("/eavedrop/match_keyword/unicast_to_receiver", Fixture, NULL,
556       setup, test_eavesdrop_unicast_to_receiver,
557       teardown);
558   g_test_add ("/eavedrop/match_keyword/unicast_to_sender", Fixture, NULL,
559       setup, test_eavesdrop_unicast_to_sender, teardown);
560
561   return g_test_run ();
562 }