diff options
Diffstat (limited to 'bus/containers.c')
-rw-r--r-- | bus/containers.c | 294 |
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 */ |