+static
+bool trigger_name_taken(struct notification_thread_state *state, const char *name)
+{
+ struct cds_lfht_node *triggers_by_name_ht_node;
+ struct cds_lfht_iter iter;
+ /* TODO change hashing for trigger */
+ cds_lfht_lookup(state->triggers_by_name_ht,
+ hash_key_str(name, lttng_ht_seed),
+ match_str,
+ name,
+ &iter);
+ triggers_by_name_ht_node = cds_lfht_iter_get_node(&iter);
+ if (triggers_by_name_ht_node) {
+ return true;
+ } else {
+ return false;
+ }
+
+}
+static
+void generate_trigger_name(struct notification_thread_state *state, struct lttng_trigger *trigger, const char **name)
+{
+ /* Here the offset criteria guarantee an end. This will be a nice
+ * bikeshedding conversation. I would simply generate uuid and use them
+ * as trigger name.
+ */
+ bool taken = false;
+ do {
+ lttng_trigger_generate_name(trigger, state->trigger_id.name_offset);
+ /* TODO error checking */
+ lttng_trigger_get_name(trigger, name);
+ taken = trigger_name_taken(state, *name);
+ if (taken) {
+ state->trigger_id.name_offset++;
+ }
+ } while (taken || state->trigger_id.name_offset == UINT32_MAX);
+}
+
+static bool action_is_notify(const struct lttng_action *action)
+{
+ /* TODO for action groups we need to iterate over all of them */
+ enum lttng_action_type type = lttng_action_get_type_const(action);
+ bool ret = false;
+ enum lttng_action_status status;
+ const struct lttng_action *tmp;
+ unsigned int i, count;
+
+ switch (type) {
+ case LTTNG_ACTION_TYPE_NOTIFY:
+ ret = true;
+ break;
+ case LTTNG_ACTION_TYPE_GROUP:
+ status = lttng_action_group_get_count(action, &count);
+ if (status != LTTNG_ACTION_STATUS_OK) {
+ assert(0);
+ }
+ for (i = 0; i < count; i++) {
+ tmp = lttng_action_group_get_at_index_const(action, i);
+ assert(tmp);
+ ret = action_is_notify(tmp);
+ if (ret) {
+ break;
+ }
+ }
+ break;
+ default:
+ ret = false;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * TODO: REVIEW THIS COMMENT.
+ * FIXME A client's credentials are not checked when registering a trigger, nor
+ * are they stored alongside with the trigger.
+ *
+ * The effects of this are benign since:
+ * - The client will succeed in registering the trigger, as it is valid,
+ * - The trigger will, internally, be bound to the channel/session,
+ * - The notifications will not be sent since the client's credentials
+ * are checked against the channel at that moment.
+ *
+ * If this function returns a non-zero value, it means something is
+ * fundamentally broken and the whole subsystem/thread will be torn down.
+ *
+ * If a non-fatal error occurs, just set the cmd_result to the appropriate
+ * error code.
+ */
+static
+int handle_notification_thread_command_register_trigger(
+ struct notification_thread_state *state,
+ struct lttng_trigger *trigger,
+ enum lttng_error_code *cmd_result)
+{
+ int ret = 0;
+ int is_supported;
+ struct lttng_condition *condition;
+ struct lttng_action *action;
+ struct lttng_trigger_ht_element *trigger_ht_element = NULL;
+ struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element = NULL;
+ struct cds_lfht_node *node;
+ const char* trigger_name;
+ bool free_trigger = true;
+
+ assert(trigger->creds.set);
+
+ rcu_read_lock();
+
+ /* Set the trigger's key */
+ lttng_trigger_set_key(trigger, state->trigger_id.token_generator);
+
+ if (lttng_trigger_get_name(trigger, &trigger_name) == LTTNG_TRIGGER_STATUS_UNSET) {
+ generate_trigger_name(state, trigger, &trigger_name);
+ } else if (trigger_name_taken(state, trigger_name)) {
+ /* Not a fatal error */
+ *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+ ret = 0;
+ goto error;
+ }
+
+ condition = lttng_trigger_get_condition(trigger);
+ assert(condition);
+
+ action = lttng_trigger_get_action(trigger);
+ assert(action);
+
+ is_supported = condition_is_supported(condition);
+ if (is_supported < 0) {
+ goto error;
+ } else if (is_supported == 0) {
+ ret = 0;
+ *cmd_result = LTTNG_ERR_NOT_SUPPORTED;
+ goto error;
+ }
+
+ is_supported = action_is_supported(action);
+ if (is_supported < 0) {
+ goto error;
+ } else if (is_supported == 0) {
+ ret = 0;
+ *cmd_result = LTTNG_ERR_NOT_SUPPORTED;
+ goto error;
+ }
+
+ trigger_ht_element = zmalloc(sizeof(*trigger_ht_element));
+ if (!trigger_ht_element) {
+ ret = -1;
+ goto error;
+ }
+
+ /* Add trigger to the trigger_ht. */
+ cds_lfht_node_init(&trigger_ht_element->node);
+ cds_lfht_node_init(&trigger_ht_element->node_by_name);
+
+ /*
+ * This element own the trigger object from now own, this is why there
+ * is no lttng_trigger_get here.
+ * This thread is now the owner of the trigger object.
+ */
+ trigger_ht_element->trigger = trigger;
+
+ node = cds_lfht_add_unique(state->triggers_ht,
+ lttng_condition_hash(condition),
+ match_trigger,
+ trigger,
+ &trigger_ht_element->node);
+ if (node != &trigger_ht_element->node) {
+ /* Not a fatal error, simply report it to the client. */
+ *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+ goto error_free_ht_element;
+ }
+
+ node = cds_lfht_add_unique(state->triggers_by_name_ht,
+ hash_key_str(trigger_name, lttng_ht_seed),
+ match_str,
+ trigger_name,
+ &trigger_ht_element->node_by_name);
+ if (node != &trigger_ht_element->node_by_name) {
+ /* This should never happen */
+ /* Not a fatal error, simply report it to the client. */
+ /* TODO remove from the trigger_ht */
+ *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) {
+ ret = -1;
+ goto error;
+ }
+
+ /* Add trigger token to the trigger_tokens_ht. */
+ cds_lfht_node_init(&trigger_tokens_ht_element->node);
+ trigger_tokens_ht_element->token = trigger->key.value;
+ 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) {
+ /* TODO: THIS IS A FATAL ERROR... should never happen */
+ /* Not a fatal error, simply report it to the client. */
+ *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+ goto error_free_ht_element;
+ }
+ }
+
+ /*
+ * Ownership of the trigger and of its wrapper was transfered to
+ * the triggers_ht. Same for token ht element if necessary.
+ */
+ trigger_tokens_ht_element = NULL;
+ trigger_ht_element = NULL;
+ free_trigger = false;
+
+ if (action_is_notify(action)) {
+ ret = action_notify_register_trigger(state, trigger);
+ if (ret < 0) {
+ /* TODO should cmd_result be set here? */
+ ret = -1;
+ goto error_free_ht_element;