Truncate exclusion names to have a terminal '\0'
[lttng-tools.git] / src / bin / lttng-sessiond / ust-registry.c
index 6c483e4b71c3107b614cc6201a53719d88307fd9..71fdc4fd8294d48c8f335222b2af2381cb1972e0 100644 (file)
@@ -14,7 +14,8 @@
  * this program; if not, write to the Free Software Foundation, Inc., 51
  * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
-#define _GNU_SOURCE
+
+#define _LGPL_SOURCE
 #include <assert.h>
 #include <inttypes.h>
 
@@ -23,6 +24,8 @@
 #include <lttng/lttng.h>
 
 #include "ust-registry.h"
+#include "ust-app.h"
+#include "utils.h"
 
 /*
  * Hash table match function for event in the registry.
@@ -40,13 +43,13 @@ static int ht_match_event(struct cds_lfht_node *node, const void *_key)
        key = _key;
 
        /* It has to be a perfect match. */
-       if (strncmp(event->name, key->name, sizeof(event->name)) != 0) {
+       if (strncmp(event->name, key->name, sizeof(event->name))) {
                goto no_match;
        }
 
        /* It has to be a perfect match. */
        if (strncmp(event->signature, key->signature,
-                               strlen(event->signature) != 0)) {
+                       strlen(event->signature))) {
                goto no_match;
        }
 
@@ -70,16 +73,78 @@ static unsigned long ht_hash_event(void *_key, unsigned long seed)
        return hash_key_u64(&xored_key, seed);
 }
 
+/*
+ * Return negative value on error, 0 if OK.
+ *
+ * TODO: we could add stricter verification of more types to catch
+ * errors in liblttng-ust implementation earlier than consumption by the
+ * trace reader.
+ */
+static
+int validate_event_field(struct ustctl_field *field,
+               const char *event_name,
+               struct ust_app *app)
+{
+       switch(field->type.atype) {
+       case ustctl_atype_integer:
+       case ustctl_atype_enum:
+       case ustctl_atype_array:
+       case ustctl_atype_sequence:
+       case ustctl_atype_string:
+               break;
+
+       case ustctl_atype_float:
+               switch (field->type.u.basic._float.mant_dig) {
+               case 0:
+                       WARN("UST application '%s' (pid: %d) has unknown float mantissa '%u' "
+                               "in field '%s', rejecting event '%s'",
+                               app->name, app->pid,
+                               field->type.u.basic._float.mant_dig,
+                               field->name,
+                               event_name);
+                       return -EINVAL;
+               default:
+                       break;
+               }
+               break;
+
+       default:
+               return -ENOENT;
+       }
+       return 0;
+}
+
+static
+int validate_event_fields(size_t nr_fields, struct ustctl_field *fields,
+               const char *event_name, struct ust_app *app)
+{
+       unsigned int i;
+
+       for (i = 0; i < nr_fields; i++) {
+               if (validate_event_field(&fields[i], event_name, app) < 0)
+                       return -EINVAL;
+       }
+       return 0;
+}
+
 /*
  * Allocate event and initialize it. This does NOT set a valid event id from a
  * registry.
  */
 static struct ust_registry_event *alloc_event(int session_objd,
                int channel_objd, char *name, char *sig, size_t nr_fields,
-               struct ustctl_field *fields, int loglevel, char *model_emf_uri)
+               struct ustctl_field *fields, int loglevel_value,
+               char *model_emf_uri, struct ust_app *app)
 {
        struct ust_registry_event *event = NULL;
 
+       /*
+        * Ensure that the field content is valid.
+        */
+       if (validate_event_fields(nr_fields, fields, name, app) < 0) {
+               return NULL;
+       }
+
        event = zmalloc(sizeof(*event));
        if (!event) {
                PERROR("zmalloc ust registry event");
@@ -92,7 +157,7 @@ static struct ust_registry_event *alloc_event(int session_objd,
        event->signature = sig;
        event->nr_fields = nr_fields;
        event->fields = fields;
-       event->loglevel = loglevel;
+       event->loglevel_value = loglevel_value;
        event->model_emf_uri = model_emf_uri;
        if (name) {
                /* Copy event name and force NULL byte. */
@@ -183,8 +248,9 @@ end:
  */
 int ust_registry_create_event(struct ust_registry_session *session,
                uint64_t chan_key, int session_objd, int channel_objd, char *name,
-               char *sig, size_t nr_fields, struct ustctl_field *fields, int loglevel,
-               char *model_emf_uri, int buffer_type, uint32_t *event_id_p)
+               char *sig, size_t nr_fields, struct ustctl_field *fields,
+               int loglevel_value, char *model_emf_uri, int buffer_type,
+               uint32_t *event_id_p, struct ust_app *app)
 {
        int ret;
        uint32_t event_id;
@@ -197,34 +263,34 @@ int ust_registry_create_event(struct ust_registry_session *session,
        assert(sig);
        assert(event_id_p);
 
+       rcu_read_lock();
+
        /*
         * This should not happen but since it comes from the UST tracer, an
         * external party, don't assert and simply validate values.
         */
        if (session_objd < 0 || channel_objd < 0) {
                ret = -EINVAL;
-               goto error;
+               goto error_free;
        }
 
-       rcu_read_lock();
-
        chan = ust_registry_channel_find(session, chan_key);
        if (!chan) {
                ret = -EINVAL;
-               goto error_unlock;
+               goto error_free;
        }
 
        /* Check if we've reached the maximum possible id. */
        if (ust_registry_is_max_id(chan->used_event_id)) {
                ret = -ENOENT;
-               goto error_unlock;
+               goto error_free;
        }
 
        event = alloc_event(session_objd, channel_objd, name, sig, nr_fields,
-                       fields, loglevel, model_emf_uri);
+                       fields, loglevel_value, model_emf_uri, app);
        if (!event) {
                ret = -ENOMEM;
-               goto error_unlock;
+               goto error_free;
        }
 
        DBG3("UST registry creating event with event: %s, sig: %s, id: %u, "
@@ -278,9 +344,12 @@ int ust_registry_create_event(struct ust_registry_session *session,
        rcu_read_unlock();
        return 0;
 
+error_free:
+       free(sig);
+       free(fields);
+       free(model_emf_uri);
 error_unlock:
        rcu_read_unlock();
-error:
        destroy_event(event);
        return ret;
 }
@@ -308,12 +377,29 @@ void ust_registry_destroy_event(struct ust_registry_channel *chan,
        return;
 }
 
+/*
+ * We need to execute ht_destroy outside of RCU read-side critical
+ * section and outside of call_rcu thread, so we postpone its execution
+ * using ht_cleanup_push. It is simpler than to change the semantic of
+ * the many callers of delete_ust_app_session().
+ */
+static
+void destroy_channel_rcu(struct rcu_head *head)
+{
+       struct ust_registry_channel *chan =
+               caa_container_of(head, struct ust_registry_channel, rcu_head);
+
+       if (chan->ht) {
+               ht_cleanup_push(chan->ht);
+       }
+       free(chan->ctx_fields);
+       free(chan);
+}
+
 /*
  * Destroy every element of the registry and free the memory. This does NOT
  * free the registry pointer since it might not have been allocated before so
  * it's the caller responsability.
- *
- * This MUST be called within a RCU read side lock section.
  */
 static void destroy_channel(struct ust_registry_channel *chan)
 {
@@ -322,14 +408,14 @@ static void destroy_channel(struct ust_registry_channel *chan)
 
        assert(chan);
 
+       rcu_read_lock();
        /* Destroy all event associated with this registry. */
        cds_lfht_for_each_entry(chan->ht->ht, &iter.iter, event, node.node) {
                /* Delete the node from the ht and free it. */
                ust_registry_destroy_event(chan, event);
        }
-       lttng_ht_destroy(chan->ht);
-
-       free(chan);
+       rcu_read_unlock();
+       call_rcu(&chan->rcu_head, destroy_channel_rcu);
 }
 
 /*
@@ -347,7 +433,7 @@ int ust_registry_channel_add(struct ust_registry_session *session,
        if (!chan) {
                PERROR("zmalloc ust registry channel");
                ret = -ENOMEM;
-               goto error;
+               goto error_alloc;
        }
 
        chan->ht = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
@@ -376,7 +462,11 @@ int ust_registry_channel_add(struct ust_registry_session *session,
        lttng_ht_add_unique_u64(session->channels, &chan->node);
        rcu_read_unlock();
 
+       return 0;
+
 error:
+       destroy_channel(chan);
+error_alloc:
        return ret;
 }
 
@@ -418,22 +508,24 @@ void ust_registry_channel_del_free(struct ust_registry_session *session,
 {
        struct lttng_ht_iter iter;
        struct ust_registry_channel *chan;
+       int ret;
 
        assert(session);
 
        rcu_read_lock();
        chan = ust_registry_channel_find(session, key);
        if (!chan) {
+               rcu_read_unlock();
                goto end;
        }
 
        iter.iter.node = &chan->node.node;
-       lttng_ht_del(session->channels, &iter);
-
+       ret = lttng_ht_del(session->channels, &iter);
+       assert(!ret);
+       rcu_read_unlock();
        destroy_channel(chan);
 
 end:
-       rcu_read_unlock();
        return;
 }
 
@@ -454,7 +546,11 @@ int ust_registry_session_init(struct ust_registry_session **sessionp,
                uint32_t long_alignment,
                int byte_order,
                uint32_t major,
-               uint32_t minor)
+               uint32_t minor,
+               const char *root_shm_path,
+               const char *shm_path,
+               uid_t euid,
+               gid_t egid)
 {
        int ret;
        struct ust_registry_session *session;
@@ -464,7 +560,7 @@ int ust_registry_session_init(struct ust_registry_session **sessionp,
        session = zmalloc(sizeof(*session));
        if (!session) {
                PERROR("zmalloc ust registry session");
-               goto error;
+               goto error_alloc;
        }
 
        pthread_mutex_init(&session->lock, NULL);
@@ -475,6 +571,43 @@ int ust_registry_session_init(struct ust_registry_session **sessionp,
        session->uint64_t_alignment = uint64_t_alignment;
        session->long_alignment = long_alignment;
        session->byte_order = byte_order;
+       session->metadata_fd = -1;
+       session->uid = euid;
+       session->gid = egid;
+       strncpy(session->root_shm_path, root_shm_path,
+               sizeof(session->root_shm_path));
+       session->root_shm_path[sizeof(session->root_shm_path) - 1] = '\0';
+       if (shm_path[0]) {
+               strncpy(session->shm_path, shm_path,
+                       sizeof(session->shm_path));
+               session->shm_path[sizeof(session->shm_path) - 1] = '\0';
+               strncpy(session->metadata_path, shm_path,
+                       sizeof(session->metadata_path));
+               session->metadata_path[sizeof(session->metadata_path) - 1] = '\0';
+               strncat(session->metadata_path, "/metadata",
+                       sizeof(session->metadata_path)
+                               - strlen(session->metadata_path) - 1);
+       }
+       if (session->shm_path[0]) {
+               ret = run_as_mkdir_recursive(session->shm_path,
+                       S_IRWXU | S_IRWXG,
+                       euid, egid);
+               if (ret) {
+                       PERROR("run_as_mkdir_recursive");
+                       goto error;
+               }
+       }
+       if (session->metadata_path[0]) {
+               /* Create metadata file */
+               ret = run_as_open(session->metadata_path,
+                       O_WRONLY | O_CREAT | O_EXCL,
+                       S_IRUSR | S_IWUSR, euid, egid);
+               if (ret < 0) {
+                       PERROR("Opening metadata file");
+                       goto error;
+               }
+               session->metadata_fd = ret;
+       }
 
        session->channels = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
        if (!session->channels) {
@@ -500,6 +633,9 @@ int ust_registry_session_init(struct ust_registry_session **sessionp,
        return 0;
 
 error:
+       ust_registry_session_destroy(session);
+       free(session);
+error_alloc:
        return -1;
 }
 
@@ -513,20 +649,45 @@ void ust_registry_session_destroy(struct ust_registry_session *reg)
        struct lttng_ht_iter iter;
        struct ust_registry_channel *chan;
 
+       if (!reg) {
+               return;
+       }
+
        /* On error, EBUSY can be returned if lock. Code flow error. */
        ret = pthread_mutex_destroy(&reg->lock);
        assert(!ret);
 
-       rcu_read_lock();
-       /* Destroy all event associated with this registry. */
-       cds_lfht_for_each_entry(reg->channels->ht, &iter.iter, chan, node.node) {
-               /* Delete the node from the ht and free it. */
-               ret = lttng_ht_del(reg->channels, &iter);
-               assert(!ret);
-               destroy_channel(chan);
+       if (reg->channels) {
+               rcu_read_lock();
+               /* Destroy all event associated with this registry. */
+               cds_lfht_for_each_entry(reg->channels->ht, &iter.iter, chan,
+                               node.node) {
+                       /* Delete the node from the ht and free it. */
+                       ret = lttng_ht_del(reg->channels, &iter);
+                       assert(!ret);
+                       destroy_channel(chan);
+               }
+               rcu_read_unlock();
+               ht_cleanup_push(reg->channels);
        }
-       lttng_ht_destroy(reg->channels);
-       rcu_read_unlock();
 
        free(reg->metadata);
+       if (reg->metadata_fd >= 0) {
+               ret = close(reg->metadata_fd);
+               if (ret) {
+                       PERROR("close");
+               }
+               ret = run_as_unlink(reg->metadata_path,
+                               reg->uid, reg->gid);
+               if (ret) {
+                       PERROR("unlink");
+               }
+       }
+       if (reg->root_shm_path[0]) {
+               /*
+                * Try deleting the directory hierarchy.
+                */
+               (void) run_as_recursive_rmdir(reg->root_shm_path,
+                               reg->uid, reg->gid);
+       }
 }
This page took 0.027393 seconds and 4 git commands to generate.