summaryrefslogtreecommitdiff
path: root/bus/containers.c
diff options
context:
space:
mode:
Diffstat (limited to 'bus/containers.c')
-rw-r--r--bus/containers.c294
1 files changed, 290 insertions, 4 deletions
diff --git a/bus/containers.c b/bus/containers.c
index 1c922952..5ee35684 100644
--- a/bus/containers.c
+++ b/bus/containers.c
@@ -51,6 +51,7 @@ typedef struct
DBusVariant *metadata;
BusContext *context;
BusContainers *containers;
+ DBusServer *server;
} BusContainerInstance;
/*
@@ -63,6 +64,7 @@ struct BusContainers
/* path borrowed from BusContainerInstance => unowned BusContainerInstance
* The BusContainerInstance removes itself from here on destruction. */
DBusHashTable *instances_by_path;
+ DBusString address_template;
dbus_uint64_t next_container_id;
};
@@ -72,14 +74,53 @@ bus_containers_new (void)
/* We allocate the hash table lazily, expecting that the common case will
* be a connection where this feature is never used */
BusContainers *self = dbus_new0 (BusContainers, 1);
+ DBusString invalid = _DBUS_STRING_INIT_INVALID;
if (self == NULL)
- return NULL;
+ goto oom;
self->refcount = 1;
self->instances_by_path = NULL;
self->next_container_id = DBUS_UINT64_CONSTANT (0);
+ self->address_template = invalid;
+
+ /* Make it mutable */
+ if (!_dbus_string_init (&self->address_template))
+ goto oom;
+
+ if (_dbus_getuid () == 0)
+ {
+ DBusString dir;
+
+ /* System bus (we haven't dropped privileges at this point), or
+ * root's session bus. Use random socket paths resembling
+ * /run/dbus/containers/dbus-abcdef, which is next to /run/dbus/pid
+ * (if not using the Red Hat init scripts, which use a different
+ * pid file for historical reasons).
+ *
+ * We rely on the tmpfiles.d snippet or an OS-specific init script to
+ * have created this directory with the appropriate owner; if it hasn't,
+ * creating container sockets will just fail. */
+ _dbus_string_init_const (&dir, DBUS_RUNSTATEDIR "/dbus/containers");
+
+ /* We specifically use paths, because an abstract socket that you can't
+ * bind-mount is not particularly useful. */
+ if (!_dbus_string_append (&self->address_template, "unix:dir=") ||
+ !_dbus_address_append_escaped (&self->address_template, &dir))
+ goto oom;
+ }
+ else
+ {
+ /* Otherwise defer creating the directory for sockets until we need it,
+ * so that we can have better error behaviour */
+ }
+
return self;
+
+oom:
+ bus_clear_containers (&self);
+
+ return NULL;
}
BusContainers *
@@ -101,10 +142,21 @@ bus_containers_unref (BusContainers *self)
if (--self->refcount == 0)
{
_dbus_clear_hash_table (&self->instances_by_path);
+ _dbus_string_free (&self->address_template);
dbus_free (self);
}
}
+static BusContainerInstance *
+bus_container_instance_ref (BusContainerInstance *self)
+{
+ _dbus_assert (self->refcount > 0);
+ _dbus_assert (self->refcount < _DBUS_INT_MAX);
+
+ self->refcount++;
+ return self;
+}
+
static void
bus_container_instance_unref (BusContainerInstance *self)
{
@@ -112,6 +164,11 @@ bus_container_instance_unref (BusContainerInstance *self)
if (--self->refcount == 0)
{
+ /* As long as the server is listening, the BusContainerInstance can't
+ * be freed, because the DBusServer holds a reference to the
+ * BusContainerInstance */
+ _dbus_assert (self->server == NULL);
+
/* It's OK to do this even if we were never added to instances_by_path,
* because the paths are globally unique. */
if (self->path != NULL && self->containers->instances_by_path != NULL)
@@ -135,6 +192,22 @@ bus_clear_container_instance (BusContainerInstance **instance_p)
bus_container_instance_unref);
}
+static void
+bus_container_instance_stop_listening (BusContainerInstance *self)
+{
+ /* In case the DBusServer holds the last reference to self */
+ bus_container_instance_ref (self);
+
+ if (self->server != NULL)
+ {
+ dbus_server_set_new_connection_function (self->server, NULL, NULL, NULL);
+ dbus_server_disconnect (self->server);
+ dbus_clear_server (&self->server);
+ }
+
+ bus_container_instance_unref (self);
+}
+
static BusContainerInstance *
bus_container_instance_new (BusContext *context,
BusContainers *containers,
@@ -167,6 +240,7 @@ bus_container_instance_new (BusContext *context,
self->metadata = NULL;
self->context = bus_context_ref (context);
self->containers = bus_containers_ref (containers);
+ self->server = NULL;
if (containers->next_container_id >=
DBUS_UINT64_CONSTANT (0xFFFFFFFFFFFFFFFF))
@@ -202,6 +276,129 @@ fail:
return NULL;
}
+/* We only accept EXTERNAL authentication, because Unix platforms that are
+ * sufficiently capable to have app-containers ought to have it. */
+static const char * const auth_mechanisms[] =
+{
+ "EXTERNAL",
+ NULL
+};
+
+static void
+new_connection_cb (DBusServer *server,
+ DBusConnection *new_connection,
+ void *data)
+{
+ /* TODO: handle new connection */
+}
+
+static const char *
+bus_containers_ensure_address_template (BusContainers *self,
+ DBusError *error)
+{
+ DBusString dir;
+ const char *ret = NULL;
+ const char *runtime_dir;
+
+ /* Early-return if we already did this */
+ if (_dbus_string_get_length (&self->address_template) > 0)
+ return _dbus_string_get_const_data (&self->address_template);
+
+ if (!_dbus_string_init (&dir))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ runtime_dir = _dbus_getenv ("XDG_RUNTIME_DIR");
+
+ if (runtime_dir != NULL)
+ {
+ /* We listen on a random socket path resembling
+ * /run/user/1000/dbus-1/containers/dbus-abcdef, chosen to share
+ * the dbus-1 directory with the dbus-1/services used for transient
+ * session services. */
+ if (!_dbus_string_append (&dir, runtime_dir) ||
+ !_dbus_string_append (&dir, "/dbus-1"))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ if (!_dbus_ensure_directory (&dir, error))
+ goto out;
+
+ if (!_dbus_string_append (&dir, "/containers"))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ if (!_dbus_ensure_directory (&dir, error))
+ goto out;
+ }
+ else
+ {
+ /* No XDG_RUNTIME_DIR, so don't do anything special or clever: just
+ * use a random socket like /tmp/dbus-abcdef. */
+ const char *tmpdir;
+
+ tmpdir = _dbus_get_tmpdir ();
+ _dbus_string_init_const (&dir, tmpdir);
+ }
+
+ /* We specifically use paths, even on Linux (unix:dir= not unix:tmpdir=),
+ * because an abstract socket that you can't bind-mount is not useful
+ * when you want something you can bind-mount into a container. */
+ if (!_dbus_string_append (&self->address_template, "unix:dir=") ||
+ !_dbus_address_append_escaped (&self->address_template, &dir))
+ {
+ _dbus_string_set_length (&self->address_template, 0);
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ ret = _dbus_string_get_const_data (&self->address_template);
+
+out:
+ _dbus_string_free (&dir);
+ return ret;
+}
+
+static dbus_bool_t
+bus_container_instance_listen (BusContainerInstance *self,
+ DBusError *error)
+{
+ BusContainers *containers = bus_context_get_containers (self->context);
+ const char *address;
+
+ address = bus_containers_ensure_address_template (containers, error);
+
+ if (address == NULL)
+ return FALSE;
+
+ self->server = dbus_server_listen (address, error);
+
+ if (self->server == NULL)
+ return FALSE;
+
+ if (!bus_context_setup_server (self->context, self->server, error))
+ return FALSE;
+
+ if (!dbus_server_set_auth_mechanisms (self->server,
+ (const char **) auth_mechanisms))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ /* Cannot fail because the memory it uses was already allocated */
+ dbus_server_set_new_connection_function (self->server, new_connection_cb,
+ bus_container_instance_ref (self),
+ (DBusFreeFunction) bus_container_instance_unref);
+ return TRUE;
+}
+
dbus_bool_t
bus_containers_handle_add_server (DBusConnection *connection,
BusTransaction *transaction,
@@ -210,11 +407,18 @@ bus_containers_handle_add_server (DBusConnection *connection,
{
DBusMessageIter iter;
DBusMessageIter dict_iter;
+ DBusMessageIter writer;
+ DBusMessageIter array_writer;
const char *type;
const char *name;
+ const char *path;
BusContainerInstance *instance = NULL;
BusContext *context;
BusContainers *containers;
+ char *address = NULL;
+ DBusAddressEntry **entries = NULL;
+ int n_entries;
+ DBusMessage *reply = NULL;
context = bus_transaction_get_context (transaction);
containers = bus_context_get_containers (context);
@@ -309,16 +513,74 @@ bus_containers_handle_add_server (DBusConnection *connection,
instance->path, instance))
goto oom;
- /* TODO: Actually implement the method */
- dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, "Not yet implemented");
- goto fail;
+ /* This part is separated out because we eventually want to be able to
+ * accept a fd-passed server socket in the named parameters, instead of
+ * creating our own server, and defer listening on it until later */
+ if (!bus_container_instance_listen (instance, error))
+ goto fail;
+
+ address = dbus_server_get_address (instance->server);
+
+ if (!dbus_parse_address (address, &entries, &n_entries, error))
+ _dbus_assert_not_reached ("listening on unix:dir= should yield a valid address");
+
+ _dbus_assert (n_entries == 1);
+ _dbus_assert (strcmp (dbus_address_entry_get_method (entries[0]), "unix") == 0);
+
+ path = dbus_address_entry_get_value (entries[0], "path");
+ _dbus_assert (path != NULL);
+
+ reply = dbus_message_new_method_return (message);
+
+ if (!dbus_message_append_args (reply,
+ DBUS_TYPE_OBJECT_PATH, &instance->path,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ dbus_message_iter_init_append (reply, &writer);
+
+ if (!dbus_message_iter_open_container (&writer, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING,
+ &array_writer))
+ goto oom;
+
+ if (!dbus_message_iter_append_fixed_array (&array_writer, DBUS_TYPE_BYTE,
+ &path, strlen (path) + 1))
+ {
+ dbus_message_iter_abandon_container (&writer, &array_writer);
+ goto oom;
+ }
+
+ if (!dbus_message_iter_close_container (&writer, &array_writer))
+ goto oom;
+
+ if (!dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ _dbus_assert (dbus_message_has_signature (reply, "oays"));
+
+ if (! bus_transaction_send_from_driver (transaction, connection, reply))
+ goto oom;
+
+ dbus_message_unref (reply);
+ bus_container_instance_unref (instance);
+ dbus_address_entries_free (entries);
+ dbus_free (address);
+ return TRUE;
oom:
BUS_SET_OOM (error);
/* fall through */
fail:
+ if (instance != NULL)
+ bus_container_instance_stop_listening (instance);
+ dbus_clear_message (&reply);
+ dbus_clear_address_entries (&entries);
bus_clear_container_instance (&instance);
+ dbus_free (address);
return FALSE;
}
@@ -335,6 +597,24 @@ bus_containers_supported_arguments_getter (BusContext *context,
dbus_message_iter_close_container (var_iter, &arr_iter);
}
+void
+bus_containers_stop_listening (BusContainers *self)
+{
+ if (self->instances_by_path != NULL)
+ {
+ DBusHashIter iter;
+
+ _dbus_hash_iter_init (self->instances_by_path, &iter);
+
+ while (_dbus_hash_iter_next (&iter))
+ {
+ BusContainerInstance *instance = _dbus_hash_iter_get_value (&iter);
+
+ bus_container_instance_stop_listening (instance);
+ }
+ }
+}
+
#else
BusContainers *
@@ -359,4 +639,10 @@ bus_containers_unref (BusContainers *self)
_dbus_assert (self == (BusContainers *) 1);
}
+void
+bus_containers_stop_listening (BusContainers *self)
+{
+ _dbus_assert (self == (BusContainers *) 1);
+}
+
#endif /* DBUS_ENABLE_CONTAINERS */