1 /* Utility functions for tests that rely on GLib
3 * Copyright © 2010-2011 Nokia Corporation
4 * Copyright © 2013-2015 Collabora Ltd.
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation files
8 * (the "Software"), to deal in the Software without restriction,
9 * including without limitation the rights to use, copy, modify, merge,
10 * publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 #include "test-utils-glib.h"
40 # include <sys/socket.h>
41 # include <sys/types.h>
46 #include <glib/gstdio.h>
48 #include <dbus/dbus.h>
51 # define isatty(x) _isatty(x)
55 _test_assert_no_error (const DBusError *e,
59 if (G_UNLIKELY (dbus_error_is_set (e)))
60 g_error ("%s:%d: expected success but got error: %s: %s",
61 file, line, e->name, e->message);
66 child_setup (gpointer user_data)
68 const struct passwd *pwd = user_data;
69 uid_t uid = geteuid ();
71 if (pwd == NULL || (pwd->pw_uid == uid && getuid () == uid))
75 g_error ("not currently euid 0: %lu", (unsigned long) uid);
77 if (setuid (pwd->pw_uid) != 0)
78 g_error ("could not setuid (%lu): %s",
79 (unsigned long) pwd->pw_uid, g_strerror (errno));
83 if (uid != pwd->pw_uid)
84 g_error ("after successful setuid (%lu) my uid is %ld",
85 (unsigned long) pwd->pw_uid, (unsigned long) uid);
89 if (uid != pwd->pw_uid)
90 g_error ("after successful setuid (%lu) my euid is %ld",
91 (unsigned long) pwd->pw_uid, (unsigned long) uid);
96 spawn_dbus_daemon (const gchar *binary,
97 const gchar *configuration,
98 const gchar *listen_address,
100 const gchar *runtime_dir,
103 GError *error = NULL;
109 const struct passwd *pwd = NULL;
112 if (user != TEST_USER_ME)
117 g_test_skip ("cannot use alternative uid when not uid 0");
126 case TEST_USER_MESSAGEBUS:
127 pwd = getpwnam (DBUS_USER);
131 gchar *message = g_strdup_printf ("user '%s' does not exist",
134 g_test_skip (message);
141 case TEST_USER_OTHER:
142 pwd = getpwnam (DBUS_TEST_USER);
146 gchar *message = g_strdup_printf ("user '%s' does not exist",
149 g_test_skip (message);
157 /* cannot get here, fall through */
159 g_assert_not_reached ();
162 g_test_skip ("cannot use alternative uid on Windows");
167 envp = g_get_environ ();
169 if (runtime_dir != NULL)
170 envp = g_environ_setenv (envp, "XDG_RUNTIME_DIR", runtime_dir, TRUE);
172 argv = g_ptr_array_new_with_free_func (g_free);
173 g_ptr_array_add (argv, g_strdup (binary));
174 g_ptr_array_add (argv, g_strdup (configuration));
175 g_ptr_array_add (argv, g_strdup ("--nofork"));
176 g_ptr_array_add (argv, g_strdup ("--print-address=1")); /* stdout */
178 if (listen_address != NULL)
179 g_ptr_array_add (argv, g_strdup (listen_address));
182 g_ptr_array_add (argv, g_strdup ("--systemd-activation"));
185 g_ptr_array_add (argv, NULL);
187 g_spawn_async_with_pipes (NULL, /* working directory */
188 (gchar **) argv->pdata,
190 G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
192 child_setup, (gpointer) pwd,
197 NULL, /* child's stdin = /dev/null */
199 NULL, /* child's stderr = our stderr */
201 g_assert_no_error (error);
203 g_ptr_array_free (argv, TRUE);
206 address = g_string_new (NULL);
208 /* polling until the dbus-daemon writes out its address is a bit stupid,
209 * but at least it's simple, unlike dbus-launch... in principle we could
210 * use select() here, but life's too short */
217 bytes = read (address_fd, buf, sizeof (buf));
220 g_string_append_len (address, buf, bytes);
222 newline = strchr (address->str, '\n');
226 if ((newline > address->str) && ('\r' == newline[-1]))
228 g_string_truncate (address, newline - address->str);
232 g_usleep (G_USEC_PER_SEC / 10);
235 g_close (address_fd, NULL);
237 return g_string_free (address, FALSE);
241 test_get_dbus_daemon (const gchar *config_file,
243 const gchar *runtime_dir,
248 const gchar *listen_address = NULL;
251 /* we often have to override this because on Windows, the default may be
252 * autolaunch:, which is globally-scoped and hence unsuitable for
253 * regression tests */
254 listen_address = "--address=" TEST_LISTEN;
256 if (config_file != NULL)
259 if (g_getenv ("DBUS_TEST_DATA") == NULL)
261 g_test_message ("set DBUS_TEST_DATA to a directory containing %s",
263 g_test_skip ("DBUS_TEST_DATA not set");
267 arg = g_strdup_printf (
268 "--config-file=%s/%s",
269 g_getenv ("DBUS_TEST_DATA"), config_file);
271 /* The configuration file is expected to give a suitable address,
272 * do not override it */
273 listen_address = NULL;
275 else if (g_getenv ("DBUS_TEST_DATADIR") != NULL)
277 arg = g_strdup_printf ("--config-file=%s/dbus-1/session.conf",
278 g_getenv ("DBUS_TEST_DATADIR"));
280 else if (g_getenv ("DBUS_TEST_DATA") != NULL)
282 arg = g_strdup_printf (
283 "--config-file=%s/valid-config-files/session.conf",
284 g_getenv ("DBUS_TEST_DATA"));
288 arg = g_strdup ("--session");
291 dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON"));
293 if (dbus_daemon == NULL)
294 dbus_daemon = g_strdup ("dbus-daemon");
296 if (g_getenv ("DBUS_TEST_DAEMON_ADDRESS") != NULL)
298 if (config_file != NULL || user != TEST_USER_ME)
300 g_test_skip ("cannot use DBUS_TEST_DAEMON_ADDRESS for "
301 "unusally-configured dbus-daemon");
306 address = g_strdup (g_getenv ("DBUS_TEST_DAEMON_ADDRESS"));
311 address = spawn_dbus_daemon (dbus_daemon, arg,
312 listen_address, user, runtime_dir, daemon_pid);
315 g_free (dbus_daemon);
321 test_connect_to_bus (TestMainContext *ctx,
322 const gchar *address)
324 DBusConnection *conn;
325 DBusError error = DBUS_ERROR_INIT;
328 conn = dbus_connection_open_private (address, &error);
329 test_assert_no_error (&error);
330 g_assert (conn != NULL);
332 ok = dbus_bus_register (conn, &error);
333 test_assert_no_error (&error);
335 g_assert (dbus_bus_get_unique_name (conn) != NULL);
338 test_connection_setup (ctx, conn);
344 test_connect_to_bus_as_user (TestMainContext *ctx,
348 /* For now we only do tests like this on Linux, because I don't know how
349 * safe this use of setresuid() is on other platforms */
350 #if defined(HAVE_GETRESUID) && defined(HAVE_SETRESUID) && defined(__linux__)
351 uid_t ruid, euid, suid;
352 const struct passwd *pwd;
353 DBusConnection *conn;
354 const char *username;
359 return test_connect_to_bus (ctx, address);
365 case TEST_USER_MESSAGEBUS:
366 username = DBUS_USER;
369 case TEST_USER_OTHER:
370 username = DBUS_TEST_USER;
374 g_return_val_if_reached (NULL);
377 if (getresuid (&ruid, &euid, &suid) != 0)
378 g_error ("getresuid: %s", g_strerror (errno));
380 if (ruid != 0 || euid != 0 || suid != 0)
382 g_test_message ("not uid 0 (ruid=%ld euid=%ld suid=%ld)",
383 (unsigned long) ruid, (unsigned long) euid, (unsigned long) suid);
384 g_test_skip ("not uid 0");
388 pwd = getpwnam (username);
392 g_test_message ("getpwnam(\"%s\"): %s", username, g_strerror (errno));
393 g_test_skip ("not uid 0");
397 /* Impersonate the desired user while we connect to the bus.
398 * This should work, because we're root. */
399 if (setresuid (pwd->pw_uid, pwd->pw_uid, 0) != 0)
400 g_error ("setresuid(%ld, (same), 0): %s",
401 (unsigned long) pwd->pw_uid, g_strerror (errno));
403 conn = test_connect_to_bus (ctx, address);
405 /* go back to our saved uid */
406 if (setresuid (0, 0, 0) != 0)
407 g_error ("setresuid(0, 0, 0): %s", g_strerror (errno));
416 return test_connect_to_bus (ctx, address);
419 case TEST_USER_MESSAGEBUS:
420 case TEST_USER_OTHER:
421 g_test_skip ("setresuid() not available, or unsure about "
422 "credentials-passing semantics on this platform");
426 g_return_val_if_reached (NULL);
437 gboolean *result = user_data;
439 g_assert (result != NULL);
445 test_kill_pid (GPid pid)
449 g_child_watch_add (pid, pid_died, &died);
453 TerminateProcess (pid, 1);
460 g_main_context_iteration (NULL, TRUE);
464 time_out (gpointer data)
466 puts ("Bail out! Test timed out (GLib main loop timeout callback reached)");
473 static void wrap_abort (int signal) _DBUS_GNUC_NORETURN;
476 wrap_abort (int signal)
478 /* We might be halfway through writing out something else, so force this
479 * onto its own line */
480 const char message [] = "\nBail out! Test timed out (SIGALRM received)\n";
482 if (write (STDOUT_FILENO, message, sizeof (message) - 1) <
483 (ssize_t) sizeof (message) - 1)
485 /* ignore short write - what would we do about it? */
493 set_timeout (guint factor)
495 static guint timeout = 0;
497 /* Prevent tests from hanging forever. This is intended to be long enough
498 * that any reasonable regression test on any reasonable hardware would
503 g_source_remove (timeout);
505 timeout = g_timeout_add_seconds (TIMEOUT * factor, time_out, NULL);
507 /* The GLib main loop might not be running (we don't use it in every
508 * test). Die with SIGALRM shortly after if necessary. */
509 alarm ((TIMEOUT * factor) + 10);
511 /* Get a log message and a core dump from the SIGALRM. */
513 struct sigaction act = { };
515 act.sa_handler = wrap_abort;
517 sigaction (SIGALRM, &act, NULL);
523 test_init (int *argcp, char ***argvp)
525 g_test_init (argcp, argvp, NULL);
526 g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
531 report_and_destroy (gpointer p)
535 g_test_message ("Time since timeout reset %p: %.3f seconds",
536 timer, g_timer_elapsed (timer, NULL));
537 g_timer_destroy (timer);
541 test_timeout_reset (guint factor)
543 GTimer *timer = g_timer_new ();
545 g_test_message ("Resetting test timeout (reference: %p; factor: %u)",
547 set_timeout (factor);
549 g_test_queue_destroy (report_and_destroy, timer);
553 test_progress (char symbol)
555 if (g_test_verbose () && isatty (1))
556 g_print ("%c", symbol);
560 * Delete @path, with a retry loop if the system call is interrupted by
561 * an async signal. If @path does not exist, ignore; otherwise, it is
562 * required to be a non-directory.
565 test_remove_if_exists (const gchar *path)
567 while (g_remove (path) != 0)
569 int saved_errno = errno;
571 if (saved_errno == ENOENT)
575 if (saved_errno == EINTR)
579 g_error ("Unable to remove file \"%s\": %s", path,
580 g_strerror (saved_errno));
585 * Delete empty directory @path, with a retry loop if the system call is
586 * interrupted by an async signal. @path is required to exist.
589 test_rmdir_must_exist (const gchar *path)
591 while (g_remove (path) != 0)
593 int saved_errno = errno;
596 if (saved_errno == EINTR)
600 g_error ("Unable to remove directory \"%s\": %s", path,
601 g_strerror (saved_errno));
606 * Delete empty directory @path, with a retry loop if the system call is
607 * interrupted by an async signal. If @path does not exist, ignore.
610 test_rmdir_if_exists (const gchar *path)
612 while (g_remove (path) != 0)
614 int saved_errno = errno;
616 if (saved_errno == ENOENT)
620 if (saved_errno == EINTR)
624 g_error ("Unable to remove directory \"%s\": %s", path,
625 g_strerror (saved_errno));
630 * Create directory @path, with a retry loop if the system call is
631 * interrupted by an async signal.
634 test_mkdir (const gchar *path,
637 while (g_mkdir (path, mode) != 0)
639 int saved_errno = errno;
642 if (saved_errno == EINTR)
646 g_error ("Unable to create directory \"%s\": %s", path,
647 g_strerror (saved_errno));
652 test_check_tcp_works (void)
655 /* In pathological container environments, we might not have a
656 * working 127.0.0.1 */
658 struct addrinfo *addrs = NULL;
659 struct addrinfo hints;
664 hints.ai_flags |= AI_ADDRCONFIG;
666 hints.ai_flags = AI_ADDRCONFIG;
667 hints.ai_family = AF_INET;
668 hints.ai_socktype = SOCK_STREAM;
669 hints.ai_protocol = IPPROTO_TCP;
671 res = getaddrinfo ("127.0.0.1", "0", &hints, &addrs);
676 const gchar *system_message;
680 if (res == EAI_SYSTEM)
681 system_message = g_strerror (saved_errno);
684 system_message = gai_strerror (res);
686 skip_message = g_strdup_printf ("Name resolution does not work here: "
687 "getaddrinfo(\"127.0.0.1\", \"0\", "
688 "{flags=ADDRCONFIG, family=INET,"
689 "socktype=STREAM, protocol=TCP}): "
692 g_test_skip (skip_message);
697 freeaddrinfo (addrs);
701 /* Assume that on Windows, TCP always works */