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"
38 # include <netinet/in.h>
41 # include <sys/socket.h>
42 # include <sys/types.h>
47 #include <glib/gstdio.h>
49 #include <dbus/dbus.h>
52 # define isatty(x) _isatty(x)
56 _test_assert_no_error (const DBusError *e,
60 if (G_UNLIKELY (dbus_error_is_set (e)))
61 g_error ("%s:%d: expected success but got error: %s: %s",
62 file, line, e->name, e->message);
67 child_setup (gpointer user_data)
69 const struct passwd *pwd = user_data;
70 uid_t uid = geteuid ();
72 if (pwd == NULL || (pwd->pw_uid == uid && getuid () == uid))
76 g_error ("not currently euid 0: %lu", (unsigned long) uid);
78 if (setuid (pwd->pw_uid) != 0)
79 g_error ("could not setuid (%lu): %s",
80 (unsigned long) pwd->pw_uid, g_strerror (errno));
84 if (uid != pwd->pw_uid)
85 g_error ("after successful setuid (%lu) my uid is %ld",
86 (unsigned long) pwd->pw_uid, (unsigned long) uid);
90 if (uid != pwd->pw_uid)
91 g_error ("after successful setuid (%lu) my euid is %ld",
92 (unsigned long) pwd->pw_uid, (unsigned long) uid);
97 spawn_dbus_daemon (const gchar *binary,
98 const gchar *configuration,
99 const gchar *listen_address,
101 const gchar *runtime_dir,
104 GError *error = NULL;
110 const struct passwd *pwd = NULL;
113 if (user != TEST_USER_ME)
118 g_test_skip ("cannot use alternative uid when not uid 0");
127 case TEST_USER_MESSAGEBUS:
128 pwd = getpwnam (DBUS_USER);
132 gchar *message = g_strdup_printf ("user '%s' does not exist",
135 g_test_skip (message);
142 case TEST_USER_OTHER:
143 pwd = getpwnam (DBUS_TEST_USER);
147 gchar *message = g_strdup_printf ("user '%s' does not exist",
150 g_test_skip (message);
158 /* cannot get here, fall through */
160 g_assert_not_reached ();
163 g_test_skip ("cannot use alternative uid on Windows");
168 envp = g_get_environ ();
170 if (runtime_dir != NULL)
171 envp = g_environ_setenv (envp, "XDG_RUNTIME_DIR", runtime_dir, TRUE);
173 argv = g_ptr_array_new_with_free_func (g_free);
174 g_ptr_array_add (argv, g_strdup (binary));
175 g_ptr_array_add (argv, g_strdup (configuration));
176 g_ptr_array_add (argv, g_strdup ("--nofork"));
177 g_ptr_array_add (argv, g_strdup ("--print-address=1")); /* stdout */
179 if (listen_address != NULL)
180 g_ptr_array_add (argv, g_strdup (listen_address));
183 g_ptr_array_add (argv, g_strdup ("--systemd-activation"));
186 g_ptr_array_add (argv, NULL);
188 g_spawn_async_with_pipes (NULL, /* working directory */
189 (gchar **) argv->pdata,
191 G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
193 child_setup, (gpointer) pwd,
198 NULL, /* child's stdin = /dev/null */
200 NULL, /* child's stderr = our stderr */
202 g_assert_no_error (error);
204 g_ptr_array_free (argv, TRUE);
207 address = g_string_new (NULL);
209 /* polling until the dbus-daemon writes out its address is a bit stupid,
210 * but at least it's simple, unlike dbus-launch... in principle we could
211 * use select() here, but life's too short */
218 bytes = read (address_fd, buf, sizeof (buf));
221 g_string_append_len (address, buf, bytes);
223 newline = strchr (address->str, '\n');
227 if ((newline > address->str) && ('\r' == newline[-1]))
229 g_string_truncate (address, newline - address->str);
233 g_usleep (G_USEC_PER_SEC / 10);
236 g_close (address_fd, NULL);
238 return g_string_free (address, FALSE);
242 test_get_dbus_daemon (const gchar *config_file,
244 const gchar *runtime_dir,
249 const gchar *listen_address = NULL;
252 /* we often have to override this because on Windows, the default may be
253 * autolaunch:, which is globally-scoped and hence unsuitable for
254 * regression tests */
255 listen_address = "--address=" TEST_LISTEN;
257 if (config_file != NULL)
260 if (g_getenv ("DBUS_TEST_DATA") == NULL)
262 g_test_message ("set DBUS_TEST_DATA to a directory containing %s",
264 g_test_skip ("DBUS_TEST_DATA not set");
268 arg = g_strdup_printf (
269 "--config-file=%s/%s",
270 g_getenv ("DBUS_TEST_DATA"), config_file);
272 /* The configuration file is expected to give a suitable address,
273 * do not override it */
274 listen_address = NULL;
276 else if (g_getenv ("DBUS_TEST_DATADIR") != NULL)
278 arg = g_strdup_printf ("--config-file=%s/dbus-1/session.conf",
279 g_getenv ("DBUS_TEST_DATADIR"));
281 else if (g_getenv ("DBUS_TEST_DATA") != NULL)
283 arg = g_strdup_printf (
284 "--config-file=%s/valid-config-files/session.conf",
285 g_getenv ("DBUS_TEST_DATA"));
289 arg = g_strdup ("--session");
292 dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON"));
294 if (dbus_daemon == NULL)
295 dbus_daemon = g_strdup ("dbus-daemon");
297 if (g_getenv ("DBUS_TEST_DAEMON_ADDRESS") != NULL)
299 if (config_file != NULL || user != TEST_USER_ME)
301 g_test_skip ("cannot use DBUS_TEST_DAEMON_ADDRESS for "
302 "unusally-configured dbus-daemon");
307 address = g_strdup (g_getenv ("DBUS_TEST_DAEMON_ADDRESS"));
312 address = spawn_dbus_daemon (dbus_daemon, arg,
313 listen_address, user, runtime_dir, daemon_pid);
316 g_free (dbus_daemon);
322 test_connect_to_bus (TestMainContext *ctx,
323 const gchar *address)
325 DBusConnection *conn;
326 DBusError error = DBUS_ERROR_INIT;
329 conn = dbus_connection_open_private (address, &error);
330 test_assert_no_error (&error);
331 g_assert (conn != NULL);
333 ok = dbus_bus_register (conn, &error);
334 test_assert_no_error (&error);
336 g_assert (dbus_bus_get_unique_name (conn) != NULL);
339 test_connection_setup (ctx, conn);
345 test_connect_to_bus_as_user (TestMainContext *ctx,
349 /* For now we only do tests like this on Linux, because I don't know how
350 * safe this use of setresuid() is on other platforms */
351 #if defined(HAVE_GETRESUID) && defined(HAVE_SETRESUID) && defined(__linux__)
352 uid_t ruid, euid, suid;
353 const struct passwd *pwd;
354 DBusConnection *conn;
355 const char *username;
360 return test_connect_to_bus (ctx, address);
366 case TEST_USER_MESSAGEBUS:
367 username = DBUS_USER;
370 case TEST_USER_OTHER:
371 username = DBUS_TEST_USER;
375 g_return_val_if_reached (NULL);
378 if (getresuid (&ruid, &euid, &suid) != 0)
379 g_error ("getresuid: %s", g_strerror (errno));
381 if (ruid != 0 || euid != 0 || suid != 0)
383 g_test_message ("not uid 0 (ruid=%ld euid=%ld suid=%ld)",
384 (unsigned long) ruid, (unsigned long) euid, (unsigned long) suid);
385 g_test_skip ("not uid 0");
389 pwd = getpwnam (username);
393 g_test_message ("getpwnam(\"%s\"): %s", username, g_strerror (errno));
394 g_test_skip ("not uid 0");
398 /* Impersonate the desired user while we connect to the bus.
399 * This should work, because we're root. */
400 if (setresuid (pwd->pw_uid, pwd->pw_uid, 0) != 0)
401 g_error ("setresuid(%ld, (same), 0): %s",
402 (unsigned long) pwd->pw_uid, g_strerror (errno));
404 conn = test_connect_to_bus (ctx, address);
406 /* go back to our saved uid */
407 if (setresuid (0, 0, 0) != 0)
408 g_error ("setresuid(0, 0, 0): %s", g_strerror (errno));
417 return test_connect_to_bus (ctx, address);
420 case TEST_USER_MESSAGEBUS:
421 case TEST_USER_OTHER:
422 g_test_skip ("setresuid() not available, or unsure about "
423 "credentials-passing semantics on this platform");
427 g_return_val_if_reached (NULL);
438 gboolean *result = user_data;
440 g_assert (result != NULL);
446 test_kill_pid (GPid pid)
450 g_child_watch_add (pid, pid_died, &died);
454 TerminateProcess (pid, 1);
461 g_main_context_iteration (NULL, TRUE);
465 time_out (gpointer data)
467 puts ("Bail out! Test timed out (GLib main loop timeout callback reached)");
474 static void wrap_abort (int signal) _DBUS_GNUC_NORETURN;
477 wrap_abort (int signal)
479 /* We might be halfway through writing out something else, so force this
480 * onto its own line */
481 const char message [] = "\nBail out! Test timed out (SIGALRM received)\n";
483 if (write (STDOUT_FILENO, message, sizeof (message) - 1) <
484 (ssize_t) sizeof (message) - 1)
486 /* ignore short write - what would we do about it? */
494 set_timeout (guint factor)
496 static guint timeout = 0;
498 /* Prevent tests from hanging forever. This is intended to be long enough
499 * that any reasonable regression test on any reasonable hardware would
504 g_source_remove (timeout);
506 timeout = g_timeout_add_seconds (TIMEOUT * factor, time_out, NULL);
508 /* The GLib main loop might not be running (we don't use it in every
509 * test). Die with SIGALRM shortly after if necessary. */
510 alarm ((TIMEOUT * factor) + 10);
512 /* Get a log message and a core dump from the SIGALRM. */
514 struct sigaction act = { };
516 act.sa_handler = wrap_abort;
518 sigaction (SIGALRM, &act, NULL);
524 test_init (int *argcp, char ***argvp)
526 g_test_init (argcp, argvp, NULL);
527 g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
532 report_and_destroy (gpointer p)
536 g_test_message ("Time since timeout reset %p: %.3f seconds",
537 timer, g_timer_elapsed (timer, NULL));
538 g_timer_destroy (timer);
542 test_timeout_reset (guint factor)
544 GTimer *timer = g_timer_new ();
546 g_test_message ("Resetting test timeout (reference: %p; factor: %u)",
548 set_timeout (factor);
550 g_test_queue_destroy (report_and_destroy, timer);
554 test_progress (char symbol)
556 if (g_test_verbose () && isatty (1))
557 g_print ("%c", symbol);
561 * Delete @path, with a retry loop if the system call is interrupted by
562 * an async signal. If @path does not exist, ignore; otherwise, it is
563 * required to be a non-directory.
566 test_remove_if_exists (const gchar *path)
568 while (g_remove (path) != 0)
570 int saved_errno = errno;
572 if (saved_errno == ENOENT)
576 if (saved_errno == EINTR)
580 g_error ("Unable to remove file \"%s\": %s", path,
581 g_strerror (saved_errno));
586 * Delete empty directory @path, with a retry loop if the system call is
587 * interrupted by an async signal. @path is required to exist.
590 test_rmdir_must_exist (const gchar *path)
592 while (g_remove (path) != 0)
594 int saved_errno = errno;
597 if (saved_errno == EINTR)
601 g_error ("Unable to remove directory \"%s\": %s", path,
602 g_strerror (saved_errno));
607 * Delete empty directory @path, with a retry loop if the system call is
608 * interrupted by an async signal. If @path does not exist, ignore.
611 test_rmdir_if_exists (const gchar *path)
613 while (g_remove (path) != 0)
615 int saved_errno = errno;
617 if (saved_errno == ENOENT)
621 if (saved_errno == EINTR)
625 g_error ("Unable to remove directory \"%s\": %s", path,
626 g_strerror (saved_errno));
631 * Create directory @path, with a retry loop if the system call is
632 * interrupted by an async signal.
635 test_mkdir (const gchar *path,
638 while (g_mkdir (path, mode) != 0)
640 int saved_errno = errno;
643 if (saved_errno == EINTR)
647 g_error ("Unable to create directory \"%s\": %s", path,
648 g_strerror (saved_errno));
653 test_check_tcp_works (void)
656 /* In pathological container environments, we might not have a
657 * working 127.0.0.1 */
659 struct addrinfo *addrs = NULL;
660 struct addrinfo hints;
665 hints.ai_flags |= AI_ADDRCONFIG;
667 hints.ai_flags = AI_ADDRCONFIG;
668 hints.ai_family = AF_INET;
669 hints.ai_socktype = SOCK_STREAM;
670 hints.ai_protocol = IPPROTO_TCP;
672 res = getaddrinfo ("127.0.0.1", "0", &hints, &addrs);
677 const gchar *system_message;
681 if (res == EAI_SYSTEM)
682 system_message = g_strerror (saved_errno);
685 system_message = gai_strerror (res);
687 skip_message = g_strdup_printf ("Name resolution does not work here: "
688 "getaddrinfo(\"127.0.0.1\", \"0\", "
689 "{flags=ADDRCONFIG, family=INET,"
690 "socktype=STREAM, protocol=TCP}): "
693 g_test_skip (skip_message);
698 freeaddrinfo (addrs);
702 /* Assume that on Windows, TCP always works */