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