Minor fix for the license of tests package
[platform/upstream/dbus.git] / test / test-utils-glib.c
1 /* Utility functions for tests that rely on GLib
2  *
3  * Copyright © 2010-2011 Nokia Corporation
4  * Copyright © 2013-2015 Collabora Ltd.
5  *
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:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
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
24  * SOFTWARE.
25  */
26
27 #include <config.h>
28 #include "test-utils-glib.h"
29
30 #include <errno.h>
31 #include <string.h>
32
33 #ifdef DBUS_WIN
34 # include <io.h>
35 # include <windows.h>
36 #else
37 # include <netdb.h>
38 # include <signal.h>
39 # include <unistd.h>
40 # include <sys/socket.h>
41 # include <sys/types.h>
42 # include <pwd.h>
43 #endif
44
45 #include <glib.h>
46 #include <glib/gstdio.h>
47
48 #include <dbus/dbus.h>
49
50 #ifdef G_OS_WIN
51 # define isatty(x) _isatty(x)
52 #endif
53
54 void
55 _test_assert_no_error (const DBusError *e,
56     const char *file,
57     int line)
58 {
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);
62 }
63
64 #ifdef DBUS_UNIX
65 static void
66 child_setup (gpointer user_data)
67 {
68   const struct passwd *pwd = user_data;
69   uid_t uid = geteuid ();
70
71   if (pwd == NULL || (pwd->pw_uid == uid && getuid () == uid))
72     return;
73
74   if (uid != 0)
75     g_error ("not currently euid 0: %lu", (unsigned long) uid);
76
77   if (setuid (pwd->pw_uid) != 0)
78     g_error ("could not setuid (%lu): %s",
79         (unsigned long) pwd->pw_uid, g_strerror (errno));
80
81   uid = getuid ();
82
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);
86
87   uid = geteuid ();
88
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);
92 }
93 #endif
94
95 static gchar *
96 spawn_dbus_daemon (const gchar *binary,
97     const gchar *configuration,
98     const gchar *listen_address,
99     TestUser user,
100     const gchar *runtime_dir,
101     GPid *daemon_pid)
102 {
103   GError *error = NULL;
104   GString *address;
105   gint address_fd;
106   GPtrArray *argv;
107   gchar **envp;
108 #ifdef DBUS_UNIX
109   const struct passwd *pwd = NULL;
110 #endif
111
112   if (user != TEST_USER_ME)
113     {
114 #ifdef DBUS_UNIX
115       if (getuid () != 0)
116         {
117           g_test_skip ("cannot use alternative uid when not uid 0");
118           return NULL;
119         }
120
121       switch (user)
122         {
123           case TEST_USER_ROOT:
124             break;
125
126           case TEST_USER_MESSAGEBUS:
127             pwd = getpwnam (DBUS_USER);
128
129             if (pwd == NULL)
130               {
131                 gchar *message = g_strdup_printf ("user '%s' does not exist",
132                     DBUS_USER);
133
134                 g_test_skip (message);
135                 g_free (message);
136                 return NULL;
137               }
138
139             break;
140
141           case TEST_USER_OTHER:
142             pwd = getpwnam (DBUS_TEST_USER);
143
144             if (pwd == NULL)
145               {
146                 gchar *message = g_strdup_printf ("user '%s' does not exist",
147                     DBUS_TEST_USER);
148
149                 g_test_skip (message);
150                 g_free (message);
151                 return NULL;
152               }
153
154             break;
155
156           case TEST_USER_ME:
157             /* cannot get here, fall through */
158           default:
159             g_assert_not_reached ();
160         }
161 #else
162       g_test_skip ("cannot use alternative uid on Windows");
163       return NULL;
164 #endif
165     }
166
167   envp = g_get_environ ();
168
169   if (runtime_dir != NULL)
170     envp = g_environ_setenv (envp, "XDG_RUNTIME_DIR", runtime_dir, TRUE);
171
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 */
177
178   if (listen_address != NULL)
179     g_ptr_array_add (argv, g_strdup (listen_address));
180
181 #ifdef DBUS_UNIX
182   g_ptr_array_add (argv, g_strdup ("--systemd-activation"));
183 #endif
184
185   g_ptr_array_add (argv, NULL);
186
187   g_spawn_async_with_pipes (NULL, /* working directory */
188       (gchar **) argv->pdata,
189       envp,
190       G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
191 #ifdef DBUS_UNIX
192       child_setup, (gpointer) pwd,
193 #else
194       NULL, NULL,
195 #endif
196       daemon_pid,
197       NULL, /* child's stdin = /dev/null */
198       &address_fd,
199       NULL, /* child's stderr = our stderr */
200       &error);
201   g_assert_no_error (error);
202
203   g_ptr_array_free (argv, TRUE);
204   g_strfreev (envp);
205
206   address = g_string_new (NULL);
207
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 */
211   while (1)
212     {
213       gssize bytes;
214       gchar buf[4096];
215       gchar *newline;
216
217       bytes = read (address_fd, buf, sizeof (buf));
218
219       if (bytes > 0)
220         g_string_append_len (address, buf, bytes);
221
222       newline = strchr (address->str, '\n');
223
224       if (newline != NULL)
225         {
226           if ((newline > address->str) && ('\r' == newline[-1]))
227             newline -= 1;
228           g_string_truncate (address, newline - address->str);
229           break;
230         }
231
232       g_usleep (G_USEC_PER_SEC / 10);
233     }
234
235   g_close (address_fd, NULL);
236
237   return g_string_free (address, FALSE);
238 }
239
240 gchar *
241 test_get_dbus_daemon (const gchar *config_file,
242                       TestUser     user,
243                       const gchar *runtime_dir,
244                       GPid        *daemon_pid)
245 {
246   gchar *dbus_daemon;
247   gchar *arg;
248   const gchar *listen_address = NULL;
249   gchar *address;
250
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;
255
256   if (config_file != NULL)
257     {
258
259       if (g_getenv ("DBUS_TEST_DATA") == NULL)
260         {
261           g_test_message ("set DBUS_TEST_DATA to a directory containing %s",
262               config_file);
263           g_test_skip ("DBUS_TEST_DATA not set");
264           return NULL;
265         }
266
267       arg = g_strdup_printf (
268           "--config-file=%s/%s",
269           g_getenv ("DBUS_TEST_DATA"), config_file);
270
271       /* The configuration file is expected to give a suitable address,
272        * do not override it */
273       listen_address = NULL;
274     }
275   else if (g_getenv ("DBUS_TEST_DATADIR") != NULL)
276     {
277       arg = g_strdup_printf ("--config-file=%s/dbus-1/session.conf",
278           g_getenv ("DBUS_TEST_DATADIR"));
279     }
280   else if (g_getenv ("DBUS_TEST_DATA") != NULL)
281     {
282       arg = g_strdup_printf (
283           "--config-file=%s/valid-config-files/session.conf",
284           g_getenv ("DBUS_TEST_DATA"));
285     }
286   else
287     {
288       arg = g_strdup ("--session");
289     }
290
291   dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON"));
292
293   if (dbus_daemon == NULL)
294     dbus_daemon = g_strdup ("dbus-daemon");
295
296   if (g_getenv ("DBUS_TEST_DAEMON_ADDRESS") != NULL)
297     {
298       if (config_file != NULL || user != TEST_USER_ME)
299         {
300           g_test_skip ("cannot use DBUS_TEST_DAEMON_ADDRESS for "
301               "unusally-configured dbus-daemon");
302           address = NULL;
303         }
304       else
305         {
306           address = g_strdup (g_getenv ("DBUS_TEST_DAEMON_ADDRESS"));
307         }
308     }
309   else
310     {
311       address = spawn_dbus_daemon (dbus_daemon, arg,
312           listen_address, user, runtime_dir, daemon_pid);
313     }
314
315   g_free (dbus_daemon);
316   g_free (arg);
317   return address;
318 }
319
320 DBusConnection *
321 test_connect_to_bus (TestMainContext *ctx,
322     const gchar *address)
323 {
324   DBusConnection *conn;
325   DBusError error = DBUS_ERROR_INIT;
326   dbus_bool_t ok;
327
328   conn = dbus_connection_open_private (address, &error);
329   test_assert_no_error (&error);
330   g_assert (conn != NULL);
331
332   ok = dbus_bus_register (conn, &error);
333   test_assert_no_error (&error);
334   g_assert (ok);
335   g_assert (dbus_bus_get_unique_name (conn) != NULL);
336
337   if (ctx != NULL)
338     test_connection_setup (ctx, conn);
339
340   return conn;
341 }
342
343 DBusConnection *
344 test_connect_to_bus_as_user (TestMainContext *ctx,
345     const char *address,
346     TestUser user)
347 {
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;
355
356   switch (user)
357     {
358       case TEST_USER_ME:
359         return test_connect_to_bus (ctx, address);
360
361       case TEST_USER_ROOT:
362         username = "root";
363         break;
364
365       case TEST_USER_MESSAGEBUS:
366         username = DBUS_USER;
367         break;
368
369       case TEST_USER_OTHER:
370         username = DBUS_TEST_USER;
371         break;
372
373       default:
374         g_return_val_if_reached (NULL);
375     }
376
377   if (getresuid (&ruid, &euid, &suid) != 0)
378     g_error ("getresuid: %s", g_strerror (errno));
379
380   if (ruid != 0 || euid != 0 || suid != 0)
381     {
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");
385       return NULL;
386     }
387
388   pwd = getpwnam (username);
389
390   if (pwd == NULL)
391     {
392       g_test_message ("getpwnam(\"%s\"): %s", username, g_strerror (errno));
393       g_test_skip ("not uid 0");
394       return NULL;
395     }
396
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));
402
403   conn = test_connect_to_bus (ctx, address);
404
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));
408
409   return conn;
410
411 #else
412
413   switch (user)
414     {
415       case TEST_USER_ME:
416         return test_connect_to_bus (ctx, address);
417
418       case TEST_USER_ROOT:
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");
423         return NULL;
424
425       default:
426         g_return_val_if_reached (NULL);
427     }
428
429 #endif
430 }
431
432 static void
433 pid_died (GPid pid,
434           gint status,
435           gpointer user_data)
436 {
437   gboolean *result = user_data;
438
439   g_assert (result != NULL);
440   g_assert (!*result);
441   *result = TRUE;
442 }
443
444 void
445 test_kill_pid (GPid pid)
446 {
447   gint died = FALSE;
448
449   g_child_watch_add (pid, pid_died, &died);
450
451 #ifdef DBUS_WIN
452   if (pid != NULL)
453     TerminateProcess (pid, 1);
454 #else
455   if (pid > 0)
456     kill (pid, SIGTERM);
457 #endif
458
459   while (!died)
460     g_main_context_iteration (NULL, TRUE);
461 }
462
463 static gboolean
464 time_out (gpointer data)
465 {
466   puts ("Bail out! Test timed out (GLib main loop timeout callback reached)");
467   fflush (stdout);
468   abort ();
469   return FALSE;
470 }
471
472 #ifdef G_OS_UNIX
473 static void wrap_abort (int signal) _DBUS_GNUC_NORETURN;
474
475 static void
476 wrap_abort (int signal)
477 {
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";
481
482   if (write (STDOUT_FILENO, message, sizeof (message) - 1) <
483       (ssize_t) sizeof (message) - 1)
484     {
485       /* ignore short write - what would we do about it? */
486     }
487
488   abort ();
489 }
490 #endif
491
492 static void
493 set_timeout (guint factor)
494 {
495   static guint timeout = 0;
496
497   /* Prevent tests from hanging forever. This is intended to be long enough
498    * that any reasonable regression test on any reasonable hardware would
499    * have finished. */
500 #define TIMEOUT 60
501
502   if (timeout != 0)
503     g_source_remove (timeout);
504
505   timeout = g_timeout_add_seconds (TIMEOUT * factor, time_out, NULL);
506 #ifdef G_OS_UNIX
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);
510
511   /* Get a log message and a core dump from the SIGALRM. */
512     {
513       struct sigaction act = { };
514
515       act.sa_handler = wrap_abort;
516
517       sigaction (SIGALRM, &act, NULL);
518     }
519 #endif
520 }
521
522 void
523 test_init (int *argcp, char ***argvp)
524 {
525   g_test_init (argcp, argvp, NULL);
526   g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
527   set_timeout (1);
528 }
529
530 static void
531 report_and_destroy (gpointer p)
532 {
533   GTimer *timer = p;
534
535   g_test_message ("Time since timeout reset %p: %.3f seconds",
536       timer, g_timer_elapsed (timer, NULL));
537   g_timer_destroy (timer);
538 }
539
540 void
541 test_timeout_reset (guint factor)
542 {
543   GTimer *timer = g_timer_new ();
544
545   g_test_message ("Resetting test timeout (reference: %p; factor: %u)",
546       timer, factor);
547   set_timeout (factor);
548
549   g_test_queue_destroy (report_and_destroy, timer);
550 }
551
552 void
553 test_progress (char symbol)
554 {
555   if (g_test_verbose () && isatty (1))
556     g_print ("%c", symbol);
557 }
558
559 /*
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.
563  */
564 void
565 test_remove_if_exists (const gchar *path)
566 {
567   while (g_remove (path) != 0)
568     {
569       int saved_errno = errno;
570
571       if (saved_errno == ENOENT)
572         return;
573
574 #ifdef G_OS_UNIX
575       if (saved_errno == EINTR)
576         continue;
577 #endif
578
579       g_error ("Unable to remove file \"%s\": %s", path,
580                g_strerror (saved_errno));
581     }
582 }
583
584 /*
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.
587  */
588 void
589 test_rmdir_must_exist (const gchar *path)
590 {
591   while (g_remove (path) != 0)
592     {
593       int saved_errno = errno;
594
595 #ifdef G_OS_UNIX
596       if (saved_errno == EINTR)
597         continue;
598 #endif
599
600       g_error ("Unable to remove directory \"%s\": %s", path,
601                g_strerror (saved_errno));
602     }
603 }
604
605 /*
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.
608  */
609 void
610 test_rmdir_if_exists (const gchar *path)
611 {
612   while (g_remove (path) != 0)
613     {
614       int saved_errno = errno;
615
616       if (saved_errno == ENOENT)
617         return;
618
619 #ifdef G_OS_UNIX
620       if (saved_errno == EINTR)
621         continue;
622 #endif
623
624       g_error ("Unable to remove directory \"%s\": %s", path,
625                g_strerror (saved_errno));
626     }
627 }
628
629 /*
630  * Create directory @path, with a retry loop if the system call is
631  * interrupted by an async signal.
632  */
633 void
634 test_mkdir (const gchar *path,
635             gint mode)
636 {
637   while (g_mkdir (path, mode) != 0)
638     {
639       int saved_errno = errno;
640
641 #ifdef G_OS_UNIX
642       if (saved_errno == EINTR)
643         continue;
644 #endif
645
646       g_error ("Unable to create directory \"%s\": %s", path,
647                g_strerror (saved_errno));
648     }
649 }
650
651 gboolean
652 test_check_tcp_works (void)
653 {
654 #ifdef DBUS_UNIX
655   /* In pathological container environments, we might not have a
656    * working 127.0.0.1 */
657   int res;
658   struct addrinfo *addrs = NULL;
659   struct addrinfo hints;
660   int saved_errno;
661
662   _DBUS_ZERO (hints);
663 #ifdef AI_ADDRCONFIG
664   hints.ai_flags |= AI_ADDRCONFIG;
665 #endif
666   hints.ai_flags = AI_ADDRCONFIG;
667   hints.ai_family = AF_INET;
668   hints.ai_socktype = SOCK_STREAM;
669   hints.ai_protocol = IPPROTO_TCP;
670
671   res = getaddrinfo ("127.0.0.1", "0", &hints, &addrs);
672   saved_errno = errno;
673
674   if (res != 0)
675     {
676       const gchar *system_message;
677       gchar *skip_message;
678
679 #ifdef EAI_SYSTEM
680       if (res == EAI_SYSTEM)
681         system_message = g_strerror (saved_errno);
682       else
683 #endif
684         system_message = gai_strerror (res);
685
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}): "
690                                       "%s",
691                                       system_message);
692       g_test_skip (skip_message);
693       free (skip_message);
694     }
695
696   if (addrs != NULL)
697     freeaddrinfo (addrs);
698
699   return (res == 0);
700 #else
701   /* Assume that on Windows, TCP always works */
702   return TRUE;
703 #endif
704 }