From 7cd2354b993366f0b45b19cced9d220d9f336c39 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 20 Jun 2017 12:31:18 +0100 Subject: bus/containers: Build a global data structure for container instances We still don't actually create a DBusServer for incoming connections at this point, much less accept incoming connections. Signed-off-by: Simon McVittie Reviewed-by: Philip Withnall Bug: https://bugs.freedesktop.org/show_bug.cgi?id=101354 --- bus/bus.c | 17 ++++ bus/bus.h | 2 + bus/containers.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- bus/containers.h | 12 +++ 4 files changed, 268 insertions(+), 3 deletions(-) diff --git a/bus/bus.c b/bus/bus.c index 295dc7b5..8b08b8ea 100644 --- a/bus/bus.c +++ b/bus/bus.c @@ -29,6 +29,7 @@ #include "activation.h" #include "connection.h" +#include "containers.h" #include "services.h" #include "utils.h" #include "policy.h" @@ -69,6 +70,7 @@ struct BusContext BusMatchmaker *matchmaker; BusLimits limits; DBusRLimit *initial_fd_limit; + BusContainers *containers; unsigned int fork : 1; unsigned int syslog : 1; unsigned int keep_umask : 1; @@ -887,6 +889,14 @@ bus_context_new (const DBusString *config_file, goto failed; } + context->containers = bus_containers_new (); + + if (context->containers == NULL) + { + BUS_SET_OOM (error); + goto failed; + } + /* check user before we fork */ if (context->user != NULL) { @@ -1172,6 +1182,7 @@ bus_context_unref (BusContext *context) context->matchmaker = NULL; } + bus_clear_containers (&context->containers); dbus_free (context->config_file); dbus_free (context->log_prefix); dbus_free (context->type); @@ -1282,6 +1293,12 @@ bus_context_get_policy (BusContext *context) return context->policy; } +BusContainers * +bus_context_get_containers (BusContext *context) +{ + return context->containers; +} + BusClientPolicy* bus_context_create_client_policy (BusContext *context, DBusConnection *connection, diff --git a/bus/bus.h b/bus/bus.h index 647f9988..edc95773 100644 --- a/bus/bus.h +++ b/bus/bus.h @@ -45,6 +45,7 @@ typedef struct BusTransaction BusTransaction; typedef struct BusMatchmaker BusMatchmaker; typedef struct BusMatchRule BusMatchRule; typedef struct BusActivationEntry BusActivationEntry; +typedef struct BusContainers BusContainers; typedef struct { @@ -106,6 +107,7 @@ dbus_bool_t bus_context_allow_unix_user (BusContext dbus_bool_t bus_context_allow_windows_user (BusContext *context, const char *windows_sid); BusPolicy* bus_context_get_policy (BusContext *context); +BusContainers *bus_context_get_containers (BusContext *context); BusClientPolicy* bus_context_create_client_policy (BusContext *context, DBusConnection *connection, diff --git a/bus/containers.c b/bus/containers.c index cf9156f4..1c922952 100644 --- a/bus/containers.c +++ b/bus/containers.c @@ -23,15 +23,185 @@ #include #include "containers.h" +#include "dbus/dbus-internals.h" + #ifdef DBUS_ENABLE_CONTAINERS #ifndef DBUS_UNIX # error DBUS_ENABLE_CONTAINERS requires DBUS_UNIX #endif -#include "dbus/dbus-internals.h" +#include "dbus/dbus-hash.h" +#include "dbus/dbus-message-internal.h" #include "dbus/dbus-sysdeps-unix.h" +#include "connection.h" +#include "utils.h" + +/* + * A container instance groups together a per-app-container server with + * all the connections for which it is responsible. + */ +typedef struct +{ + int refcount; + char *path; + char *type; + char *name; + DBusVariant *metadata; + BusContext *context; + BusContainers *containers; +} BusContainerInstance; + +/* + * Singleton data structure encapsulating the container-related parts of + * a BusContext. + */ +struct BusContainers +{ + int refcount; + /* path borrowed from BusContainerInstance => unowned BusContainerInstance + * The BusContainerInstance removes itself from here on destruction. */ + DBusHashTable *instances_by_path; + dbus_uint64_t next_container_id; +}; + +BusContainers * +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); + + if (self == NULL) + return NULL; + + self->refcount = 1; + self->instances_by_path = NULL; + self->next_container_id = DBUS_UINT64_CONSTANT (0); + return self; +} + +BusContainers * +bus_containers_ref (BusContainers *self) +{ + _dbus_assert (self->refcount > 0); + _dbus_assert (self->refcount < _DBUS_INT_MAX); + + self->refcount++; + return self; +} + +void +bus_containers_unref (BusContainers *self) +{ + _dbus_assert (self != NULL); + _dbus_assert (self->refcount > 0); + + if (--self->refcount == 0) + { + _dbus_clear_hash_table (&self->instances_by_path); + dbus_free (self); + } +} + +static void +bus_container_instance_unref (BusContainerInstance *self) +{ + _dbus_assert (self->refcount > 0); + + if (--self->refcount == 0) + { + /* 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) + _dbus_hash_table_remove_string (self->containers->instances_by_path, + self->path); + + _dbus_clear_variant (&self->metadata); + bus_context_unref (self->context); + bus_containers_unref (self->containers); + dbus_free (self->path); + dbus_free (self->type); + dbus_free (self->name); + dbus_free (self); + } +} + +static inline void +bus_clear_container_instance (BusContainerInstance **instance_p) +{ + _dbus_clear_pointer_impl (BusContainerInstance, instance_p, + bus_container_instance_unref); +} + +static BusContainerInstance * +bus_container_instance_new (BusContext *context, + BusContainers *containers, + DBusError *error) +{ + BusContainerInstance *self = NULL; + DBusString path = _DBUS_STRING_INIT_INVALID; + + _dbus_assert (context != NULL); + _dbus_assert (containers != NULL); + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!_dbus_string_init (&path)) + { + BUS_SET_OOM (error); + goto fail; + } + + self = dbus_new0 (BusContainerInstance, 1); + + if (self == NULL) + { + BUS_SET_OOM (error); + goto fail; + } + + self->refcount = 1; + self->type = NULL; + self->name = NULL; + self->metadata = NULL; + self->context = bus_context_ref (context); + self->containers = bus_containers_ref (containers); + + if (containers->next_container_id >= + DBUS_UINT64_CONSTANT (0xFFFFFFFFFFFFFFFF)) + { + /* We can't increment it any further without wrapping around */ + dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED, + "Too many containers created during the lifetime of " + "this bus"); + goto fail; + } + + /* We assume PRIu64 exists on all Unix platforms: it's ISO C99, and the + * only non-C99 platform we support is MSVC on Windows. */ + if (!_dbus_string_append_printf (&path, + "/org/freedesktop/DBus/Containers1/c%" PRIu64, + containers->next_container_id++)) + { + BUS_SET_OOM (error); + goto fail; + } + + if (!_dbus_string_steal_data (&path, &self->path)) + goto fail; + + return self; + +fail: + _dbus_string_free (&path); + + if (self != NULL) + bus_container_instance_unref (self); + + return NULL; +} + dbus_bool_t bus_containers_handle_add_server (DBusConnection *connection, BusTransaction *transaction, @@ -42,6 +212,17 @@ bus_containers_handle_add_server (DBusConnection *connection, DBusMessageIter dict_iter; const char *type; const char *name; + BusContainerInstance *instance = NULL; + BusContext *context; + BusContainers *containers; + + context = bus_transaction_get_context (transaction); + containers = bus_context_get_containers (context); + + instance = bus_container_instance_new (context, containers, error); + + if (instance == NULL) + goto fail; /* We already checked this in bus_driver_handle_message() */ _dbus_assert (dbus_message_has_signature (message, "ssa{sv}a{sv}")); @@ -52,6 +233,10 @@ bus_containers_handle_add_server (DBusConnection *connection, _dbus_assert (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING); dbus_message_iter_get_basic (&iter, &type); + instance->type = _dbus_strdup (type); + + if (instance->type == NULL) + goto oom; if (!dbus_validate_interface (type, NULL)) { @@ -67,13 +252,19 @@ bus_containers_handle_add_server (DBusConnection *connection, _dbus_assert (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING); dbus_message_iter_get_basic (&iter, &name); + instance->name = _dbus_strdup (name); + + if (instance->name == NULL) + goto oom; /* Argument 2: Metadata as defined by container manager */ if (!dbus_message_iter_next (&iter)) _dbus_assert_not_reached ("Message type was already checked"); _dbus_assert (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_ARRAY); - /* TODO: Copy the metadata */ + instance->metadata = _dbus_variant_read (&iter); + _dbus_assert (strcmp (_dbus_variant_get_signature (instance->metadata), + "a{sv}") == 0); /* Argument 3: Named parameters */ if (!dbus_message_iter_next (&iter)) @@ -95,7 +286,7 @@ bus_containers_handle_add_server (DBusConnection *connection, DBUS_TYPE_STRING); dbus_message_iter_get_basic (&pair_iter, ¶m_name); - /* If we supported any named parameters, we'd copy them into a data + /* If we supported any named parameters, we'd copy them into the data * structure here; but we don't, so fail instead. */ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, "Named parameter %s is not understood", param_name); @@ -105,10 +296,29 @@ bus_containers_handle_add_server (DBusConnection *connection, /* End of arguments */ _dbus_assert (!dbus_message_iter_has_next (&iter)); + if (containers->instances_by_path == NULL) + { + containers->instances_by_path = _dbus_hash_table_new (DBUS_HASH_STRING, + NULL, NULL); + + if (containers->instances_by_path == NULL) + goto oom; + } + + if (!_dbus_hash_table_insert_string (containers->instances_by_path, + instance->path, instance)) + goto oom; + /* TODO: Actually implement the method */ dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, "Not yet implemented"); + goto fail; +oom: + BUS_SET_OOM (error); + /* fall through */ fail: + + bus_clear_container_instance (&instance); return FALSE; } @@ -125,4 +335,28 @@ bus_containers_supported_arguments_getter (BusContext *context, dbus_message_iter_close_container (var_iter, &arr_iter); } +#else + +BusContainers * +bus_containers_new (void) +{ + /* Return an arbitrary non-NULL pointer just to indicate that we didn't + * fail. There is no valid operation to do with it on this platform, + * other than unreffing it, which does nothing. */ + return (BusContainers *) 1; +} + +BusContainers * +bus_containers_ref (BusContainers *self) +{ + _dbus_assert (self == (BusContainers *) 1); + return self; +} + +void +bus_containers_unref (BusContainers *self) +{ + _dbus_assert (self == (BusContainers *) 1); +} + #endif /* DBUS_ENABLE_CONTAINERS */ diff --git a/bus/containers.h b/bus/containers.h index 3564bbd2..bda0a879 100644 --- a/bus/containers.h +++ b/bus/containers.h @@ -25,6 +25,12 @@ #include "bus.h" +#include + +BusContainers *bus_containers_new (void); +BusContainers *bus_containers_ref (BusContainers *self); +void bus_containers_unref (BusContainers *self); + dbus_bool_t bus_containers_handle_add_server (DBusConnection *connection, BusTransaction *transaction, DBusMessage *message, @@ -32,4 +38,10 @@ dbus_bool_t bus_containers_handle_add_server (DBusConnection *connecti dbus_bool_t bus_containers_supported_arguments_getter (BusContext *context, DBusMessageIter *var_iter); +static inline void +bus_clear_containers (BusContainers **containers_p) +{ + _dbus_clear_pointer_impl (BusContainers, containers_p, bus_containers_unref); +} + #endif /* multiple-inclusion guard */ -- cgit v1.2.3