trackers: add sessiond tracker list implementation
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Thu, 5 Jul 2018 14:31:01 +0000 (10:31 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Fri, 20 Dec 2019 05:28:15 +0000 (00:28 -0500)
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Change-Id: I28dbeac1f3db2df7e61911249a3250fbc17dc244
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
src/bin/lttng-sessiond/tracker.c [new file with mode: 0644]
src/bin/lttng-sessiond/tracker.h [new file with mode: 0644]

diff --git a/src/bin/lttng-sessiond/tracker.c b/src/bin/lttng-sessiond/tracker.c
new file mode 100644 (file)
index 0000000..af527b4
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2018 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _LGPL_SOURCE
+#include <grp.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "tracker.h"
+#include <common/defaults.h>
+#include <common/error.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <lttng/lttng-error.h>
+
+#define FALLBACK_USER_BUFLEN 16384
+#define FALLBACK_GROUP_BUFLEN 16384
+
+struct lttng_tracker_list *lttng_tracker_list_create(void)
+{
+       struct lttng_tracker_list *t;
+
+       t = zmalloc(sizeof(*t));
+       if (!t) {
+               return NULL;
+       }
+       t->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
+                       CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
+       if (!t->ht) {
+               goto error;
+       }
+       CDS_INIT_LIST_HEAD(&t->list_head);
+       t->state = LTTNG_TRACK_ALL;
+       return t;
+
+error:
+       free(t);
+       return NULL;
+}
+
+static int match_tracker_key(struct cds_lfht_node *node, const void *key)
+{
+       const struct lttng_tracker_id *tracker_key = key;
+       struct lttng_tracker_list_node *tracker_node;
+
+       tracker_node = caa_container_of(
+                       node, struct lttng_tracker_list_node, ht_node);
+       if (tracker_node->id.type != tracker_key->type) {
+               return 0;
+       }
+       switch (tracker_node->id.type) {
+       case LTTNG_ID_ALL:
+               return 1;
+       case LTTNG_ID_VALUE:
+               if (tracker_node->id.value != tracker_key->value) {
+                       return 0;
+               }
+               break;
+       case LTTNG_ID_STRING:
+               if (strcmp(tracker_node->id.string, tracker_key->string) != 0) {
+                       return 0;
+               }
+               break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static unsigned long hash_tracker_key(
+               const struct lttng_tracker_id *tracker_key)
+{
+       unsigned long key_hash = 0;
+
+       switch (tracker_key->type) {
+       case LTTNG_ID_ALL:
+               break;
+       case LTTNG_ID_VALUE:
+               key_hash ^= hash_key_ulong(
+                               (void *) (unsigned long) tracker_key->value,
+                               lttng_ht_seed);
+               break;
+       case LTTNG_ID_STRING:
+               key_hash ^= hash_key_str(tracker_key->string, lttng_ht_seed);
+               break;
+       case LTTNG_ID_UNKNOWN:
+               break;
+       }
+       key_hash ^= hash_key_ulong((void *) (unsigned long) tracker_key->type,
+                       lttng_ht_seed);
+       return key_hash;
+}
+
+static struct lttng_tracker_id *lttng_tracker_list_lookup(
+               const struct lttng_tracker_list *tracker_list,
+               const struct lttng_tracker_id *key)
+{
+       struct lttng_tracker_list_node *list_node;
+       struct cds_lfht_iter iter;
+       struct cds_lfht_node *node;
+
+       cds_lfht_lookup(tracker_list->ht, hash_tracker_key(key),
+                       match_tracker_key, key, &iter);
+       node = cds_lfht_iter_get_node(&iter);
+       if (!node) {
+               return NULL;
+       }
+       list_node = caa_container_of(
+                       node, struct lttng_tracker_list_node, ht_node);
+       return &list_node->id;
+}
+
+static void destroy_list_node_rcu(struct rcu_head *head)
+{
+       struct lttng_tracker_list_node *n = caa_container_of(
+                       head, struct lttng_tracker_list_node, rcu_head);
+
+       free(n->id.string);
+       free(n);
+}
+
+static void _lttng_tracker_list_remove(struct lttng_tracker_list *tracker_list,
+               struct lttng_tracker_list_node *n)
+{
+       cds_list_del(&n->list_node);
+
+       rcu_read_lock();
+       cds_lfht_del(tracker_list->ht, &n->ht_node);
+       rcu_read_unlock();
+
+       call_rcu(&n->rcu_head, destroy_list_node_rcu);
+}
+
+static void lttng_tracker_list_reset(struct lttng_tracker_list *tracker_list)
+{
+       struct lttng_tracker_list_node *n, *t;
+
+       cds_list_for_each_entry_safe (
+                       n, t, &tracker_list->list_head, list_node) {
+               _lttng_tracker_list_remove(tracker_list, n);
+       }
+       tracker_list->state = LTTNG_TRACK_ALL;
+}
+
+/* Protected by session mutex held by caller. */
+int lttng_tracker_list_add(struct lttng_tracker_list *tracker_list,
+               const struct lttng_tracker_id *_id)
+{
+       struct lttng_tracker_id *id;
+       struct lttng_tracker_list_node *n;
+       int ret;
+
+       if (_id->type == LTTNG_ID_ALL) {
+               /* Track all, so remove each individual item. */
+               lttng_tracker_list_reset(tracker_list);
+               return LTTNG_OK;
+       }
+       rcu_read_lock();
+       id = lttng_tracker_list_lookup(tracker_list, _id);
+       /*
+        * It is okay to release the RCU read lock here since id is only checked
+        * for != NULL and not dereferenced.
+        */
+       rcu_read_unlock();
+       if (id) {
+               return LTTNG_ERR_ID_TRACKED;
+       }
+       n = zmalloc(sizeof(*n));
+       if (!n) {
+               return LTTNG_ERR_NOMEM;
+       }
+       n->id = *_id;
+       if (_id->type == LTTNG_ID_STRING) {
+               n->id.string = strdup(_id->string);
+               if (!n->id.string) {
+                       ret = LTTNG_ERR_NOMEM;
+                       goto error;
+               }
+       } else {
+               n->id.string = NULL;
+       }
+
+       cds_list_add_tail(&n->list_node, &tracker_list->list_head);
+       tracker_list->state = LTTNG_TRACK_LIST;
+
+       rcu_read_lock();
+       cds_lfht_add(tracker_list->ht, hash_tracker_key(&n->id), &n->ht_node);
+       rcu_read_unlock();
+
+       return LTTNG_OK;
+
+error:
+       free(n);
+       return ret;
+}
+
+/*
+ * Lookup and remove.
+ * Protected by session mutex held by caller.
+ */
+int lttng_tracker_list_remove(struct lttng_tracker_list *tracker_list,
+               const struct lttng_tracker_id *_id)
+{
+       enum lttng_error_code ret = LTTNG_OK;
+       struct lttng_tracker_id *id;
+       struct lttng_tracker_list_node *n;
+
+       if (_id->type == LTTNG_ID_ALL) {
+               /* Untrack all. */
+               lttng_tracker_list_reset(tracker_list);
+               /* Set state to "track none". */
+               tracker_list->state = LTTNG_TRACK_NONE;
+               goto end;
+       }
+
+       rcu_read_lock();
+       id = lttng_tracker_list_lookup(tracker_list, _id);
+       if (!id) {
+               ret = LTTNG_ERR_ID_NOT_TRACKED;
+               goto rcu_unlock;
+       }
+
+       n = caa_container_of(id, struct lttng_tracker_list_node, id);
+       _lttng_tracker_list_remove(tracker_list, n);
+
+rcu_unlock:
+       rcu_read_unlock();
+end:
+       return ret;
+}
+
+void lttng_tracker_list_destroy(struct lttng_tracker_list *tracker_list)
+{
+       if (!tracker_list) {
+               return;
+       }
+       lttng_tracker_list_reset(tracker_list);
+       cds_lfht_destroy(tracker_list->ht, NULL);
+       free(tracker_list);
+}
+
+static int lttng_lookup_user(const char *username, int *result)
+{
+       struct passwd p, *pres;
+       int ret, retval = LTTNG_OK;
+       char *buf = NULL;
+       ssize_t buflen;
+
+       buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+       if (buflen < 0) {
+               buflen = FALLBACK_USER_BUFLEN;
+       }
+       buf = zmalloc(buflen);
+       if (!buf) {
+               retval = LTTNG_ERR_NOMEM;
+               goto end;
+       }
+       for (;;) {
+               ret = getpwnam_r(username, &p, buf, buflen, &pres);
+               switch (ret) {
+               case EINTR:
+                       continue;
+               case ERANGE:
+                       buflen *= 2;
+                       free(buf);
+                       buf = zmalloc(buflen);
+                       if (!buf) {
+                               retval = LTTNG_ERR_NOMEM;
+                               goto end;
+                       }
+                       continue;
+               default:
+                       goto end_loop;
+               }
+       }
+end_loop:
+
+       switch (ret) {
+       case 0:
+               if (pres == NULL) {
+                       retval = LTTNG_ERR_USER_NOT_FOUND;
+               } else {
+                       *result = (int) p.pw_uid;
+                       DBG("Lookup of tracker UID/VUID: name '%s' maps to id %d.",
+                                       username, *result);
+                       retval = LTTNG_OK;
+               }
+               break;
+       case ENOENT:
+       case ESRCH:
+       case EBADF:
+       case EPERM:
+               retval = LTTNG_ERR_USER_NOT_FOUND;
+               break;
+       default:
+               retval = LTTNG_ERR_NOMEM;
+       }
+end:
+       free(buf);
+       return retval;
+}
+
+static int lttng_lookup_group(const char *groupname, int *result)
+{
+       struct group g, *gres;
+       int ret, retval = LTTNG_OK;
+       char *buf = NULL;
+       ssize_t buflen;
+
+       buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+       if (buflen < 0) {
+               buflen = FALLBACK_GROUP_BUFLEN;
+       }
+       buf = zmalloc(buflen);
+       if (!buf) {
+               retval = LTTNG_ERR_NOMEM;
+               goto end;
+       }
+       for (;;) {
+               ret = getgrnam_r(groupname, &g, buf, buflen, &gres);
+               switch (ret) {
+               case EINTR:
+                       continue;
+               case ERANGE:
+                       buflen *= 2;
+                       free(buf);
+                       buf = zmalloc(buflen);
+                       if (!buf) {
+                               retval = LTTNG_ERR_NOMEM;
+                               goto end;
+                       }
+                       continue;
+               default:
+                       goto end_loop;
+               }
+       }
+end_loop:
+
+       switch (ret) {
+       case 0:
+               if (gres == NULL) {
+                       retval = LTTNG_ERR_GROUP_NOT_FOUND;
+               } else {
+                       *result = (int) g.gr_gid;
+                       DBG("Lookup of tracker GID/GUID: name '%s' maps to id %d.",
+                                       groupname, *result);
+                       retval = LTTNG_OK;
+               }
+               break;
+       case ENOENT:
+       case ESRCH:
+       case EBADF:
+       case EPERM:
+               retval = LTTNG_ERR_GROUP_NOT_FOUND;
+               break;
+       default:
+               retval = LTTNG_ERR_NOMEM;
+       }
+end:
+       free(buf);
+       return retval;
+}
+
+int lttng_tracker_id_lookup_string(enum lttng_tracker_type tracker_type,
+               const struct lttng_tracker_id *id,
+               int *result)
+{
+       switch (id->type) {
+       case LTTNG_ID_ALL:
+               *result = -1;
+               return LTTNG_OK;
+       case LTTNG_ID_VALUE:
+               *result = id->value;
+               return LTTNG_OK;
+       case LTTNG_ID_STRING:
+               switch (tracker_type) {
+               case LTTNG_TRACKER_PID:
+               case LTTNG_TRACKER_VPID:
+                       ERR("Lookup of tracker PID/VPID by name unsupported.");
+                       return LTTNG_ERR_INVALID;
+               case LTTNG_TRACKER_UID:
+               case LTTNG_TRACKER_VUID:
+                       DBG("Lookup of tracker UID/VUID by name.");
+                       return lttng_lookup_user(id->string, result);
+               case LTTNG_TRACKER_GID:
+               case LTTNG_TRACKER_VGID:
+                       DBG("Lookup of tracker GID/VGID by name.");
+                       return lttng_lookup_group(id->string, result);
+               default:
+                       return LTTNG_ERR_INVALID;
+               }
+               break;
+       default:
+               return LTTNG_ERR_INVALID;
+       }
+}
+
+/*
+ * Protected by session mutex held by caller.
+ * On success, _ids and the strings it contains must be freed by caller.
+ */
+ssize_t lttng_tracker_id_get_list(const struct lttng_tracker_list *tracker_list,
+               struct lttng_tracker_id **_ids)
+{
+       struct lttng_tracker_list_node *n;
+       ssize_t count = 0, i = 0, retval = 0;
+       struct lttng_tracker_id *ids;
+
+       switch (tracker_list->state) {
+       case LTTNG_TRACK_LIST:
+               cds_list_for_each_entry (
+                               n, &tracker_list->list_head, list_node) {
+                       count++;
+               }
+               ids = zmalloc(sizeof(*ids) * count);
+               if (ids == NULL) {
+                       PERROR("Failed to allocate tracked ID list");
+                       retval = -LTTNG_ERR_NOMEM;
+                       goto end;
+               }
+               cds_list_for_each_entry (
+                               n, &tracker_list->list_head, list_node) {
+                       ids[i].type = n->id.type;
+                       ids[i].value = n->id.value;
+                       if (ids[i].type == LTTNG_ID_STRING) {
+                               ids[i].string = strdup(n->id.string);
+                               if (!ids[i].string) {
+                                       retval = -LTTNG_ERR_NOMEM;
+                                       goto error;
+                               }
+                       }
+                       i++;
+               }
+               *_ids = ids;
+               retval = count;
+               break;
+       case LTTNG_TRACK_ALL:
+               ids = zmalloc(sizeof(*ids));
+               if (ids == NULL) {
+                       PERROR("Failed to allocate tracked ID list");
+                       retval = -LTTNG_ERR_NOMEM;
+                       goto end;
+               }
+               ids->type = LTTNG_TRACK_ALL;
+               *_ids = ids;
+               retval = 1;
+               break;
+       case LTTNG_TRACK_NONE:
+               /* No ids track, so we return 0 element. */
+               *_ids = NULL;
+               break;
+       }
+end:
+       return retval;
+
+error:
+       for (i = 0; i < count; i++) {
+               free(ids[i].string);
+       }
+       free(ids);
+       return retval;
+}
+
+int lttng_tracker_id_set_list(struct lttng_tracker_list *tracker_list,
+               struct lttng_tracker_id *_ids,
+               size_t count)
+{
+       size_t i;
+
+       lttng_tracker_list_reset(tracker_list);
+       if (count == 1 && _ids[0].type == LTTNG_ID_ALL) {
+               /* Track all. */
+               return LTTNG_OK;
+       }
+       if (count == 0) {
+               /* Set state to "track none". */
+               tracker_list->state = LTTNG_TRACK_NONE;
+               return LTTNG_OK;
+       }
+       for (i = 0; i < count; i++) {
+               struct lttng_tracker_id *id = &_ids[i];
+               int ret;
+
+               ret = lttng_tracker_list_add(tracker_list, id);
+               if (ret != LTTNG_OK) {
+                       return ret;
+               }
+       }
+       return LTTNG_OK;
+}
diff --git a/src/bin/lttng-sessiond/tracker.h b/src/bin/lttng-sessiond/tracker.h
new file mode 100644 (file)
index 0000000..1df4a0c
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef _LTT_TRACKER_H
+#define _LTT_TRACKER_H
+
+/*
+ * Copyright (C) 2018 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <lttng/session.h>
+#include <urcu.h>
+#include <urcu/list.h>
+#include <urcu/rculfhash.h>
+
+enum lttng_tracker_list_state {
+       LTTNG_TRACK_ALL,
+       LTTNG_TRACK_NONE,
+       LTTNG_TRACK_LIST,
+};
+
+/* Tracker ID */
+struct lttng_tracker_list_node {
+       struct lttng_tracker_id id;
+
+       struct cds_list_head list_node;
+       struct cds_lfht_node ht_node;
+       struct rcu_head rcu_head;
+};
+
+struct lttng_tracker_list {
+       struct cds_list_head list_head;
+       /* Hash table for O(1) removal lookup. */
+       struct cds_lfht *ht;
+       enum lttng_tracker_list_state state;
+};
+
+struct lttng_tracker_list *lttng_tracker_list_create(void);
+void lttng_tracker_list_destroy(struct lttng_tracker_list *tracker_list);
+
+int lttng_tracker_list_add(struct lttng_tracker_list *tracker_list,
+               const struct lttng_tracker_id *id);
+int lttng_tracker_list_remove(struct lttng_tracker_list *tracker_list,
+               const struct lttng_tracker_id *id);
+
+int lttng_tracker_id_lookup_string(enum lttng_tracker_type tracker_type,
+               const struct lttng_tracker_id *id,
+               int *result);
+ssize_t lttng_tracker_id_get_list(const struct lttng_tracker_list *tracker_list,
+               struct lttng_tracker_id **_ids);
+int lttng_tracker_id_set_list(struct lttng_tracker_list *tracker_list,
+               struct lttng_tracker_id *_ids,
+               size_t count);
+
+#endif /* _LTT_TRACKER_H */
This page took 0.031577 seconds and 4 git commands to generate.