Merge branch 'dbus-1.4'
[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 #include <dbus/dbus-glib-lowlevel.h>
34
35 #include <string.h>
36
37 #ifdef DBUS_WIN
38 # include <windows.h>
39 #else
40 # include <signal.h>
41 # include <unistd.h>
42 #endif
43
44 #define SENDER_NAME "test.eavesdrop.sender"
45 #define SENDER_PATH "/test/eavesdrop/sender"
46 #define SENDER_IFACE SENDER_NAME
47 #define SENDER_SIGNAL_NAME "Signal"
48 #define SENDER_STOPPER_NAME "Stopper"
49
50 /* This rule is equivalent to the one added to a proxy connecting to
51  * SENDER_NAME+SENDER_IFACE, plus restricting on signal name.
52  * Being more restrictive, if the connection receives what we need, for sure
53  * the original proxy rule will match it */
54 #define RECEIVER_RULE "sender='" SENDER_NAME "'," \
55   "interface='" SENDER_IFACE "'," \
56   "type='signal'," \
57   "member='" SENDER_SIGNAL_NAME "'"
58 #define POLITELISTENER_RULE RECEIVER_RULE
59 #define EAVESDROPPER_RULE RECEIVER_RULE ",eavesdrop=true"
60
61 #define STOPPER_RULE "sender='" SENDER_NAME \
62   "',interface='" SENDER_IFACE "',type='signal',member='" SENDER_STOPPER_NAME "'"
63
64 /* a connection received a signal to whom? */
65 typedef enum {
66   NONE_YET = 0,
67   TO_ME,
68   TO_OTHER,
69   BROADCAST,
70 } SignalDst;
71
72 typedef struct {
73     DBusError e;
74     GError *ge;
75
76     gint daemon_pid;
77
78     /* eavedrop keyword tests */
79     DBusConnection *sender;
80     DBusConnection *receiver;
81     SignalDst receiver_dst;
82     dbus_bool_t receiver_got_stopper;
83     DBusConnection *eavesdropper;
84     SignalDst eavesdropper_dst;
85     dbus_bool_t eavesdropper_got_stopper;
86     DBusConnection *politelistener;
87     SignalDst politelistener_dst;
88     dbus_bool_t politelistener_got_stopper;
89 } Fixture;
90
91 #define assert_no_error(e) _assert_no_error (e, __FILE__, __LINE__)
92 static void
93 _assert_no_error (const DBusError *e,
94     const char *file,
95     int line)
96 {
97   if (G_UNLIKELY (dbus_error_is_set (e)))
98     g_error ("%s:%d: expected success but got error: %s: %s",
99         file, line, e->name, e->message);
100 }
101
102 static gchar *
103 spawn_dbus_daemon (gchar *binary,
104     gchar *configuration,
105     gint *daemon_pid)
106 {
107   GError *error = NULL;
108   GString *address;
109   gint address_fd;
110   gchar *argv[] = {
111       binary,
112       configuration,
113       "--nofork",
114       "--print-address=1", /* stdout */
115       NULL
116   };
117
118   g_spawn_async_with_pipes (NULL, /* working directory */
119       argv,
120       NULL, /* envp */
121       G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
122       NULL, /* child_setup */
123       NULL, /* user data */
124       daemon_pid,
125       NULL, /* child's stdin = /dev/null */
126       &address_fd,
127       NULL, /* child's stderr = our stderr */
128       &error);
129   g_assert_no_error (error);
130
131   address = g_string_new (NULL);
132
133   /* polling until the dbus-daemon writes out its address is a bit stupid,
134    * but at least it's simple, unlike dbus-launch... in principle we could
135    * use select() here, but life's too short */
136   while (1)
137     {
138       gssize bytes;
139       gchar buf[4096];
140       gchar *newline;
141
142       bytes = read (address_fd, buf, sizeof (buf));
143
144       if (bytes > 0)
145         g_string_append_len (address, buf, bytes);
146
147       newline = strchr (address->str, '\n');
148
149       if (newline != NULL)
150         {
151           g_string_truncate (address, newline - address->str);
152           break;
153         }
154
155       g_usleep (G_USEC_PER_SEC / 10);
156     }
157
158   return g_string_free (address, FALSE);
159 }
160
161 static DBusConnection *
162 connect_to_bus (const gchar *address)
163 {
164   DBusConnection *conn;
165   DBusError error = DBUS_ERROR_INIT;
166   dbus_bool_t ok;
167
168   conn = dbus_connection_open_private (address, &error);
169   assert_no_error (&error);
170   g_assert (conn != NULL);
171
172   ok = dbus_bus_register (conn, &error);
173   assert_no_error (&error);
174   g_assert (ok);
175   g_assert (dbus_bus_get_unique_name (conn) != NULL);
176
177   dbus_connection_setup_with_g_main (conn, NULL);
178   return conn;
179 }
180
181 /* send a unicast signal to <self> to ensure that no other connection
182  * listening is the actual recipient for the signal */
183 static DBusHandlerResult
184 sender_send_unicast_to_sender (Fixture *f)
185 {
186   DBusError error = DBUS_ERROR_INIT;
187   DBusMessage *signal;
188
189   signal = dbus_message_new_signal (SENDER_PATH, SENDER_IFACE,
190       SENDER_SIGNAL_NAME);
191   dbus_message_set_destination (signal, dbus_bus_get_unique_name (f->sender));
192
193   if (signal == NULL)
194     g_error ("OOM");
195
196   if (!dbus_connection_send (f->sender, signal, NULL))
197     g_error ("OOM");
198
199   dbus_message_unref (signal);
200
201   return DBUS_HANDLER_RESULT_HANDLED;
202 }
203
204 /* send a unicast signal to <receiver>, making <politelistener> and
205  * <eavesdropper> not a actual recipient for it */
206 static DBusHandlerResult
207 sender_send_unicast_to_receiver (Fixture *f)
208 {
209   DBusError error = DBUS_ERROR_INIT;
210   DBusMessage *signal;
211
212   signal = dbus_message_new_signal (SENDER_PATH, SENDER_IFACE, SENDER_SIGNAL_NAME);
213   dbus_message_set_destination (signal, dbus_bus_get_unique_name (f->receiver));
214
215   if (signal == NULL)
216     g_error ("OOM");
217
218   if (!dbus_connection_send (f->sender, signal, NULL))
219     g_error ("OOM");
220
221   dbus_message_unref (signal);
222
223   return DBUS_HANDLER_RESULT_HANDLED;
224 }
225
226 static DBusHandlerResult
227 sender_send_broadcast (Fixture *f)
228 {
229   DBusError error = DBUS_ERROR_INIT;
230   DBusMessage *signal;
231
232   signal = dbus_message_new_signal (SENDER_PATH, SENDER_IFACE, SENDER_SIGNAL_NAME);
233   dbus_message_set_destination (signal, NULL);
234
235   if (signal == NULL)
236     g_error ("OOM");
237
238   if (!dbus_connection_send (f->sender, signal, NULL))
239     g_error ("OOM");
240
241   dbus_message_unref (signal);
242
243   return DBUS_HANDLER_RESULT_HANDLED;
244 }
245
246 /* Send special broadcast signal to indicate that the connections can "stop"
247  * listening and check their results.
248  * DBus does not re-order messages, so when the three connections have received
249  * this signal, we are sure that any message sent before it has also been
250  * dispatched. */
251 static DBusHandlerResult
252 sender_send_stopper (Fixture *f)
253 {
254   DBusError error = DBUS_ERROR_INIT;
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->ge = NULL;
387   dbus_error_init (&f->e);
388
389   dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON"));
390
391   if (dbus_daemon == NULL)
392     dbus_daemon = g_strdup ("dbus-daemon");
393
394   if (g_getenv ("DBUS_TEST_SYSCONFDIR") != NULL)
395     {
396       config = g_strdup_printf ("--config-file=%s/dbus-1/session.conf",
397           g_getenv ("DBUS_TEST_SYSCONFDIR"));
398     }
399   else if (g_getenv ("DBUS_TEST_DATA") != NULL)
400     {
401       config = g_strdup_printf (
402           "--config-file=%s/valid-config-files/session.conf",
403           g_getenv ("DBUS_TEST_DATA"));
404     }
405   else
406     {
407       config = g_strdup ("--session");
408     }
409
410   address = spawn_dbus_daemon (dbus_daemon, config, &f->daemon_pid);
411
412   g_free (dbus_daemon);
413   g_free (config);
414
415   f->sender = connect_to_bus (address);
416   dbus_bus_request_name (f->sender, SENDER_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE,
417       &(f->e));
418   f->receiver = connect_to_bus (address);
419   f->eavesdropper = connect_to_bus (address);
420   f->politelistener = connect_to_bus (address);
421   add_receiver_filter (f);
422   add_politelistener_filter (f);
423   add_eavesdropper_filter (f);
424
425   g_free (address);
426 }
427
428 static void
429 test_eavesdrop_broadcast (Fixture *f,
430     gconstpointer context G_GNUC_UNUSED)
431 {
432   sender_send_broadcast (f);
433   sender_send_stopper (f);
434
435   while (!f->receiver_got_stopper ||
436       !f->politelistener_got_stopper ||
437       !f->eavesdropper_got_stopper)
438     g_main_context_iteration (NULL, TRUE);
439
440   /* all the three connection can receive a broadcast */
441   g_assert_cmpint (f->receiver_dst, ==, BROADCAST);
442   g_assert_cmpint (f->politelistener_dst, ==, BROADCAST);
443   g_assert_cmpint (f->eavesdropper_dst, ==, BROADCAST);
444 }
445
446 /* a way to say that none of the listening connection are destination of the
447  * signal */
448 static void
449 test_eavesdrop_unicast_to_sender (Fixture *f,
450     gconstpointer context G_GNUC_UNUSED)
451 {
452   sender_send_unicast_to_sender (f);
453   sender_send_stopper (f);
454
455   while (!f->receiver_got_stopper ||
456       !f->politelistener_got_stopper ||
457       !f->eavesdropper_got_stopper)
458     g_main_context_iteration (NULL, TRUE);
459
460   /* not directed to it and not broadcasted, they cannot receive it */
461   g_assert_cmpint (f->receiver_dst, ==, NONE_YET);
462   g_assert_cmpint (f->politelistener_dst, ==, NONE_YET);
463   /* eavesdrop=true, it will receive the signal even though it's not directed
464    * to it */
465   g_assert_cmpint (f->eavesdropper_dst, ==, TO_OTHER);
466 }
467
468 static void
469 test_eavesdrop_unicast_to_receiver (Fixture *f,
470     gconstpointer context G_GNUC_UNUSED)
471 {
472   sender_send_unicast_to_receiver (f);
473   sender_send_stopper (f);
474
475   while (!f->receiver_got_stopper ||
476       !f->politelistener_got_stopper ||
477       !f->eavesdropper_got_stopper)
478     g_main_context_iteration (NULL, TRUE);
479
480   /* direct to him */
481   g_assert_cmpint (f->receiver_dst, ==, TO_ME);
482   /* not directed to it and not broadcasted, it cannot receive it */
483   g_assert_cmpint (f->politelistener_dst, ==, NONE_YET);
484   /* eavesdrop=true, it will receive the signal even though it's not directed
485    * to it */
486   g_assert_cmpint (f->eavesdropper_dst, ==, TO_OTHER);
487 }
488
489 static void
490 teardown (Fixture *f,
491     gconstpointer context G_GNUC_UNUSED)
492 {
493   dbus_error_free (&f->e);
494   g_clear_error (&f->ge);
495
496   if (f->sender != NULL)
497     {
498       dbus_connection_close (f->sender);
499       dbus_connection_unref (f->sender);
500       f->sender = NULL;
501     }
502
503   if (f->receiver != NULL)
504     {
505       dbus_connection_remove_filter (f->receiver,
506           signal_filter, f);
507
508       dbus_connection_close (f->receiver);
509       dbus_connection_unref (f->receiver);
510       f->receiver = NULL;
511     }
512
513   if (f->politelistener != NULL)
514     {
515       dbus_connection_remove_filter (f->politelistener,
516           signal_filter, f);
517
518       dbus_connection_close (f->politelistener);
519       dbus_connection_unref (f->politelistener);
520       f->politelistener = NULL;
521     }
522
523   if (f->eavesdropper != NULL)
524     {
525       dbus_connection_remove_filter (f->eavesdropper,
526           signal_filter, f);
527
528       dbus_connection_close (f->eavesdropper);
529       dbus_connection_unref (f->eavesdropper);
530       f->eavesdropper = NULL;
531     }
532
533 #ifdef DBUS_WIN
534   TerminateProcess (f->daemon_pid, 1);
535 #else
536   kill (f->daemon_pid, SIGTERM);
537 #endif
538
539   g_spawn_close_pid (f->daemon_pid);
540 }
541
542 int
543 main (int argc,
544     char **argv)
545 {
546   g_test_init (&argc, &argv, NULL);
547   g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
548
549   g_test_add ("/eavedrop/match_keyword/broadcast", Fixture, NULL,
550       setup, test_eavesdrop_broadcast, teardown);
551   g_test_add ("/eavedrop/match_keyword/unicast_to_receiver", Fixture, NULL,
552       setup, test_eavesdrop_unicast_to_receiver,
553       teardown);
554   g_test_add ("/eavedrop/match_keyword/unicast_to_sender", Fixture, NULL,
555       setup, test_eavesdrop_unicast_to_sender, teardown);
556
557   return g_test_run ();
558 }