summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2017-02-14 15:10:20 +0000
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2017-02-21 13:24:02 +0000
commit3d5bc65333f3ab6437ddeeed1f4e30856aa32e6b (patch)
tree087ba1b47675258283cdc8af4aa9e92384076829 /test
parentff03660715bee6b97fa6bed179f23316cf28723f (diff)
downloaddbus-3d5bc65333f3ab6437ddeeed1f4e30856aa32e6b.tar.gz
sd-activation test: Exercise transient services
To do this, we have to use the <standard_session_servicedirs/>. A previous commit ensured that those don't provide any service files we don't expect. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=99825 Reviewed-by: Philip Withnall <withnall@endlessm.com> Signed-off-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
Diffstat (limited to 'test')
-rw-r--r--test/data/valid-config-files/systemd-activation.conf.in2
-rw-r--r--test/sd-activation.c245
-rw-r--r--test/test-utils-glib.c22
-rw-r--r--test/test-utils-glib.h1
4 files changed, 269 insertions, 1 deletions
diff --git a/test/data/valid-config-files/systemd-activation.conf.in b/test/data/valid-config-files/systemd-activation.conf.in
index a3a59f05..2c3d2c21 100644
--- a/test/data/valid-config-files/systemd-activation.conf.in
+++ b/test/data/valid-config-files/systemd-activation.conf.in
@@ -2,7 +2,9 @@
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<listen>@TEST_LISTEN@</listen>
+
<servicedir>@DBUS_TEST_DATA@/systemd-activation</servicedir>
+ <standard_session_servicedirs />
<policy context="default">
<allow send_destination="*"/>
diff --git a/test/sd-activation.c b/test/sd-activation.c
index 71a6da25..ff4a657f 100644
--- a/test/sd-activation.c
+++ b/test/sd-activation.c
@@ -65,12 +65,20 @@ typedef struct {
DBusMessage *activated_message;
dbus_bool_t activated_filter_added;
+ gchar *transient_service_file;
gchar *tmp_runtime_dir;
} Fixture;
+typedef enum
+{
+ FLAG_EARLY_TRANSIENT_SERVICE = (1 << 0),
+ FLAG_NONE = 0
+} Flags;
+
typedef struct
{
const gchar *bus_name;
+ Flags flags;
} Config;
/* this is a macro so it gets the right line number */
@@ -187,6 +195,10 @@ activated_filter (DBusConnection *connection,
g_assert (f->activated_message == NULL);
f->activated_message = dbus_message_ref (message);
+ /* Test code is expected to reply to method calls itself */
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+ return DBUS_HANDLER_RESULT_HANDLED;
+
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
@@ -212,9 +224,34 @@ caller_filter (DBusConnection *connection,
}
static void
+fixture_create_transient_service (Fixture *f,
+ const gchar *name)
+{
+ gchar *service;
+ gchar *content;
+ gboolean ok;
+
+ service = g_strdup_printf ("%s.service", name);
+ f->transient_service_file = g_build_filename (f->tmp_runtime_dir, "dbus-1",
+ "services", service, NULL);
+ g_free (service);
+
+ content = g_strdup_printf (
+ "[D-BUS Service]\n"
+ "Name=%s\n"
+ "Exec=/bin/false %s\n"
+ "SystemdService=dbus-%s.service\n", name, name, name);
+ ok = g_file_set_contents (f->transient_service_file, content, -1, &f->ge);
+ g_assert_no_error (f->ge);
+ g_assert (ok);
+ g_free (content);
+}
+
+static void
setup (Fixture *f,
- gconstpointer context G_GNUC_UNUSED)
+ gconstpointer context)
{
+ const Config *config = context;
#if defined(DBUS_TEST_APPARMOR_ACTIVATION) && defined(HAVE_APPARMOR_2_10)
aa_features *features;
#endif
@@ -225,6 +262,19 @@ setup (Fixture *f,
f->tmp_runtime_dir = g_dir_make_tmp ("dbus-daemon-test.XXXXXX", &f->ge);
g_assert_no_error (f->ge);
+ if (config != NULL && (config->flags & FLAG_EARLY_TRANSIENT_SERVICE) != 0)
+ {
+ gchar *dbus1 = g_build_filename (f->tmp_runtime_dir, "dbus-1", NULL);
+ gchar *services = g_build_filename (dbus1, "services", NULL);
+
+ /* We just created it so the directories shouldn't exist yet */
+ test_mkdir (dbus1, 0700);
+ test_mkdir (services, 0700);
+ fixture_create_transient_service (f, config->bus_name);
+ g_free (dbus1);
+ g_free (services);
+ }
+
#if defined(DBUS_TEST_APPARMOR_ACTIVATION) && !defined(HAVE_APPARMOR_2_10)
g_test_skip ("AppArmor support not compiled or AppArmor 2.10 unavailable");
@@ -782,6 +832,169 @@ test_deny_receive (Fixture *f,
g_assert (f->activated_message == NULL);
}
+/*
+ * Test that we can set up transient services.
+ *
+ * If (flags & FLAG_EARLY_TRANSIENT_SERVICE), we assert that a service that
+ * was deployed before starting systemd (in setup()) is available.
+ *
+ * Otherwise, we assert that a service that is deployed while dbus-daemon
+ * is already running becomes available after reloading the dbus-daemon
+ * configuration.
+ */
+static void
+test_transient_services (Fixture *f,
+ gconstpointer context)
+{
+ const Config *config = context;
+ DBusMessage *m = NULL;
+ DBusMessage *send_reply = NULL;
+ DBusMessage *reply = NULL;
+ DBusPendingCall *pc;
+
+ if (f->address == NULL)
+ return;
+
+ /* Connect the fake systemd to the bus. */
+ f->systemd = test_connect_to_bus (f->ctx, f->address);
+ if (!dbus_connection_add_filter (f->systemd, systemd_filter, f, NULL))
+ g_error ("OOM");
+ f->systemd_filter_added = TRUE;
+ f->systemd_name = dbus_bus_get_unique_name (f->systemd);
+ take_well_known_name (f, f->systemd, "org.freedesktop.systemd1");
+
+ if (config == NULL || (config->flags & FLAG_EARLY_TRANSIENT_SERVICE) == 0)
+ {
+ /* Try to activate a service that isn't there. */
+ m = dbus_message_new_method_call (config->bus_name,
+ "/foo", "com.example.bar", "Activate");
+
+ if (m == NULL ||
+ !dbus_connection_send_with_reply (f->caller, m, &pc,
+ DBUS_TIMEOUT_USE_DEFAULT) || pc == NULL)
+ g_error ("OOM");
+
+ dbus_message_unref (m);
+ m = NULL;
+
+ /* It fails. */
+
+ if (dbus_pending_call_get_completed (pc))
+ test_pending_call_store_reply (pc, &reply);
+ else if (!dbus_pending_call_set_notify (pc, test_pending_call_store_reply,
+ &m, NULL))
+ g_error ("OOM");
+
+ while (m == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ assert_error_reply (m, DBUS_SERVICE_DBUS, f->caller_name,
+ DBUS_ERROR_SERVICE_UNKNOWN);
+
+ dbus_message_unref (m);
+ m = NULL;
+
+ /* Now generate a transient D-Bus service file for it. The directory
+ * should have been created during dbus-daemon startup, so we don't have to
+ * recreate it. */
+ fixture_create_transient_service (f, config->bus_name);
+
+ /* To guarantee that the transient service has been picked up, we have
+ * to reload. */
+ m = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS, "ReloadConfig");
+
+ if (m == NULL ||
+ !dbus_connection_send_with_reply (f->caller, m, &pc,
+ DBUS_TIMEOUT_USE_DEFAULT) || pc == NULL)
+ g_error ("OOM");
+
+ dbus_message_unref (m);
+ m = NULL;
+
+ if (dbus_pending_call_get_completed (pc))
+ test_pending_call_store_reply (pc, &m);
+ else if (!dbus_pending_call_set_notify (pc, test_pending_call_store_reply,
+ &m, NULL))
+ g_error ("OOM");
+
+ while (m == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ assert_method_reply (m, DBUS_SERVICE_DBUS, f->caller_name, "");
+ dbus_message_unref (m);
+ m = NULL;
+ }
+
+ /* The service is present now. */
+ m = dbus_message_new_method_call (config->bus_name,
+ "/foo", "com.example.bar", "Activate");
+
+ if (m == NULL ||
+ !dbus_connection_send_with_reply (f->caller, m, &pc,
+ DBUS_TIMEOUT_USE_DEFAULT) || pc == NULL)
+ g_error ("OOM");
+
+ dbus_message_unref (m);
+ m = NULL;
+
+ if (dbus_pending_call_get_completed (pc))
+ test_pending_call_store_reply (pc, &reply);
+ else if (!dbus_pending_call_set_notify (pc, test_pending_call_store_reply,
+ &reply, NULL))
+ g_error ("OOM");
+
+ /* The mock systemd is told to start the service. */
+ while (f->systemd_message == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = f->systemd_message;
+ f->systemd_message = NULL;
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ "org.freedesktop.systemd1.Activator", "ActivationRequest", "s",
+ "org.freedesktop.systemd1");
+ dbus_message_unref (m);
+ m = NULL;
+
+ /* The activatable service connects and gets its name. */
+ f->activated = test_connect_to_bus (f->ctx, f->address);
+ if (!dbus_connection_add_filter (f->activated, activated_filter,
+ f, NULL))
+ g_error ("OOM");
+ f->activated_filter_added = TRUE;
+ f->activated_name = dbus_bus_get_unique_name (f->activated);
+ take_well_known_name (f, f->activated, config->bus_name);
+
+ /* The message is delivered to the activatable service. */
+ while (f->activated_message == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = f->activated_message;
+ f->activated_message = NULL;
+ assert_method_call (m, f->caller_name, config->bus_name, "/foo",
+ "com.example.bar", "Activate", "");
+
+ /* The activatable service sends back a reply. */
+ send_reply = dbus_message_new_method_return (m);
+
+ if (send_reply == NULL ||
+ !dbus_connection_send (f->activated, send_reply, NULL))
+ g_error ("OOM");
+
+ dbus_message_unref (send_reply);
+ send_reply = NULL;
+ dbus_message_unref (m);
+ m = NULL;
+
+ /* The caller receives the reply. */
+ while (reply == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ assert_method_reply (reply, f->activated_name, f->caller_name, "");
+ dbus_message_unref (reply);
+ reply = NULL;
+}
+
static void
teardown (Fixture *f,
gconstpointer context G_GNUC_UNUSED)
@@ -830,11 +1043,24 @@ teardown (Fixture *f,
g_free (f->address);
+ if (f->transient_service_file != NULL)
+ {
+ test_remove_if_exists (f->transient_service_file);
+ g_free (f->transient_service_file);
+ }
+
if (f->tmp_runtime_dir != NULL)
{
+ gchar *dbus1 = g_build_filename (f->tmp_runtime_dir, "dbus-1", NULL);
+ gchar *services = g_build_filename (dbus1, "services", NULL);
+
+ test_rmdir_if_exists (services);
+ test_rmdir_if_exists (dbus1);
test_rmdir_if_exists (f->tmp_runtime_dir);
g_free (f->tmp_runtime_dir);
+ g_free (dbus1);
+ g_free (services);
}
}
@@ -856,6 +1082,18 @@ static const Config deny_receive_tests[] =
{ "com.example.ReceiveDenied" }
};
+static const Config transient_service_later =
+{
+ "com.example.TransientActivatable1",
+ FLAG_NONE
+};
+
+static const Config transient_service_in_advance =
+{
+ "com.example.TransientActivatable1",
+ FLAG_EARLY_TRANSIENT_SERVICE
+};
+
int
main (int argc,
char **argv)
@@ -889,5 +1127,10 @@ main (int argc,
g_free (name);
}
+ g_test_add ("/sd-activation/transient-services/later", Fixture,
+ &transient_service_later, setup, test_transient_services, teardown);
+ g_test_add ("/sd-activation/transient-services/in-advance", Fixture,
+ &transient_service_in_advance, setup, test_transient_services, teardown);
+
return g_test_run ();
}
diff --git a/test/test-utils-glib.c b/test/test-utils-glib.c
index e60a40d3..e13d20d3 100644
--- a/test/test-utils-glib.c
+++ b/test/test-utils-glib.c
@@ -570,3 +570,25 @@ test_rmdir_if_exists (const gchar *path)
g_strerror (saved_errno));
}
}
+
+/*
+ * Create directory @path, with a retry loop if the system call is
+ * interrupted by an async signal.
+ */
+void
+test_mkdir (const gchar *path,
+ gint mode)
+{
+ while (g_mkdir (path, mode) != 0)
+ {
+ int saved_errno = errno;
+
+#ifdef G_OS_UNIX
+ if (saved_errno == EINTR)
+ continue;
+#endif
+
+ g_error ("Unable to create directory \"%s\": %s", path,
+ g_strerror (saved_errno));
+ }
+}
diff --git a/test/test-utils-glib.h b/test/test-utils-glib.h
index d29c58da..d4b2e614 100644
--- a/test/test-utils-glib.h
+++ b/test/test-utils-glib.h
@@ -95,5 +95,6 @@ static inline void my_test_skip (const gchar *s)
void test_remove_if_exists (const gchar *path);
void test_rmdir_must_exist (const gchar *path);
void test_rmdir_if_exists (const gchar *path);
+void test_mkdir (const gchar *path, gint mode);
#endif