/*
- * Copyright (C) 2018 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2018 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@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.
+ * SPDX-License-Identifier: GPL-2.0-only
*
- * 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/tracker.h"
+#include "common/dynamic-array.h"
+#include "common/macros.h"
#define _LGPL_SOURCE
#include <grp.h>
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
+#include <urcu.h>
+#include <urcu/list.h>
+#include <urcu/rculfhash.h>
#include "tracker.h"
#include <common/defaults.h>
#include <common/error.h>
#include <common/hashtable/hashtable.h>
#include <common/hashtable/utils.h>
+#include <common/tracker.h>
#include <lttng/lttng-error.h>
-#define FALLBACK_USER_BUFLEN 16384
-#define FALLBACK_GROUP_BUFLEN 16384
+struct process_attr_tracker_value_node {
+ struct process_attr_value *value;
+ struct cds_lfht_node inclusion_set_ht_node;
+ struct rcu_head rcu_head;
+};
+
+struct process_attr_tracker {
+ enum lttng_tracking_policy policy;
+ struct cds_lfht *inclusion_set_ht;
+};
+
+static void process_attr_tracker_value_node_rcu_free(struct rcu_head *rcu_head)
+{
+ struct process_attr_tracker_value_node *node =
+ container_of(rcu_head, typeof(*node), rcu_head);
+
+ free(node);
+}
-struct lttng_tracker_list *lttng_tracker_list_create(void)
+struct process_attr_tracker *process_attr_tracker_create(void)
{
- struct lttng_tracker_list *t;
+ struct process_attr_tracker *tracker;
- t = zmalloc(sizeof(*t));
- if (!t) {
+ tracker = zmalloc(sizeof(*tracker));
+ if (!tracker) {
return NULL;
}
- t->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
+
+ (void) process_attr_tracker_set_tracking_policy(
+ tracker, LTTNG_TRACKING_POLICY_INCLUDE_ALL);
+
+ tracker->inclusion_set_ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
- if (!t->ht) {
+ if (!tracker->inclusion_set_ht) {
goto error;
}
- CDS_INIT_LIST_HEAD(&t->list_head);
- t->state = LTTNG_TRACK_ALL;
- return t;
+ return tracker;
error:
- free(t);
+ process_attr_tracker_destroy(tracker);
return NULL;
}
-static int match_tracker_key(struct cds_lfht_node *node, const void *key)
+static void process_attr_tracker_remove_value_node(
+ struct process_attr_tracker *tracker,
+ struct process_attr_tracker_value_node *value_node)
{
- const struct lttng_tracker_id *tracker_key = key;
- struct lttng_tracker_list_node *tracker_node;
+ cds_lfht_del(tracker->inclusion_set_ht,
+ &value_node->inclusion_set_ht_node);
+ process_attr_value_destroy(value_node->value);
+ call_rcu(&value_node->rcu_head,
+ process_attr_tracker_value_node_rcu_free);
+}
+
+static void process_attr_tracker_clear_inclusion_set(
+ struct process_attr_tracker *tracker)
+{
+ int ret;
+ struct lttng_ht_iter iter;
+ struct process_attr_tracker_value_node *value_node;
- tracker_node = caa_container_of(
- node, struct lttng_tracker_list_node, ht_node);
- if (tracker_node->id.type != tracker_key->type) {
- return 0;
+ if (!tracker->inclusion_set_ht) {
+ return;
}
- 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;
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry (tracker->inclusion_set_ht, &iter.iter,
+ value_node, inclusion_set_ht_node) {
+ process_attr_tracker_remove_value_node(tracker, value_node);
}
- return 1;
+ rcu_read_unlock();
+ ret = cds_lfht_destroy(tracker->inclusion_set_ht, NULL);
+ assert(ret == 0);
+ tracker->inclusion_set_ht = NULL;
}
-static unsigned long hash_tracker_key(
- const struct lttng_tracker_id *tracker_key)
+static int process_attr_tracker_create_inclusion_set(
+ struct process_attr_tracker *tracker)
{
- 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;
+ assert(!tracker->inclusion_set_ht);
+ tracker->inclusion_set_ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
+ CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
+ return tracker->inclusion_set_ht ? 0 : -1;
}
-static struct lttng_tracker_id *lttng_tracker_list_lookup(
- const struct lttng_tracker_list *tracker_list,
- const struct lttng_tracker_id *key)
+void process_attr_tracker_destroy(struct process_attr_tracker *tracker)
{
- 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;
+ if (!tracker) {
+ return;
}
- list_node = caa_container_of(
- node, struct lttng_tracker_list_node, ht_node);
- return &list_node->id;
+
+ process_attr_tracker_clear_inclusion_set(tracker);
+ free(tracker);
}
-static void destroy_list_node_rcu(struct rcu_head *head)
+enum lttng_tracking_policy process_attr_tracker_get_tracking_policy(
+ const struct process_attr_tracker *tracker)
{
- struct lttng_tracker_list_node *n = caa_container_of(
- head, struct lttng_tracker_list_node, rcu_head);
-
- free(n->id.string);
- free(n);
+ return tracker->policy;
}
-static void _lttng_tracker_list_remove(struct lttng_tracker_list *tracker_list,
- struct lttng_tracker_list_node *n)
+int process_attr_tracker_set_tracking_policy(
+ struct process_attr_tracker *tracker,
+ enum lttng_tracking_policy tracking_policy)
{
- cds_list_del(&n->list_node);
+ int ret = 0;
- rcu_read_lock();
- cds_lfht_del(tracker_list->ht, &n->ht_node);
- rcu_read_unlock();
+ if (tracker->policy == tracking_policy) {
+ goto end;
+ }
- call_rcu(&n->rcu_head, destroy_list_node_rcu);
+ process_attr_tracker_clear_inclusion_set(tracker);
+ ret = process_attr_tracker_create_inclusion_set(tracker);
+ if (ret) {
+ goto end;
+ }
+ tracker->policy = tracking_policy;
+end:
+ return ret;
}
-static void lttng_tracker_list_reset(struct lttng_tracker_list *tracker_list)
+static int match_inclusion_set_value(
+ struct cds_lfht_node *node, const void *key)
{
- struct lttng_tracker_list_node *n, *t;
+ const struct process_attr_value *value_key = key;
+ const struct process_attr_tracker_value_node *value_node =
+ caa_container_of(node,
+ struct process_attr_tracker_value_node,
+ inclusion_set_ht_node);
- 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;
+ return process_attr_tracker_value_equal(value_node->value, value_key);
}
-/* Protected by session mutex held by caller. */
-int lttng_tracker_list_add(struct lttng_tracker_list *tracker_list,
- const struct lttng_tracker_id *_id)
+static struct process_attr_tracker_value_node *process_attr_tracker_lookup(
+ const struct process_attr_tracker *tracker,
+ const struct process_attr_value *value)
{
- 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;
- }
+ struct cds_lfht_iter iter;
+ struct cds_lfht_node *node;
- cds_list_add_tail(&n->list_node, &tracker_list->list_head);
- tracker_list->state = LTTNG_TRACK_LIST;
+ assert(tracker->policy == LTTNG_TRACKING_POLICY_INCLUDE_SET);
rcu_read_lock();
- cds_lfht_add(tracker_list->ht, hash_tracker_key(&n->id), &n->ht_node);
+ cds_lfht_lookup(tracker->inclusion_set_ht,
+ process_attr_value_hash(value),
+ match_inclusion_set_value, value, &iter);
+ node = cds_lfht_iter_get_node(&iter);
rcu_read_unlock();
- return LTTNG_OK;
-
-error:
- free(n);
- return ret;
+ return node ? container_of(node, struct process_attr_tracker_value_node,
+ inclusion_set_ht_node) :
+ NULL;
}
-/*
- * 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)
+/* Protected by session mutex held by caller. */
+enum process_attr_tracker_status process_attr_tracker_inclusion_set_add_value(
+ struct process_attr_tracker *tracker,
+ const struct process_attr_value *value)
{
- 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;
- }
+ enum process_attr_tracker_status status =
+ PROCESS_ATTR_TRACKER_STATUS_OK;
+ struct process_attr_value *value_copy = NULL;
+ struct process_attr_tracker_value_node *value_node = NULL;
rcu_read_lock();
- id = lttng_tracker_list_lookup(tracker_list, _id);
- if (!id) {
- ret = LTTNG_ERR_ID_NOT_TRACKED;
- goto rcu_unlock;
+ if (tracker->policy != LTTNG_TRACKING_POLICY_INCLUDE_SET) {
+ status = PROCESS_ATTR_TRACKER_STATUS_INVALID_TRACKING_POLICY;
+ goto end;
}
- 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;
+ if (process_attr_tracker_lookup(tracker, value)) {
+ status = PROCESS_ATTR_TRACKER_STATUS_EXISTS;
+ goto end;
}
- 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;
+ value_node = zmalloc(sizeof(*value_node));
+ if (!value_node) {
+ status = PROCESS_ATTR_TRACKER_STATUS_ERROR;
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;
+ value_copy = process_attr_value_copy(value);
+ if (!value_copy) {
+ status = PROCESS_ATTR_TRACKER_STATUS_ERROR;
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;
- }
+
+ value_node->value = value_copy;
+ cds_lfht_add(tracker->inclusion_set_ht,
+ process_attr_value_hash(value_copy),
+ &value_node->inclusion_set_ht_node);
+ value_copy = NULL;
+ value_node = NULL;
+end:
+ if (value_copy) {
+ process_attr_value_destroy(value_copy);
}
-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;
+ if (value_node) {
+ free(value_node);
}
-end:
- free(buf);
- return retval;
+ rcu_read_unlock();
+ return status;
}
-int lttng_tracker_id_lookup_string(enum lttng_tracker_type tracker_type,
- const struct lttng_tracker_id *id,
- int *result)
+/* Protected by session mutex held by caller. */
+enum process_attr_tracker_status
+process_attr_tracker_inclusion_set_remove_value(
+ struct process_attr_tracker *tracker,
+ const struct process_attr_value *value)
{
- 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;
- }
-}
+ struct process_attr_tracker_value_node *value_node;
+ enum process_attr_tracker_status status =
+ PROCESS_ATTR_TRACKER_STATUS_OK;
-/*
- * 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;
+ rcu_read_lock();
+ if (tracker->policy != LTTNG_TRACKING_POLICY_INCLUDE_SET) {
+ status = PROCESS_ATTR_TRACKER_STATUS_INVALID_TRACKING_POLICY;
+ goto end;
}
-end:
- return retval;
-error:
- for (i = 0; i < count; i++) {
- free(ids[i].string);
+ value_node = process_attr_tracker_lookup(tracker, value);
+ if (!value_node) {
+ status = PROCESS_ATTR_TRACKER_STATUS_MISSING;
+ goto end;
}
- free(ids);
- return retval;
+
+ process_attr_tracker_remove_value_node(tracker, value_node);
+end:
+ rcu_read_unlock();
+ return status;
}
-int lttng_tracker_id_set_list(struct lttng_tracker_list *tracker_list,
- struct lttng_tracker_id *_ids,
- size_t count)
+enum process_attr_tracker_status process_attr_tracker_get_inclusion_set(
+ const struct process_attr_tracker *tracker,
+ struct lttng_process_attr_values **_values)
{
- size_t i;
-
- lttng_tracker_list_reset(tracker_list);
- if (count == 1 && _ids[0].type == LTTNG_ID_ALL) {
- /* Track all. */
- return LTTNG_OK;
+ struct lttng_ht_iter iter;
+ struct process_attr_tracker_value_node *value_node;
+ enum process_attr_tracker_status status =
+ PROCESS_ATTR_TRACKER_STATUS_OK;
+ struct lttng_process_attr_values *values;
+ struct process_attr_value *new_value = NULL;
+
+ values = lttng_process_attr_values_create();
+ if (!values) {
+ status = PROCESS_ATTR_TRACKER_STATUS_ERROR;
+ goto error;
}
- if (count == 0) {
- /* Set state to "track none". */
- tracker_list->state = LTTNG_TRACK_NONE;
- return LTTNG_OK;
+
+ if (tracker->policy != LTTNG_TRACKING_POLICY_INCLUDE_SET) {
+ status = PROCESS_ATTR_TRACKER_STATUS_INVALID_TRACKING_POLICY;
+ goto error;
}
- for (i = 0; i < count; i++) {
- struct lttng_tracker_id *id = &_ids[i];
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry (tracker->inclusion_set_ht, &iter.iter,
+ value_node, inclusion_set_ht_node) {
int ret;
- ret = lttng_tracker_list_add(tracker_list, id);
- if (ret != LTTNG_OK) {
- return ret;
+ new_value = process_attr_value_copy(value_node->value);
+ if (!new_value) {
+ status = PROCESS_ATTR_TRACKER_STATUS_ERROR;
+ goto error_unlock;
+ }
+
+ ret = lttng_dynamic_pointer_array_add_pointer(
+ &values->array, new_value);
+ if (ret) {
+ status = PROCESS_ATTR_TRACKER_STATUS_ERROR;
+ goto error_unlock;
}
+
+ new_value = NULL;
}
- return LTTNG_OK;
+ rcu_read_unlock();
+ *_values = values;
+ return status;
+error_unlock:
+ rcu_read_unlock();
+error:
+ lttng_process_attr_values_destroy(values);
+ process_attr_value_destroy(new_value);
+ return status;
}