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