#include <lttng/condition/buffer-usage-internal.h>
#include <lttng/condition/session-consumed-size-internal.h>
#include <lttng/condition/session-rotation-internal.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/domain-internal.h>
#include <lttng/notification/channel-internal.h>
+#include <lttng/trigger/trigger-internal.h>
+#include <lttng/event-rule/event-rule-internal.h>
#include <time.h>
#include <unistd.h>
#include <inttypes.h>
#include <fcntl.h>
+#include "condition-internal.h"
#include "notification-thread.h"
#include "notification-thread-events.h"
#include "notification-thread-commands.h"
struct lttng_trigger_ht_element {
struct lttng_trigger *trigger;
struct cds_lfht_node node;
+ struct cds_lfht_node node_by_name_uid;
/* call_rcu delayed reclaim. */
struct rcu_head rcu_node;
};
struct cds_list_head node;
};
+/*
+ * Facilities to carry the different notifications type in the action processing
+ * code path.
+ */
+struct lttng_event_notifier_notification {
+ union {
+ struct lttng_ust_event_notifier_notification *ust;
+ struct lttng_kernel_event_notifier_notification *kernel;
+ } notification;
+ uint64_t token;
+ enum lttng_domain_type type;
+};
+
struct channel_state_sample {
struct channel_key key;
struct cds_lfht_node channel_state_ht_node;
enum client_transmission_status transmission_status,
struct notification_thread_state *state);
+static
+void free_lttng_trigger_ht_element_rcu(struct rcu_head *node);
+
static
int match_client_socket(struct cds_lfht_node *node, const void *key)
{
}
static
-int match_condition(struct cds_lfht_node *node, const void *key)
+int match_trigger(struct cds_lfht_node *node, const void *key)
{
- struct lttng_condition *condition_key = (struct lttng_condition *) key;
- struct lttng_trigger_ht_element *trigger;
- struct lttng_condition *condition;
+ struct lttng_trigger *trigger_key = (struct lttng_trigger *) key;
+ struct lttng_trigger_ht_element *trigger_ht_element;
- trigger = caa_container_of(node, struct lttng_trigger_ht_element,
+ trigger_ht_element = caa_container_of(node, struct lttng_trigger_ht_element,
node);
- condition = lttng_trigger_get_condition(trigger->trigger);
- assert(condition);
- return !!lttng_condition_is_equal(condition_key, condition);
+ return !!lttng_trigger_is_equal(trigger_key, trigger_ht_element->trigger);
+}
+
+static
+int match_trigger_token(struct cds_lfht_node *node, const void *key)
+{
+ const uint64_t *_key = key;
+ struct notification_trigger_tokens_ht_element *element;
+
+ element = caa_container_of(node,
+ struct notification_trigger_tokens_ht_element, node);
+ return *_key == element->token;
}
static
}
static
-unsigned long lttng_condition_buffer_usage_hash(
- const struct lttng_condition *_condition)
+const char *notification_command_type_str(
+ enum notification_thread_command_type type)
{
- unsigned long hash;
- unsigned long condition_type;
- struct lttng_condition_buffer_usage *condition;
-
- condition = container_of(_condition,
- struct lttng_condition_buffer_usage, parent);
-
- condition_type = (unsigned long) condition->parent.type;
- hash = hash_key_ulong((void *) condition_type, lttng_ht_seed);
- if (condition->session_name) {
- hash ^= hash_key_str(condition->session_name, lttng_ht_seed);
- }
- if (condition->channel_name) {
- hash ^= hash_key_str(condition->channel_name, lttng_ht_seed);
- }
- if (condition->domain.set) {
- hash ^= hash_key_ulong(
- (void *) condition->domain.type,
- lttng_ht_seed);
- }
- if (condition->threshold_ratio.set) {
- uint64_t val;
-
- val = condition->threshold_ratio.value * (double) UINT32_MAX;
- hash ^= hash_key_u64(&val, lttng_ht_seed);
- } else if (condition->threshold_bytes.set) {
- uint64_t val;
-
- val = condition->threshold_bytes.value;
- hash ^= hash_key_u64(&val, lttng_ht_seed);
+ switch (type) {
+ case NOTIFICATION_COMMAND_TYPE_REGISTER_TRIGGER:
+ return "REGISTER_TRIGGER";
+ case NOTIFICATION_COMMAND_TYPE_UNREGISTER_TRIGGER:
+ return "UNREGISTER_TRIGGER";
+ case NOTIFICATION_COMMAND_TYPE_ADD_CHANNEL:
+ return "ADD_CHANNEL";
+ case NOTIFICATION_COMMAND_TYPE_REMOVE_CHANNEL:
+ return "REMOVE_CHANNEL";
+ case NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_ONGOING:
+ return "SESSION_ROTATION_ONGOING";
+ case NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_COMPLETED:
+ return "SESSION_ROTATION_COMPLETED";
+ case NOTIFICATION_COMMAND_TYPE_ADD_TRACER_EVENT_SOURCE:
+ return "ADD_TRACER_EVENT_SOURCE";
+ case NOTIFICATION_COMMAND_TYPE_REMOVE_TRACER_EVENT_SOURCE:
+ return "REMOVE_TRACER_EVENT_SOURCE";
+ case NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS:
+ return "LIST_TRIGGERS";
+ case NOTIFICATION_COMMAND_TYPE_QUIT:
+ return "QUIT";
+ case NOTIFICATION_COMMAND_TYPE_CLIENT_COMMUNICATION_UPDATE:
+ return "CLIENT_COMMUNICATION_UPDATE";
+ default:
+ abort();
}
- return hash;
}
+/*
+ * Match trigger based on name and credentials only.
+ * Name duplication is NOT allowed for the same uid.
+ */
static
-unsigned long lttng_condition_session_consumed_size_hash(
- const struct lttng_condition *_condition)
+int match_trigger_by_name_uid(struct cds_lfht_node *node,
+ const void *key)
{
- unsigned long hash;
- unsigned long condition_type =
- (unsigned long) LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE;
- struct lttng_condition_session_consumed_size *condition;
- uint64_t val;
-
- condition = container_of(_condition,
- struct lttng_condition_session_consumed_size, parent);
-
- hash = hash_key_ulong((void *) condition_type, lttng_ht_seed);
- if (condition->session_name) {
- hash ^= hash_key_str(condition->session_name, lttng_ht_seed);
+ bool match = false;
+ const char *name;
+ const char *key_name;
+ enum lttng_trigger_status status;
+ const struct lttng_credentials *key_creds;
+ const struct lttng_credentials *node_creds;
+ const struct lttng_trigger *trigger_key =
+ (const struct lttng_trigger *) key;
+ const struct lttng_trigger_ht_element *trigger_ht_element =
+ caa_container_of(node,
+ struct lttng_trigger_ht_element,
+ node_by_name_uid);
+
+ status = lttng_trigger_get_name(trigger_ht_element->trigger, &name);
+ assert(status == LTTNG_TRIGGER_STATUS_OK);
+
+ status = lttng_trigger_get_name(trigger_key, &key_name);
+ assert(status == LTTNG_TRIGGER_STATUS_OK);
+
+ /* Compare the names. */
+ if (strcmp(name, key_name) != 0) {
+ goto end;
}
- val = condition->consumed_threshold_bytes.value;
- hash ^= hash_key_u64(&val, lttng_ht_seed);
- return hash;
-}
-static
-unsigned long lttng_condition_session_rotation_hash(
- const struct lttng_condition *_condition)
-{
- unsigned long hash, condition_type;
- struct lttng_condition_session_rotation *condition;
+ /* Compare the owners' UIDs. */
+ key_creds = lttng_trigger_get_credentials(trigger_key);
+ node_creds = lttng_trigger_get_credentials(trigger_ht_element->trigger);
- condition = container_of(_condition,
- struct lttng_condition_session_rotation, parent);
- condition_type = (unsigned long) condition->parent.type;
- hash = hash_key_ulong((void *) condition_type, lttng_ht_seed);
- assert(condition->session_name);
- hash ^= hash_key_str(condition->session_name, lttng_ht_seed);
- return hash;
+ match = lttng_credentials_is_equal_uid(key_creds, node_creds);
+
+end:
+ return match;
}
/*
- * The lttng_condition hashing code is kept in this file (rather than
- * condition.c) since it makes use of GPLv2 code (hashtable utils), which we
- * don't want to link in liblttng-ctl.
+ * Hash trigger based on name and credentials only.
*/
static
-unsigned long lttng_condition_hash(const struct lttng_condition *condition)
+unsigned long hash_trigger_by_name_uid(const struct lttng_trigger *trigger)
{
- switch (condition->type) {
- case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
- case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
- return lttng_condition_buffer_usage_hash(condition);
- case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
- return lttng_condition_session_consumed_size_hash(condition);
- case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
- case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
- return lttng_condition_session_rotation_hash(condition);
- default:
- ERR("[notification-thread] Unexpected condition type caught");
- abort();
+ unsigned long hash = 0;
+ const struct lttng_credentials *trigger_creds;
+ const char *trigger_name;
+ enum lttng_trigger_status status;
+
+ status = lttng_trigger_get_name(trigger, &trigger_name);
+ if (status == LTTNG_TRIGGER_STATUS_OK) {
+ hash = hash_key_str(trigger_name, lttng_ht_seed);
}
+
+ trigger_creds = lttng_trigger_get_credentials(trigger);
+ hash ^= hash_key_ulong((void *) (unsigned long) LTTNG_OPTIONAL_GET(trigger_creds->uid),
+ lttng_ht_seed);
+
+ return hash;
}
static
case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
return LTTNG_OBJECT_TYPE_SESSION;
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ return LTTNG_OBJECT_TYPE_NONE;
default:
return LTTNG_OBJECT_TYPE_UNKNOWN;
}
&evaluation, &object_uid, &object_gid);
break;
case LTTNG_OBJECT_TYPE_NONE:
+ DBG("[notification-thread] Newly subscribed-to condition not bound to object, nothing to evaluate");
ret = 0;
goto end;
case LTTNG_OBJECT_TYPE_UNKNOWN:
}
static
-int condition_is_supported(struct lttng_condition *condition)
+int handle_notification_thread_command_add_tracer_event_source(
+ struct notification_thread_state *state,
+ int tracer_event_source_fd,
+ enum lttng_domain_type domain_type,
+ enum lttng_error_code *_cmd_result)
+{
+ int ret = 0;
+ enum lttng_error_code cmd_result = LTTNG_OK;
+ struct notification_event_tracer_event_source_element *element = NULL;
+
+ element = zmalloc(sizeof(*element));
+ if (!element) {
+ cmd_result = LTTNG_ERR_NOMEM;
+ ret = -1;
+ goto end;
+ }
+
+ element->fd = tracer_event_source_fd;
+ element->domain = domain_type;
+
+ cds_list_add(&element->node, &state->tracer_event_sources_list);
+
+ DBG3("[notification-thread] Adding tracer event source fd to poll set: tracer_event_source_fd = %d, domain = '%s'",
+ tracer_event_source_fd,
+ lttng_domain_type_str(domain_type));
+
+ /* Adding the read side pipe to the event poll. */
+ ret = lttng_poll_add(&state->events, tracer_event_source_fd, LPOLLIN | LPOLLERR);
+ if (ret < 0) {
+ ERR("[notification-thread] Failed to add tracer event source to poll set: tracer_event_source_fd = %d, domain = '%s'",
+ tracer_event_source_fd,
+ lttng_domain_type_str(element->domain));
+ cds_list_del(&element->node);
+ free(element);
+ goto end;
+ }
+
+ element->is_fd_in_poll_set = true;
+
+end:
+ *_cmd_result = cmd_result;
+ return ret;
+}
+
+static
+int handle_notification_thread_command_remove_tracer_event_source(
+ struct notification_thread_state *state,
+ int tracer_event_source_fd,
+ enum lttng_error_code *_cmd_result)
+{
+ int ret = 0;
+ bool found = false;
+ enum lttng_error_code cmd_result = LTTNG_OK;
+ struct notification_event_tracer_event_source_element *source_element = NULL, *tmp;
+
+ cds_list_for_each_entry_safe(source_element, tmp,
+ &state->tracer_event_sources_list, node) {
+ if (source_element->fd != tracer_event_source_fd) {
+ continue;
+ }
+
+ DBG("[notification-thread] Removed tracer event source from poll set: tracer_event_source_fd = %d, domain = '%s'",
+ tracer_event_source_fd,
+ lttng_domain_type_str(source_element->domain));
+ cds_list_del(&source_element->node);
+ found = true;
+ break;
+ }
+
+ if (!found) {
+ /*
+ * This is temporarily allowed since the poll activity set is
+ * not properly cleaned-up for the moment. This is adressed in
+ * an upcoming fix.
+ */
+ source_element = NULL;
+ goto end;
+ }
+
+ if (!source_element->is_fd_in_poll_set) {
+ /* Skip the poll set removal. */
+ goto end;
+ }
+
+ DBG3("[notification-thread] Removing tracer event source from poll set: tracer_event_source_fd = %d, domain = '%s'",
+ tracer_event_source_fd,
+ lttng_domain_type_str(source_element->domain));
+
+ /* Removing the fd from the event poll set. */
+ ret = lttng_poll_del(&state->events, tracer_event_source_fd);
+ if (ret < 0) {
+ ERR("[notification-thread] Failed to remove tracer event source from poll set: tracer_event_source_fd = %d, domain = '%s'",
+ tracer_event_source_fd,
+ lttng_domain_type_str(source_element->domain));
+ cmd_result = LTTNG_ERR_FATAL;
+ goto end;
+ }
+
+ source_element->is_fd_in_poll_set = false;
+
+end:
+ free(source_element);
+ *_cmd_result = cmd_result;
+ return ret;
+}
+
+int handle_notification_thread_remove_tracer_event_source_no_result(
+ struct notification_thread_state *state,
+ int tracer_event_source_fd)
{
int ret;
+ enum lttng_error_code cmd_result;
+
+ ret = handle_notification_thread_command_remove_tracer_event_source(
+ state, tracer_event_source_fd, &cmd_result);
+ (void) cmd_result;
+ return ret;
+}
+
+static int handle_notification_thread_command_list_triggers(
+ struct notification_thread_handle *handle,
+ struct notification_thread_state *state,
+ uid_t client_uid,
+ struct lttng_triggers **triggers,
+ enum lttng_error_code *_cmd_result)
+{
+ int ret = 0;
+ enum lttng_error_code cmd_result = LTTNG_OK;
+ struct cds_lfht_iter iter;
+ struct lttng_trigger_ht_element *trigger_ht_element;
+ struct lttng_triggers *local_triggers = NULL;
+ const struct lttng_credentials *creds;
+
+ rcu_read_lock();
+
+ local_triggers = lttng_triggers_create();
+ if (!local_triggers) {
+ /* Not a fatal error. */
+ cmd_result = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ cds_lfht_for_each_entry(state->triggers_ht, &iter,
+ trigger_ht_element, node) {
+ /*
+ * Only return the triggers to which the client has access.
+ * The root user has visibility over all triggers.
+ */
+ creds = lttng_trigger_get_credentials(trigger_ht_element->trigger);
+ if (client_uid != lttng_credentials_get_uid(creds) && client_uid != 0) {
+ continue;
+ }
+
+ ret = lttng_triggers_add(local_triggers,
+ trigger_ht_element->trigger);
+ if (ret < 0) {
+ /* Not a fatal error. */
+ ret = 0;
+ cmd_result = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+ }
+
+ /* Transferring ownership to the caller. */
+ *triggers = local_triggers;
+ local_triggers = NULL;
+
+end:
+ rcu_read_unlock();
+ lttng_triggers_destroy(local_triggers);
+ *_cmd_result = cmd_result;
+ return ret;
+}
+
+static
+bool condition_is_supported(struct lttng_condition *condition)
+{
+ bool is_supported;
switch (lttng_condition_get_type(condition)) {
case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
{
+ int ret;
enum lttng_domain_type domain;
ret = lttng_condition_buffer_usage_get_domain_type(condition,
&domain);
- if (ret) {
- ret = -1;
- goto end;
- }
+ assert(ret == 0);
if (domain != LTTNG_DOMAIN_KERNEL) {
- ret = 1;
+ is_supported = true;
goto end;
}
* Older kernel tracers don't expose the API to monitor their
* buffers. Therefore, we reject triggers that require that
* mechanism to be available to be evaluated.
+ *
+ * Assume unsupported on error.
+ */
+ is_supported = kernel_supports_ring_buffer_snapshot_sample_positions() == 1;
+ break;
+ }
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ {
+ const struct lttng_event_rule *event_rule;
+ enum lttng_domain_type domain;
+ const enum lttng_condition_status status =
+ lttng_condition_event_rule_get_rule(
+ condition, &event_rule);
+
+ assert(status == LTTNG_CONDITION_STATUS_OK);
+
+ domain = lttng_event_rule_get_domain_type(event_rule);
+ if (domain != LTTNG_DOMAIN_KERNEL) {
+ is_supported = true;
+ goto end;
+ }
+
+ /*
+ * Older kernel tracers can't emit notification. Therefore, we
+ * reject triggers that require that mechanism to be available
+ * to be evaluated.
+ *
+ * Assume unsupported on error.
*/
- ret = kernel_supports_ring_buffer_snapshot_sample_positions();
+ is_supported = kernel_supports_event_notifiers() == 1;
break;
}
default:
- ret = 1;
+ is_supported = true;
}
end:
- return ret;
+ return is_supported;
}
/* Must be called with RCU read lock held. */
return is_notify;
}
+static bool trigger_name_taken(struct notification_thread_state *state,
+ const struct lttng_trigger *trigger)
+{
+ struct cds_lfht_iter iter;
+
+ /*
+ * No duplicata is allowed in the triggers_by_name_uid_ht.
+ * The match is done against the trigger name and uid.
+ */
+ cds_lfht_lookup(state->triggers_by_name_uid_ht,
+ hash_trigger_by_name_uid(trigger),
+ match_trigger_by_name_uid,
+ trigger,
+ &iter);
+ return !!cds_lfht_iter_get_node(&iter);
+}
+
+static
+enum lttng_error_code generate_trigger_name(
+ struct notification_thread_state *state,
+ struct lttng_trigger *trigger, const char **name)
+{
+ enum lttng_error_code ret_code = LTTNG_OK;
+ bool taken = false;
+ enum lttng_trigger_status status;
+
+ do {
+ const int ret = lttng_trigger_generate_name(trigger,
+ state->trigger_id.name_offset++);
+ if (ret) {
+ /* The only reason this can fail right now. */
+ ret_code = LTTNG_ERR_NOMEM;
+ break;
+ }
+
+ status = lttng_trigger_get_name(trigger, name);
+ assert(status == LTTNG_TRIGGER_STATUS_OK);
+
+ taken = trigger_name_taken(state, trigger);
+ } while (taken || state->trigger_id.name_offset == UINT64_MAX);
+
+ return ret_code;
+}
+
/*
* FIXME A client's credentials are not checked when registering a trigger.
*
struct notification_client_list *client_list = NULL;
struct lttng_trigger_ht_element *trigger_ht_element = NULL;
struct notification_client_list_element *client_list_element;
+ struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element = NULL;
struct cds_lfht_node *node;
struct cds_lfht_iter iter;
+ const char* trigger_name;
bool free_trigger = true;
struct lttng_evaluation *evaluation = NULL;
struct lttng_credentials object_creds;
uid_t object_uid;
gid_t object_gid;
enum action_executor_status executor_status;
+ const uint64_t trigger_tracer_token =
+ state->trigger_id.next_tracer_token++;
rcu_read_lock();
+ /* Set the trigger's tracer token. */
+ lttng_trigger_set_tracer_token(trigger, trigger_tracer_token);
+
+ if (lttng_trigger_get_name(trigger, &trigger_name) ==
+ LTTNG_TRIGGER_STATUS_UNSET) {
+ const enum lttng_error_code ret_code = generate_trigger_name(
+ state, trigger, &trigger_name);
+
+ if (ret_code != LTTNG_OK) {
+ /* Fatal error. */
+ ret = -1;
+ *cmd_result = ret_code;
+ goto error;
+ }
+ } else if (trigger_name_taken(state, trigger)) {
+ /* Not a fatal error. */
+ *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+ ret = 0;
+ goto error;
+ }
+
condition = lttng_trigger_get_condition(trigger);
assert(condition);
- ret = condition_is_supported(condition);
- if (ret < 0) {
- goto error;
- } else if (ret == 0) {
+ /* Some conditions require tracers to implement a minimal ABI version. */
+ if (!condition_is_supported(condition)) {
*cmd_result = LTTNG_ERR_NOT_SUPPORTED;
goto error;
- } else {
- /* Feature is supported, continue. */
- ret = 0;
}
trigger_ht_element = zmalloc(sizeof(*trigger_ht_element));
/* Add trigger to the trigger_ht. */
cds_lfht_node_init(&trigger_ht_element->node);
+ cds_lfht_node_init(&trigger_ht_element->node_by_name_uid);
trigger_ht_element->trigger = trigger;
node = cds_lfht_add_unique(state->triggers_ht,
lttng_condition_hash(condition),
- match_condition,
- condition,
+ match_trigger,
+ trigger,
&trigger_ht_element->node);
if (node != &trigger_ht_element->node) {
/* Not a fatal error, simply report it to the client. */
goto error_free_ht_element;
}
+ node = cds_lfht_add_unique(state->triggers_by_name_uid_ht,
+ hash_trigger_by_name_uid(trigger),
+ match_trigger_by_name_uid,
+ trigger,
+ &trigger_ht_element->node_by_name_uid);
+ if (node != &trigger_ht_element->node_by_name_uid) {
+ /* Not a fatal error, simply report it to the client. */
+ cds_lfht_del(state->triggers_ht, &trigger_ht_element->node);
+ *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+ goto error_free_ht_element;
+ }
+
+ if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
+ trigger_tokens_ht_element = zmalloc(sizeof(*trigger_tokens_ht_element));
+ if (!trigger_tokens_ht_element) {
+ /* Fatal error. */
+ ret = -1;
+ cds_lfht_del(state->triggers_ht,
+ &trigger_ht_element->node);
+ cds_lfht_del(state->triggers_by_name_uid_ht,
+ &trigger_ht_element->node_by_name_uid);
+ goto error_free_ht_element;
+ }
+
+ /* Add trigger token to the trigger_tokens_ht. */
+ cds_lfht_node_init(&trigger_tokens_ht_element->node);
+ trigger_tokens_ht_element->token =
+ LTTNG_OPTIONAL_GET(trigger->tracer_token);
+ trigger_tokens_ht_element->trigger = trigger;
+
+ node = cds_lfht_add_unique(state->trigger_tokens_ht,
+ hash_key_u64(&trigger_tokens_ht_element->token,
+ lttng_ht_seed),
+ match_trigger_token,
+ &trigger_tokens_ht_element->token,
+ &trigger_tokens_ht_element->node);
+ if (node != &trigger_tokens_ht_element->node) {
+ /* Internal corruption, fatal error. */
+ ret = -1;
+ *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+ cds_lfht_del(state->triggers_ht,
+ &trigger_ht_element->node);
+ cds_lfht_del(state->triggers_by_name_uid_ht,
+ &trigger_ht_element->node_by_name_uid);
+ goto error_free_ht_element;
+ }
+ }
+
/*
* Ownership of the trigger and of its wrapper was transfered to
- * the triggers_ht.
+ * the triggers_ht. Same for token ht element if necessary.
*/
+ trigger_tokens_ht_element = NULL;
trigger_ht_element = NULL;
free_trigger = false;
break;
case LTTNG_OBJECT_TYPE_NONE:
ret = 0;
- goto error_put_client_list;
+ break;
case LTTNG_OBJECT_TYPE_UNKNOWN:
default:
ret = -1;
- goto error_put_client_list;
+ break;
}
if (ret) {
if (!evaluation) {
/* Evaluation yielded nothing. Normal exit. */
ret = 0;
- goto error_put_client_list;
+ goto end;
}
/*
*/
WARN("No space left when enqueuing action associated to newly registered trigger");
ret = 0;
- goto error_put_client_list;
+ goto end;
default:
abort();
}
+end:
*cmd_result = LTTNG_OK;
+ DBG("Registered trigger: name = `%s`, tracer token = %" PRIu64,
+ trigger_name, trigger_tracer_token);
error_put_client_list:
notification_client_list_put(client_list);
error_free_ht_element:
- free(trigger_ht_element);
+ if (trigger_ht_element) {
+ /* Delayed removal due to RCU constraint on delete. */
+ call_rcu(&trigger_ht_element->rcu_node,
+ free_lttng_trigger_ht_element_rcu);
+ }
+
+ free(trigger_tokens_ht_element);
error:
if (free_trigger) {
lttng_trigger_destroy(trigger);
rcu_node));
}
+static
+void free_notification_trigger_tokens_ht_element_rcu(struct rcu_head *node)
+{
+ free(caa_container_of(node, struct notification_trigger_tokens_ht_element,
+ rcu_node));
+}
+
static
int handle_notification_thread_command_unregister_trigger(
struct notification_thread_state *state,
- struct lttng_trigger *trigger,
+ const struct lttng_trigger *trigger,
enum lttng_error_code *_cmd_reply)
{
struct cds_lfht_iter iter;
struct lttng_channel_trigger_list *trigger_list;
struct notification_client_list *client_list;
struct lttng_trigger_ht_element *trigger_ht_element = NULL;
- struct lttng_condition *condition = lttng_trigger_get_condition(
+ const struct lttng_condition *condition = lttng_trigger_get_const_condition(
trigger);
enum lttng_error_code cmd_reply;
cds_lfht_lookup(state->triggers_ht,
lttng_condition_hash(condition),
- match_condition,
- condition,
+ match_trigger,
+ trigger,
&iter);
triggers_ht_node = cds_lfht_iter_get_node(&iter);
if (!triggers_ht_node) {
cds_list_for_each_entry_safe(trigger_element, tmp,
&trigger_list->list, node) {
- const struct lttng_condition *current_condition =
- lttng_trigger_get_const_condition(
- trigger_element->trigger);
-
- assert(current_condition);
- if (!lttng_condition_is_equal(condition,
- current_condition)) {
+ if (!lttng_trigger_is_equal(trigger, trigger_element->trigger)) {
continue;
}
}
}
- /*
- * Remove and release the client list from
- * notification_trigger_clients_ht.
- */
- client_list = get_client_list_from_condition(state, condition);
- assert(client_list);
+ if (lttng_condition_get_type(condition) ==
+ LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
+ struct notification_trigger_tokens_ht_element
+ *trigger_tokens_ht_element;
- /* Put new reference and the hashtable's reference. */
- notification_client_list_put(client_list);
- notification_client_list_put(client_list);
- client_list = NULL;
+ cds_lfht_for_each_entry (state->trigger_tokens_ht, &iter,
+ trigger_tokens_ht_element, node) {
+ if (!lttng_trigger_is_equal(trigger,
+ trigger_tokens_ht_element->trigger)) {
+ continue;
+ }
+
+ DBG("[notification-thread] Removed trigger from tokens_ht");
+ cds_lfht_del(state->trigger_tokens_ht,
+ &trigger_tokens_ht_element->node);
+ call_rcu(&trigger_tokens_ht_element->rcu_node,
+ free_notification_trigger_tokens_ht_element_rcu);
+
+ break;
+ }
+ }
+
+ if (is_trigger_action_notify(trigger)) {
+ /*
+ * Remove and release the client list from
+ * notification_trigger_clients_ht.
+ */
+ client_list = get_client_list_from_condition(state, condition);
+ assert(client_list);
+
+ /* Put new reference and the hashtable's reference. */
+ notification_client_list_put(client_list);
+ notification_client_list_put(client_list);
+ client_list = NULL;
+ }
/* Remove trigger from triggers_ht. */
trigger_ht_element = caa_container_of(triggers_ht_node,
struct lttng_trigger_ht_element, node);
+ cds_lfht_del(state->triggers_by_name_uid_ht, &trigger_ht_element->node_by_name_uid);
cds_lfht_del(state->triggers_ht, triggers_ht_node);
/* Release the ownership of the trigger. */
struct notification_thread_command, cmd_list_node);
cds_list_del(&cmd->cmd_list_node);
pthread_mutex_unlock(&handle->cmd_queue.lock);
+
+ DBG("[notification-thread] Received `%s` command",
+ notification_command_type_str(cmd->type));
switch (cmd->type) {
case NOTIFICATION_COMMAND_TYPE_REGISTER_TRIGGER:
- DBG("[notification-thread] Received register trigger command");
- ret = handle_notification_thread_command_register_trigger(
- state, cmd->parameters.trigger,
+ ret = handle_notification_thread_command_register_trigger(state,
+ cmd->parameters.register_trigger.trigger,
&cmd->reply_code);
break;
case NOTIFICATION_COMMAND_TYPE_UNREGISTER_TRIGGER:
- DBG("[notification-thread] Received unregister trigger command");
ret = handle_notification_thread_command_unregister_trigger(
- state, cmd->parameters.trigger,
+ state,
+ cmd->parameters.unregister_trigger.trigger,
&cmd->reply_code);
break;
case NOTIFICATION_COMMAND_TYPE_ADD_CHANNEL:
- DBG("[notification-thread] Received add channel command");
ret = handle_notification_thread_command_add_channel(
state,
cmd->parameters.add_channel.session.name,
&cmd->reply_code);
break;
case NOTIFICATION_COMMAND_TYPE_REMOVE_CHANNEL:
- DBG("[notification-thread] Received remove channel command");
ret = handle_notification_thread_command_remove_channel(
state, cmd->parameters.remove_channel.key,
cmd->parameters.remove_channel.domain,
break;
case NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_ONGOING:
case NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_COMPLETED:
- DBG("[notification-thread] Received session rotation %s command",
- cmd->type == NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_ONGOING ?
- "ongoing" : "completed");
ret = handle_notification_thread_command_session_rotation(
state,
cmd->type,
cmd->parameters.session_rotation.location,
&cmd->reply_code);
break;
+ case NOTIFICATION_COMMAND_TYPE_ADD_TRACER_EVENT_SOURCE:
+ ret = handle_notification_thread_command_add_tracer_event_source(
+ state,
+ cmd->parameters.tracer_event_source.tracer_event_source_fd,
+ cmd->parameters.tracer_event_source.domain,
+ &cmd->reply_code);
+ break;
+ case NOTIFICATION_COMMAND_TYPE_REMOVE_TRACER_EVENT_SOURCE:
+ ret = handle_notification_thread_command_remove_tracer_event_source(
+ state,
+ cmd->parameters.tracer_event_source.tracer_event_source_fd,
+ &cmd->reply_code);
+ break;
+ case NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS:
+ {
+ struct lttng_triggers *triggers = NULL;
+
+ ret = handle_notification_thread_command_list_triggers(
+ handle,
+ state,
+ cmd->parameters.list_triggers.uid,
+ &triggers,
+ &cmd->reply_code);
+ cmd->reply.list_triggers.triggers = triggers;
+ ret = 0;
+ break;
+ }
case NOTIFICATION_COMMAND_TYPE_QUIT:
- DBG("[notification-thread] Received quit command");
cmd->reply_code = LTTNG_OK;
ret = 1;
goto end;
return ret;
}
+int handle_notification_thread_event_notification(struct notification_thread_state *state,
+ int notification_pipe_read_fd,
+ enum lttng_domain_type domain)
+{
+ int ret;
+ struct lttng_ust_event_notifier_notification ust_notification;
+ struct lttng_kernel_event_notifier_notification kernel_notification;
+ struct cds_lfht_node *node;
+ struct cds_lfht_iter iter;
+ struct notification_trigger_tokens_ht_element *element;
+ enum lttng_action_type action_type;
+ const struct lttng_action *action;
+ struct lttng_event_notifier_notification notification;
+ void *reception_buffer;
+ size_t reception_size;
+
+ notification.type = domain;
+
+ switch(domain) {
+ case LTTNG_DOMAIN_UST:
+ reception_buffer = (void *) &ust_notification;
+ reception_size = sizeof(ust_notification);
+ notification.notification.ust = &ust_notification;
+ break;
+ case LTTNG_DOMAIN_KERNEL:
+ reception_buffer = (void *) &kernel_notification;
+ reception_size = sizeof(kernel_notification);
+ notification.notification.kernel = &kernel_notification;
+ break;
+ default:
+ abort();
+ }
+
+ /*
+ * The monitoring pipe only holds messages smaller than PIPE_BUF,
+ * ensuring that read/write of tracer notifications are atomic.
+ */
+ ret = lttng_read(notification_pipe_read_fd, reception_buffer,
+ reception_size);
+ if (ret != reception_size) {
+ PERROR("Failed to read from event source notification pipe: fd = %d, size to read = %zu, ret = %d",
+ notification_pipe_read_fd, reception_size, ret);
+ ret = -1;
+ goto end;
+ }
+
+ switch(domain) {
+ case LTTNG_DOMAIN_UST:
+ notification.token = ust_notification.token;
+ break;
+ case LTTNG_DOMAIN_KERNEL:
+ notification.token = kernel_notification.token;
+ break;
+ default:
+ abort();
+ }
+
+ /* Find triggers associated with this token. */
+ rcu_read_lock();
+ cds_lfht_lookup(state->trigger_tokens_ht,
+ hash_key_u64(¬ification.token, lttng_ht_seed),
+ match_trigger_token, ¬ification.token, &iter);
+ node = cds_lfht_iter_get_node(&iter);
+ if (caa_likely(!node)) {
+ /*
+ * This is not an error, slow consumption of the pipe can lead
+ * to situations where a trigger is removed but we still get
+ * tracer notification matching to a previous trigger.
+ */
+ ret = 0;
+ goto end_unlock;
+ }
+
+ element = caa_container_of(node,
+ struct notification_trigger_tokens_ht_element,
+ node);
+
+ action = lttng_trigger_get_const_action(element->trigger);
+ action_type = lttng_action_get_type(action);
+ DBG("Received message from tracer event source: event source fd = %d, token = %" PRIu64 ", action type = '%s'",
+ notification_pipe_read_fd, notification.token,
+ lttng_action_type_string(action_type));
+
+ /* TODO: Perform actions */
+
+ ret = 0;
+
+end_unlock:
+ rcu_read_unlock();
+end:
+ return ret;
+}
+
int handle_notification_thread_channel_sample(
struct notification_thread_state *state, int pipe,
enum lttng_domain_type domain)
goto put_list;
}
+ if (!lttng_trigger_should_fire(trigger)) {
+ goto put_list;
+ }
+
+ lttng_trigger_fire(trigger);
+
/*
* Ownership of `evaluation` transferred to the action executor
* no matter the result.