/* Integration tests for restricted sockets for containers * * Copyright © 2017 Collabora Ltd. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #include #if defined(DBUS_ENABLE_CONTAINERS) && defined(HAVE_GIO_UNIX) #define HAVE_CONTAINERS_TEST #include #include #include "dbus/dbus-sysdeps-unix.h" #endif #include "test-utils-glib.h" typedef struct { gboolean skip; gchar *bus_address; GPid daemon_pid; GError *error; GDBusProxy *proxy; gchar *instance_path; gchar *socket_path; gchar *socket_dbus_address; GDBusConnection *unconfined_conn; GDBusConnection *confined_conn; } Fixture; typedef struct { const gchar *config_file; enum { STOP_SERVER_EXPLICITLY, STOP_SERVER_DISCONNECT_FIRST, STOP_SERVER_NEVER_CONNECTED, STOP_SERVER_FORCE, STOP_SERVER_WITH_MANAGER } stop_server; } Config; static const Config default_config = { NULL, 0 /* not used, the stop-server test always uses non-default config */ }; #ifdef DBUS_ENABLE_CONTAINERS /* A GDBusNameVanishedCallback that sets a boolean flag. */ static void name_gone_set_boolean_cb (GDBusConnection *conn, const gchar *name, gpointer user_data) { gboolean *gone_p = user_data; g_assert_nonnull (gone_p); g_assert_false (*gone_p); *gone_p = TRUE; } #endif static void setup (Fixture *f, gconstpointer context) { const Config *config = context; if (config == NULL) config = &default_config; f->bus_address = test_get_dbus_daemon (config->config_file, TEST_USER_ME, NULL, &f->daemon_pid); if (f->bus_address == NULL) { f->skip = TRUE; return; } f->unconfined_conn = g_dbus_connection_new_for_address_sync (f->bus_address, (G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION | G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT), NULL, NULL, &f->error); g_assert_no_error (f->error); } /* * Assert that Get(SupportedArguments) contains what we expect it to. */ static void test_get_supported_arguments (Fixture *f, gconstpointer context) { GVariant *v; #ifdef DBUS_ENABLE_CONTAINERS const gchar **args; gsize len; #endif if (f->skip) return; f->proxy = g_dbus_proxy_new_sync (f->unconfined_conn, G_DBUS_PROXY_FLAGS_NONE, NULL, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_CONTAINERS1, NULL, &f->error); /* This one is DBUS_ENABLE_CONTAINERS rather than HAVE_CONTAINERS_TEST * because we can still test whether the interface appears or not, even * if we were not able to detect gio-unix-2.0 */ #ifdef DBUS_ENABLE_CONTAINERS g_assert_no_error (f->error); v = g_dbus_proxy_get_cached_property (f->proxy, "SupportedArguments"); g_assert_cmpstr (g_variant_get_type_string (v), ==, "as"); args = g_variant_get_strv (v, &len); /* No arguments are defined yet */ g_assert_cmpuint (len, ==, 0); g_free (args); g_variant_unref (v); #else /* !DBUS_ENABLE_CONTAINERS */ g_assert_no_error (f->error); v = g_dbus_proxy_get_cached_property (f->proxy, "SupportedArguments"); g_assert_null (v); #endif /* !DBUS_ENABLE_CONTAINERS */ } #ifdef HAVE_CONTAINERS_TEST /* * Try to make an AddServer call that usually succeeds, but may fail and * be skipped if we are running as root and this version of dbus has not * been fully installed. Return TRUE if we can continue. * * parameters is sunk if it is a floating reference. */ static gboolean add_container_server (Fixture *f, GVariant *parameters) { GVariant *tuple; GStatBuf stat_buf; f->proxy = g_dbus_proxy_new_sync (f->unconfined_conn, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_CONTAINERS1, NULL, &f->error); g_assert_no_error (f->error); g_test_message ("Calling AddServer..."); tuple = g_dbus_proxy_call_sync (f->proxy, "AddServer", parameters, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &f->error); /* For root, the sockets go in /run/dbus/containers, which we rely on * system infrastructure to create; so it's OK for AddServer to fail * when uninstalled, although not OK if it fails as an installed-test. */ if (f->error != NULL && _dbus_getuid () == 0 && _dbus_getenv ("DBUS_TEST_UNINSTALLED") != NULL) { g_test_message ("AddServer: %s", f->error->message); g_assert_error (f->error, G_DBUS_ERROR, G_DBUS_ERROR_FILE_NOT_FOUND); g_test_skip ("AddServer failed, probably because this dbus " "version is not fully installed"); return FALSE; } g_assert_no_error (f->error); g_assert_nonnull (tuple); g_assert_cmpstr (g_variant_get_type_string (tuple), ==, "(oays)"); g_variant_get (tuple, "(o^ays)", &f->instance_path, &f->socket_path, &f->socket_dbus_address); g_assert_true (g_str_has_prefix (f->socket_dbus_address, "unix:")); g_assert_null (strchr (f->socket_dbus_address, ';')); g_assert_null (strchr (f->socket_dbus_address + strlen ("unix:"), ':')); g_clear_pointer (&tuple, g_variant_unref); g_assert_nonnull (f->instance_path); g_assert_true (g_variant_is_object_path (f->instance_path)); g_assert_nonnull (f->socket_path); g_assert_true (g_path_is_absolute (f->socket_path)); g_assert_nonnull (f->socket_dbus_address); g_assert_cmpstr (g_stat (f->socket_path, &stat_buf) == 0 ? NULL : g_strerror (errno), ==, NULL); g_assert_cmpuint ((stat_buf.st_mode & S_IFMT), ==, S_IFSOCK); return TRUE; } #endif /* * Assert that a simple AddServer() call succeeds and has the behaviour * we expect (we can connect a confined connection to it, the confined * connection can talk to the dbus-daemon and to an unconfined connection, * and the socket gets cleaned up when the dbus-daemon terminates). */ static void test_basic (Fixture *f, gconstpointer context) { #ifdef HAVE_CONTAINERS_TEST GVariant *parameters; const gchar *manager_unique_name; const gchar *name_owner; GStatBuf stat_buf; GVariant *tuple; if (f->skip) return; parameters = g_variant_new ("(ssa{sv}a{sv})", "com.example.NotFlatpak", "sample-app", NULL, /* no metadata */ NULL); /* no named arguments */ if (!add_container_server (f, g_steal_pointer (¶meters))) return; g_test_message ("Connecting to %s...", f->socket_dbus_address); f->confined_conn = g_dbus_connection_new_for_address_sync ( f->socket_dbus_address, (G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION | G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT), NULL, NULL, &f->error); g_assert_no_error (f->error); g_test_message ("Making a method call from confined app..."); tuple = g_dbus_connection_call_sync (f->confined_conn, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner", g_variant_new ("(s)", DBUS_SERVICE_DBUS), G_VARIANT_TYPE ("(s)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &f->error); g_assert_no_error (f->error); g_assert_nonnull (tuple); g_assert_cmpstr (g_variant_get_type_string (tuple), ==, "(s)"); g_variant_get (tuple, "(&s)", &name_owner); g_assert_cmpstr (name_owner, ==, DBUS_SERVICE_DBUS); g_clear_pointer (&tuple, g_variant_unref); g_test_message ("Making a method call from confined app to unconfined..."); manager_unique_name = g_dbus_connection_get_unique_name (f->unconfined_conn); tuple = g_dbus_connection_call_sync (f->confined_conn, manager_unique_name, "/", DBUS_INTERFACE_PEER, "Ping", NULL, G_VARIANT_TYPE_UNIT, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &f->error); g_assert_no_error (f->error); g_assert_nonnull (tuple); g_assert_cmpstr (g_variant_get_type_string (tuple), ==, "()"); g_clear_pointer (&tuple, g_variant_unref); /* Check that the socket is cleaned up when the dbus-daemon is terminated */ test_kill_pid (f->daemon_pid); g_spawn_close_pid (f->daemon_pid); f->daemon_pid = 0; while (g_stat (f->socket_path, &stat_buf) == 0) g_usleep (G_USEC_PER_SEC / 20); g_assert_cmpint (errno, ==, ENOENT); #else /* !HAVE_CONTAINERS_TEST */ g_test_skip ("Containers or gio-unix-2.0 not supported"); #endif /* !HAVE_CONTAINERS_TEST */ } /* * If we are running as root, assert that when one uid (root) creates a * container server, another uid (TEST_USER_OTHER) cannot connect to it */ static void test_wrong_uid (Fixture *f, gconstpointer context) { #ifdef HAVE_CONTAINERS_TEST GVariant *parameters; if (f->skip) return; parameters = g_variant_new ("(ssa{sv}a{sv})", "com.example.NotFlatpak", "sample-app", NULL, /* no metadata */ NULL); /* no named arguments */ if (!add_container_server (f, g_steal_pointer (¶meters))) return; g_test_message ("Connecting to %s...", f->socket_dbus_address); f->confined_conn = test_try_connect_gdbus_as_user (f->socket_dbus_address, TEST_USER_OTHER, &f->error); /* That might be skipped if we can't become TEST_USER_OTHER */ if (f->error != NULL && g_error_matches (f->error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) { g_test_skip (f->error->message); return; } /* The connection was unceremoniously closed */ g_assert_error (f->error, G_IO_ERROR, G_IO_ERROR_CLOSED); #else /* !HAVE_CONTAINERS_TEST */ g_test_skip ("Containers or gio-unix-2.0 not supported"); #endif /* !HAVE_CONTAINERS_TEST */ } /* * With config->stop_server == STOP_SERVER_WITH_MANAGER: * Assert that without special parameters, when the container manager * disappears from the bus, so does the confined server. * * With config->stop_server == STOP_SERVER_EXPLICITLY or * config->stop_server == STOP_SERVER_DISCONNECT_FIRST: * Test StopListening(), which just closes the listening socket. * * With config->stop_server == STOP_SERVER_FORCE: * Test StopInstance(), which closes the listening socket and * disconnects all its clients. */ static void test_stop_server (Fixture *f, gconstpointer context) { #ifdef HAVE_CONTAINERS_TEST const Config *config = context; GDBusConnection *attacker; GDBusConnection *second_confined_conn; GDBusProxy *attacker_proxy; GSocket *client_socket; GSocketAddress *socket_address; GVariant *tuple; GVariant *parameters; const gchar *confined_unique_name; const gchar *manager_unique_name; const gchar *name_owner; gboolean gone = FALSE; guint name_watch; guint i; g_assert_nonnull (config); if (f->skip) return; parameters = g_variant_new ("(ssa{sv}a{sv})", "com.example.NotFlatpak", "sample-app", NULL, /* no metadata */ NULL); /* no named arguments */ if (!add_container_server (f, g_steal_pointer (¶meters))) return; socket_address = g_unix_socket_address_new (f->socket_path); if (config->stop_server != STOP_SERVER_NEVER_CONNECTED) { g_test_message ("Connecting to %s...", f->socket_dbus_address); f->confined_conn = g_dbus_connection_new_for_address_sync ( f->socket_dbus_address, (G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION | G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT), NULL, NULL, &f->error); g_assert_no_error (f->error); if (config->stop_server == STOP_SERVER_DISCONNECT_FIRST) { g_test_message ("Disconnecting confined connection..."); gone = FALSE; confined_unique_name = g_dbus_connection_get_unique_name ( f->confined_conn); name_watch = g_bus_watch_name_on_connection (f->unconfined_conn, confined_unique_name, G_BUS_NAME_WATCHER_FLAGS_NONE, NULL, name_gone_set_boolean_cb, &gone, NULL); g_dbus_connection_close_sync (f->confined_conn, NULL, &f->error); g_assert_no_error (f->error); g_test_message ("Waiting for confined app bus name to disappear..."); while (!gone) g_main_context_iteration (NULL, TRUE); g_bus_unwatch_name (name_watch); } } /* If we are able to switch uid (i.e. we are root), check that a local * attacker with a different uid cannot close our container instances. */ attacker = test_try_connect_gdbus_as_user (f->bus_address, TEST_USER_OTHER, &f->error); if (attacker != NULL) { attacker_proxy = g_dbus_proxy_new_sync (attacker, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_CONTAINERS1, NULL, &f->error); g_assert_no_error (f->error); tuple = g_dbus_proxy_call_sync (attacker_proxy, "StopListening", g_variant_new ("(o)", f->instance_path), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &f->error); g_assert_error (f->error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED); g_assert_null (tuple); g_clear_error (&f->error); tuple = g_dbus_proxy_call_sync (attacker_proxy, "StopInstance", g_variant_new ("(o)", f->instance_path), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &f->error); g_assert_error (f->error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED); g_assert_null (tuple); g_clear_error (&f->error); g_clear_object (&attacker_proxy); g_dbus_connection_close_sync (attacker, NULL, &f->error); g_assert_no_error (f->error); g_clear_object (&attacker); } else { /* If we aren't running as root, it's OK to not be able to connect again * as some other user (usually 'nobody'). We don't g_test_skip() here * because this is just extra coverage */ g_assert_error (f->error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); g_clear_error (&f->error); } switch (config->stop_server) { case STOP_SERVER_WITH_MANAGER: /* Close the unconfined connection (the container manager) and wait * for it to go away */ g_test_message ("Closing container manager..."); manager_unique_name = g_dbus_connection_get_unique_name (f->unconfined_conn); name_watch = g_bus_watch_name_on_connection (f->confined_conn, manager_unique_name, G_BUS_NAME_WATCHER_FLAGS_NONE, NULL, name_gone_set_boolean_cb, &gone, NULL); g_dbus_connection_close_sync (f->unconfined_conn, NULL, &f->error); g_assert_no_error (f->error); g_test_message ("Waiting for container manager bus name to disappear..."); while (!gone) g_main_context_iteration (NULL, TRUE); g_bus_unwatch_name (name_watch); break; case STOP_SERVER_EXPLICITLY: case STOP_SERVER_DISCONNECT_FIRST: case STOP_SERVER_NEVER_CONNECTED: g_test_message ("Stopping server (but not confined connection)..."); tuple = g_dbus_proxy_call_sync (f->proxy, "StopListening", g_variant_new ("(o)", f->instance_path), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &f->error); g_assert_no_error (f->error); g_variant_unref (tuple); break; case STOP_SERVER_FORCE: g_test_message ("Stopping server and all confined connections..."); tuple = g_dbus_proxy_call_sync (f->proxy, "StopInstance", g_variant_new ("(o)", f->instance_path), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &f->error); g_assert_no_error (f->error); g_variant_unref (tuple); break; default: g_assert_not_reached (); } /* Now if we try to connect to the server again, it will fail (eventually - * closing the socket is not synchronous with respect to the name owner * change, so try a few times) */ for (i = 0; i < 50; i++) { g_test_message ("Trying to connect to %s again...", f->socket_path); client_socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &f->error); g_assert_no_error (f->error); if (!g_socket_connect (client_socket, socket_address, NULL, &f->error)) { g_assert_cmpstr (g_quark_to_string (f->error->domain), ==, g_quark_to_string (G_IO_ERROR)); if (f->error->code != G_IO_ERROR_CONNECTION_REFUSED && f->error->code != G_IO_ERROR_NOT_FOUND) g_error ("Unexpected error code %d", f->error->code); g_clear_error (&f->error); g_clear_object (&client_socket); break; } g_clear_object (&client_socket); g_usleep (G_USEC_PER_SEC / 10); } /* The same thing happens for a D-Bus connection */ g_test_message ("Trying to connect to %s again...", f->socket_dbus_address); second_confined_conn = g_dbus_connection_new_for_address_sync ( f->socket_dbus_address, (G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION | G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT), NULL, NULL, &f->error); g_assert_cmpstr (g_quark_to_string (f->error->domain), ==, g_quark_to_string (G_IO_ERROR)); if (f->error->code != G_IO_ERROR_CONNECTION_REFUSED && f->error->code != G_IO_ERROR_NOT_FOUND) g_error ("Unexpected error code %d", f->error->code); g_clear_error (&f->error); g_assert_null (second_confined_conn); /* The socket has been deleted */ g_assert_false (g_file_test (f->socket_path, G_FILE_TEST_EXISTS)); switch (config->stop_server) { case STOP_SERVER_FORCE: g_test_message ("Checking that the confined app gets disconnected..."); while (!g_dbus_connection_is_closed (f->confined_conn)) g_main_context_iteration (NULL, TRUE); break; case STOP_SERVER_DISCONNECT_FIRST: case STOP_SERVER_NEVER_CONNECTED: /* Nothing to be done here, no confined app is connected */ break; case STOP_SERVER_EXPLICITLY: case STOP_SERVER_WITH_MANAGER: g_test_message ("Checking that the confined app still works..."); tuple = g_dbus_connection_call_sync (f->confined_conn, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner", g_variant_new ("(s)", DBUS_SERVICE_DBUS), G_VARIANT_TYPE ("(s)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &f->error); g_assert_no_error (f->error); g_assert_nonnull (tuple); g_assert_cmpstr (g_variant_get_type_string (tuple), ==, "(s)"); g_variant_get (tuple, "(&s)", &name_owner); g_assert_cmpstr (name_owner, ==, DBUS_SERVICE_DBUS); g_clear_pointer (&tuple, g_variant_unref); break; default: g_assert_not_reached (); } #else /* !HAVE_CONTAINERS_TEST */ g_test_skip ("Containers or gio-unix-2.0 not supported"); #endif /* !HAVE_CONTAINERS_TEST */ } /* * Assert that named arguments are validated: passing an unsupported * named argument causes an error. */ static void test_unsupported_parameter (Fixture *f, gconstpointer context) { #ifdef HAVE_CONTAINERS_TEST GVariant *tuple; GVariant *parameters; GVariantDict named_argument_builder; if (f->skip) return; f->proxy = g_dbus_proxy_new_sync (f->unconfined_conn, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_CONTAINERS1, NULL, &f->error); g_assert_no_error (f->error); g_variant_dict_init (&named_argument_builder, NULL); g_variant_dict_insert (&named_argument_builder, "ThisArgumentIsntImplemented", "b", FALSE); parameters = g_variant_new ("(ssa{sv}@a{sv})", "com.example.NotFlatpak", "sample-app", NULL, /* no metadata */ g_variant_dict_end (&named_argument_builder)); tuple = g_dbus_proxy_call_sync (f->proxy, "AddServer", g_steal_pointer (¶meters), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &f->error); g_assert_error (f->error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); g_assert_null (tuple); g_clear_error (&f->error); #else /* !HAVE_CONTAINERS_TEST */ g_test_skip ("Containers or gio-unix-2.0 not supported"); #endif /* !HAVE_CONTAINERS_TEST */ } /* * Assert that container types are validated: a container type (container * technology) that is not a syntactically valid D-Bus interface name * causes an error. */ static void test_invalid_type_name (Fixture *f, gconstpointer context) { #ifdef HAVE_CONTAINERS_TEST GVariant *tuple; GVariant *parameters; if (f->skip) return; f->proxy = g_dbus_proxy_new_sync (f->unconfined_conn, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_CONTAINERS1, NULL, &f->error); g_assert_no_error (f->error); parameters = g_variant_new ("(ssa{sv}a{sv})", "this is not a valid container type name", "sample-app", NULL, /* no metadata */ NULL); /* no named arguments */ tuple = g_dbus_proxy_call_sync (f->proxy, "AddServer", g_steal_pointer (¶meters), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &f->error); g_assert_error (f->error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); g_assert_null (tuple); g_clear_error (&f->error); #else /* !HAVE_CONTAINERS_TEST */ g_test_skip ("Containers or gio-unix-2.0 not supported"); #endif /* !HAVE_CONTAINERS_TEST */ } static void teardown (Fixture *f, gconstpointer context G_GNUC_UNUSED) { g_clear_object (&f->proxy); if (f->unconfined_conn != NULL) { GError *error = NULL; g_dbus_connection_close_sync (f->unconfined_conn, NULL, &error); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED)) g_clear_error (&error); else g_assert_no_error (error); } g_clear_object (&f->unconfined_conn); if (f->confined_conn != NULL) { GError *error = NULL; g_dbus_connection_close_sync (f->confined_conn, NULL, &error); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED)) g_clear_error (&error); else g_assert_no_error (error); } g_clear_object (&f->confined_conn); if (f->daemon_pid != 0) { test_kill_pid (f->daemon_pid); g_spawn_close_pid (f->daemon_pid); f->daemon_pid = 0; } g_free (f->instance_path); g_free (f->socket_path); g_free (f->socket_dbus_address); g_free (f->bus_address); g_clear_error (&f->error); } static const Config stop_server_explicitly = { "valid-config-files/multi-user.conf", STOP_SERVER_EXPLICITLY }; static const Config stop_server_disconnect_first = { "valid-config-files/multi-user.conf", STOP_SERVER_DISCONNECT_FIRST }; static const Config stop_server_never_connected = { "valid-config-files/multi-user.conf", STOP_SERVER_NEVER_CONNECTED }; static const Config stop_server_force = { "valid-config-files/multi-user.conf", STOP_SERVER_FORCE }; static const Config stop_server_with_manager = { "valid-config-files/multi-user.conf", STOP_SERVER_WITH_MANAGER }; int main (int argc, char **argv) { GError *error = NULL; gchar *runtime_dir; gchar *runtime_dbus_dir; gchar *runtime_containers_dir; gchar *runtime_services_dir; int ret; runtime_dir = g_dir_make_tmp ("dbus-test-containers.XXXXXX", &error); if (runtime_dir == NULL) { g_print ("Bail out! %s\n", error->message); g_clear_error (&error); return 1; } g_setenv ("XDG_RUNTIME_DIR", runtime_dir, TRUE); runtime_dbus_dir = g_build_filename (runtime_dir, "dbus-1", NULL); runtime_containers_dir = g_build_filename (runtime_dir, "dbus-1", "containers", NULL); runtime_services_dir = g_build_filename (runtime_dir, "dbus-1", "services", NULL); test_init (&argc, &argv); g_test_add ("/containers/get-supported-arguments", Fixture, NULL, setup, test_get_supported_arguments, teardown); g_test_add ("/containers/basic", Fixture, NULL, setup, test_basic, teardown); g_test_add ("/containers/wrong-uid", Fixture, NULL, setup, test_wrong_uid, teardown); g_test_add ("/containers/stop-server/explicitly", Fixture, &stop_server_explicitly, setup, test_stop_server, teardown); g_test_add ("/containers/stop-server/disconnect-first", Fixture, &stop_server_disconnect_first, setup, test_stop_server, teardown); g_test_add ("/containers/stop-server/never-connected", Fixture, &stop_server_never_connected, setup, test_stop_server, teardown); g_test_add ("/containers/stop-server/force", Fixture, &stop_server_force, setup, test_stop_server, teardown); g_test_add ("/containers/stop-server/with-manager", Fixture, &stop_server_with_manager, setup, test_stop_server, teardown); g_test_add ("/containers/unsupported-parameter", Fixture, NULL, setup, test_unsupported_parameter, teardown); g_test_add ("/containers/invalid-type-name", Fixture, NULL, setup, test_invalid_type_name, teardown); ret = g_test_run (); test_rmdir_if_exists (runtime_containers_dir); test_rmdir_if_exists (runtime_services_dir); test_rmdir_if_exists (runtime_dbus_dir); test_rmdir_must_exist (runtime_dir); g_free (runtime_containers_dir); g_free (runtime_services_dir); g_free (runtime_dbus_dir); g_free (runtime_dir); return ret; }