+
+error_version:
+ modprobe_remove_lttng_control();
+ ret = close(kernel_tracer_fd);
+ if (ret) {
+ PERROR("Failed to close kernel tracer file descriptor: fd = %d",
+ kernel_tracer_fd);
+ }
+
+ kernel_tracer_fd = -1;
+ return LTTNG_ERR_KERN_VERSION;
+
+
+error_token_ht:
+ ret = close(kernel_tracer_event_notifier_group_notification_fd);
+ if (ret) {
+ PERROR("Failed to close kernel tracer event notifier group notification file descriptor: fd = %d",
+ kernel_tracer_event_notifier_group_notification_fd);
+ }
+
+ kernel_tracer_event_notifier_group_notification_fd = -1;
+
+error_modules:
+ ret = close(kernel_tracer_event_notifier_group_fd);
+ if (ret) {
+ PERROR("Failed to close kernel tracer event notifier group file descriptor: fd = %d",
+ kernel_tracer_event_notifier_group_fd);
+ }
+
+ kernel_tracer_event_notifier_group_fd = -1;
+
+ ret = close(kernel_tracer_fd);
+ if (ret) {
+ PERROR("Failed to close kernel tracer file descriptor: fd = %d",
+ kernel_tracer_fd);
+ }
+
+ kernel_tracer_fd = -1;
+
+error_open:
+ modprobe_remove_lttng_control();
+
+error:
+ WARN("No kernel tracer available");
+ kernel_tracer_fd = -1;
+ if (!is_root) {
+ return LTTNG_ERR_NEED_ROOT_SESSIOND;
+ } else {
+ return LTTNG_ERR_KERN_NA;
+ }
+}
+
+LTTNG_HIDDEN
+void cleanup_kernel_tracer(void)
+{
+ DBG2("Closing kernel event notifier group notification file descriptor");
+ if (kernel_tracer_event_notifier_group_notification_fd >= 0) {
+ int ret = notification_thread_command_remove_tracer_event_source(
+ notification_thread_handle,
+ kernel_tracer_event_notifier_group_notification_fd);
+ if (ret != LTTNG_OK) {
+ ERR("Failed to remove kernel event notifier notification from notification thread");
+ }
+
+ ret = close(kernel_tracer_event_notifier_group_notification_fd);
+ if (ret) {
+ PERROR("Failed to close kernel event notifier group notification file descriptor: fd = %d",
+ kernel_tracer_event_notifier_group_notification_fd);
+ }
+
+ kernel_tracer_event_notifier_group_notification_fd = -1;
+ }
+
+ if (kernel_token_to_event_notifier_rule_ht) {
+ const int ret = cds_lfht_destroy(
+ kernel_token_to_event_notifier_rule_ht, NULL);
+ assert(ret == 0);
+ }
+
+ DBG2("Closing kernel event notifier group file descriptor");
+ if (kernel_tracer_event_notifier_group_fd >= 0) {
+ const int ret = close(kernel_tracer_event_notifier_group_fd);
+
+ if (ret) {
+ PERROR("Failed to close kernel event notifier group file descriptor: fd = %d",
+ kernel_tracer_event_notifier_group_fd);
+ }
+
+ kernel_tracer_event_notifier_group_fd = -1;
+ }
+
+ DBG2("Closing kernel fd");
+ if (kernel_tracer_fd >= 0) {
+ const int ret = close(kernel_tracer_fd);
+
+ if (ret) {
+ PERROR("Failed to close kernel tracer file descriptor: fd = %d",
+ kernel_tracer_fd);
+ }
+
+ kernel_tracer_fd = -1;
+ }
+
+ free(syscall_table);
+}
+
+LTTNG_HIDDEN
+bool kernel_tracer_is_initialized(void)
+{
+ return kernel_tracer_fd >= 0;
+}
+
+/*
+ * Clear a kernel session.
+ *
+ * Return LTTNG_OK on success or else an LTTng error code.
+ */
+enum lttng_error_code kernel_clear_session(struct ltt_session *session)
+{
+ int ret;
+ enum lttng_error_code status = LTTNG_OK;
+ struct consumer_socket *socket;
+ struct lttng_ht_iter iter;
+ struct ltt_kernel_session *ksess = session->kernel_session;
+
+ assert(ksess);
+ assert(ksess->consumer);
+
+ DBG("Clear kernel session %s (session %" PRIu64 ")",
+ session->name, session->id);
+
+ rcu_read_lock();
+
+ if (ksess->active) {
+ ERR("Expecting inactive session %s (%" PRIu64 ")", session->name, session->id);
+ status = LTTNG_ERR_FATAL;
+ goto end;
+ }
+
+ /*
+ * Note that this loop will end after one iteration given that there is
+ * only one kernel consumer.
+ */
+ cds_lfht_for_each_entry(ksess->consumer->socks->ht, &iter.iter,
+ socket, node.node) {
+ struct ltt_kernel_channel *chan;
+
+ /* For each channel, ask the consumer to clear it. */
+ cds_list_for_each_entry(chan, &ksess->channel_list.head, list) {
+ DBG("Clear kernel channel %" PRIu64 ", session %s",
+ chan->key, session->name);
+ ret = consumer_clear_channel(socket, chan->key);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+
+ if (!ksess->metadata) {
+ /*
+ * Nothing to do for the metadata.
+ * This is a snapshot session.
+ * The metadata is genererated on the fly.
+ */
+ continue;
+ }
+
+ /*
+ * Clear the metadata channel.
+ * Metadata channel is not cleared per se but we still need to
+ * perform a rotation operation on it behind the scene.
+ */
+ ret = consumer_clear_channel(socket, ksess->metadata->key);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+
+ goto end;
+error:
+ switch (-ret) {
+ case LTTCOMM_CONSUMERD_RELAYD_CLEAR_DISALLOWED:
+ status = LTTNG_ERR_CLEAR_RELAY_DISALLOWED;
+ break;
+ default:
+ status = LTTNG_ERR_CLEAR_FAIL_CONSUMER;
+ break;
+ }
+end:
+ rcu_read_unlock();
+ return status;
+}
+
+enum lttng_error_code kernel_create_event_notifier_group_notification_fd(
+ int *event_notifier_group_notification_fd)
+{
+ int local_fd = -1, ret;
+ enum lttng_error_code error_code_ret;
+
+ assert(event_notifier_group_notification_fd);
+
+ ret = kernctl_create_event_notifier_group_notification_fd(
+ kernel_tracer_event_notifier_group_fd);
+ if (ret < 0) {
+ PERROR("Failed to create kernel event notifier group notification file descriptor");
+ error_code_ret = LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD;
+ goto error;
+ }
+
+ local_fd = ret;
+
+ /* Prevent fd duplication after execlp(). */
+ ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC);
+ if (ret < 0) {
+ PERROR("Failed to set FD_CLOEXEC on kernel event notifier group notification file descriptor: fd = %d",
+ local_fd);
+ error_code_ret = LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD;
+ goto error;
+ }
+
+ DBG("Created kernel notifier group notification file descriptor: fd = %d",
+ local_fd);
+ error_code_ret = LTTNG_OK;
+ *event_notifier_group_notification_fd = local_fd;
+ local_fd = -1;
+
+error:
+ if (local_fd >= 0) {
+ ret = close(local_fd);
+ if (ret) {
+ PERROR("Failed to close kernel event notifier group notification file descriptor: fd = %d",
+ local_fd);
+ }
+ }
+
+ return error_code_ret;
+}
+
+enum lttng_error_code kernel_destroy_event_notifier_group_notification_fd(
+ int event_notifier_group_notification_fd)
+{
+ enum lttng_error_code ret_code = LTTNG_OK;
+
+ DBG("Closing event notifier group notification file descriptor: fd = %d",
+ event_notifier_group_notification_fd);
+ if (event_notifier_group_notification_fd >= 0) {
+ const int ret = close(event_notifier_group_notification_fd);
+ if (ret) {
+ PERROR("Failed to close event notifier group notification file descriptor: fd = %d",
+ event_notifier_group_notification_fd);
+ }
+ }
+
+ return ret_code;
+}
+
+static
+unsigned long hash_trigger(const struct lttng_trigger *trigger)
+{
+ const struct lttng_condition *condition =
+ lttng_trigger_get_const_condition(trigger);
+
+ return lttng_condition_hash(condition);
+}
+
+static
+int match_trigger(struct cds_lfht_node *node, const void *key)
+{
+ const struct ltt_kernel_event_notifier_rule *event_notifier_rule;
+ const struct lttng_trigger *trigger = key;
+
+ event_notifier_rule = caa_container_of(node,
+ const struct ltt_kernel_event_notifier_rule, ht_node);
+
+ return lttng_trigger_is_equal(trigger, event_notifier_rule->trigger);
+}
+
+static enum lttng_error_code kernel_create_event_notifier_rule(
+ struct lttng_trigger *trigger,
+ const struct lttng_credentials *creds, uint64_t token)
+{
+ int err, fd, ret = 0;
+ enum lttng_error_code error_code_ret;
+ enum lttng_condition_status condition_status;
+ enum lttng_condition_type condition_type;
+ enum lttng_event_rule_type event_rule_type;
+ struct ltt_kernel_event_notifier_rule *event_notifier_rule;
+ struct lttng_kernel_event_notifier kernel_event_notifier = {};
+ unsigned int capture_bytecode_count = 0, i;
+ const struct lttng_condition *condition = NULL;
+ const struct lttng_event_rule *event_rule = NULL;
+ enum lttng_condition_status cond_status;
+
+ assert(trigger);
+
+ condition = lttng_trigger_get_const_condition(trigger);
+ assert(condition);
+
+ condition_type = lttng_condition_get_type(condition);
+ assert(condition_type == LTTNG_CONDITION_TYPE_ON_EVENT);
+
+ /* Does not acquire a reference. */
+ condition_status = lttng_condition_on_event_get_rule(
+ condition, &event_rule);
+ assert(condition_status == LTTNG_CONDITION_STATUS_OK);
+ assert(event_rule);
+
+ event_rule_type = lttng_event_rule_get_type(event_rule);
+ assert(event_rule_type != LTTNG_EVENT_RULE_TYPE_UNKNOWN);
+
+ error_code_ret = trace_kernel_create_event_notifier_rule(trigger, token,
+ lttng_condition_on_event_get_error_counter_index(condition),
+ &event_notifier_rule);
+ if (error_code_ret != LTTNG_OK) {
+ goto error;
+ }
+
+ error_code_ret = trace_kernel_init_event_notifier_from_event_rule(
+ event_rule, &kernel_event_notifier);
+ if (error_code_ret != LTTNG_OK) {
+ goto free_event;
+ }
+
+ kernel_event_notifier.event.token = event_notifier_rule->token;
+ kernel_event_notifier.error_counter_idx =
+ lttng_condition_on_event_get_error_counter_index(condition);
+
+ fd = kernctl_create_event_notifier(
+ kernel_tracer_event_notifier_group_fd,
+ &kernel_event_notifier);
+ if (fd < 0) {
+ switch (-fd) {
+ case EEXIST:
+ error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST;
+ break;
+ case ENOSYS:
+ WARN("Failed to create kernel event notifier: not notifier type not implemented");
+ error_code_ret = LTTNG_ERR_KERN_EVENT_ENOSYS;
+ break;
+ case ENOENT:
+ WARN("Failed to create kernel event notifier: not found: name = '%s'",
+ kernel_event_notifier.event.name);
+ error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+ break;
+ default:
+ PERROR("Failed to create kernel event notifier: error code = %d, name = '%s'",
+ fd, kernel_event_notifier.event.name);
+ error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+ }
+ goto free_event;
+ }
+
+ event_notifier_rule->fd = fd;
+ /* Prevent fd duplication after execlp(). */
+ err = fcntl(event_notifier_rule->fd, F_SETFD, FD_CLOEXEC);
+ if (err < 0) {
+ PERROR("Failed to set FD_CLOEXEC on kernel event notifier file descriptor: fd = %d",
+ fd);
+ error_code_ret = LTTNG_ERR_FATAL;
+ goto set_cloexec_error;
+ }
+
+ if (event_notifier_rule->filter) {
+ err = kernctl_filter(event_notifier_rule->fd, event_notifier_rule->filter);
+ if (err < 0) {
+ switch (-err) {
+ case ENOMEM:
+ error_code_ret = LTTNG_ERR_FILTER_NOMEM;
+ break;
+ default:
+ error_code_ret = LTTNG_ERR_FILTER_INVAL;
+ break;
+ }
+ goto filter_error;
+ }
+ }
+
+ if (lttng_event_rule_get_type(event_rule) ==
+ LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE) {
+ ret = userspace_probe_event_rule_add_callsites(
+ event_rule, creds, event_notifier_rule->fd);
+ if (ret) {
+ error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+ goto add_callsite_error;
+ }
+ }
+
+ /* Set the capture bytecode if any. */
+ cond_status = lttng_condition_on_event_get_capture_descriptor_count(
+ condition, &capture_bytecode_count);
+ assert(cond_status == LTTNG_CONDITION_STATUS_OK);
+
+ for (i = 0; i < capture_bytecode_count; i++) {
+ const struct lttng_bytecode *capture_bytecode =
+ lttng_condition_on_event_get_capture_bytecode_at_index(
+ condition, i);
+
+ if (capture_bytecode == NULL) {
+ ERR("Unexpected NULL capture bytecode on condition");
+ error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+ goto capture_error;
+ }
+
+ ret = kernctl_capture(event_notifier_rule->fd, capture_bytecode);
+ if (ret < 0) {
+ ERR("Failed to set capture bytecode on event notifier rule fd: fd = %d",
+ event_notifier_rule->fd);
+ error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+ goto capture_error;
+ }
+ }
+
+ err = kernctl_enable(event_notifier_rule->fd);
+ if (err < 0) {
+ switch (-err) {
+ case EEXIST:
+ error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST;
+ break;
+ default:
+ PERROR("enable kernel event notifier");
+ error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+ break;
+ }
+ goto enable_error;
+ }
+
+ /* Add trigger to kernel token mapping in the hash table. */
+ rcu_read_lock();
+ cds_lfht_add(kernel_token_to_event_notifier_rule_ht, hash_trigger(trigger),
+ &event_notifier_rule->ht_node);
+ rcu_read_unlock();
+
+ DBG("Created kernel event notifier: name = '%s', fd = %d",
+ kernel_event_notifier.event.name,
+ event_notifier_rule->fd);
+
+ return LTTNG_OK;
+
+capture_error:
+add_callsite_error:
+enable_error:
+set_cloexec_error:
+filter_error:
+ {
+ const int close_ret = close(event_notifier_rule->fd);
+
+ if (close_ret) {
+ PERROR("Failed to close kernel event notifier file descriptor: fd = %d",
+ event_notifier_rule->fd);
+ }
+ }
+free_event:
+ free(event_notifier_rule);
+error:
+ return error_code_ret;
+}
+
+enum lttng_error_code kernel_register_event_notifier(
+ struct lttng_trigger *trigger,
+ const struct lttng_credentials *cmd_creds)
+{
+ enum lttng_error_code ret;
+ enum lttng_condition_status status;
+ enum lttng_domain_type domain_type;
+ const struct lttng_event_rule *event_rule;
+ const struct lttng_condition *const condition =
+ lttng_trigger_get_const_condition(trigger);
+ const uint64_t token = lttng_trigger_get_tracer_token(trigger);
+
+ assert(condition);
+
+ /* Does not acquire a reference to the event rule. */
+ status = lttng_condition_on_event_get_rule(
+ condition, &event_rule);
+ assert(status == LTTNG_CONDITION_STATUS_OK);
+
+ domain_type = lttng_event_rule_get_domain_type(event_rule);
+ assert(domain_type == LTTNG_DOMAIN_KERNEL);
+
+ ret = kernel_create_event_notifier_rule(trigger, cmd_creds, token);
+ if (ret != LTTNG_OK) {
+ ERR("Failed to create kernel event notifier rule");
+ }
+
+ return ret;
+}
+
+enum lttng_error_code kernel_unregister_event_notifier(
+ const struct lttng_trigger *trigger)
+{
+ struct ltt_kernel_event_notifier_rule *token_event_rule_element;
+ struct cds_lfht_node *node;
+ struct cds_lfht_iter iter;
+ enum lttng_error_code error_code_ret;
+ int ret;
+
+ rcu_read_lock();
+
+ cds_lfht_lookup(kernel_token_to_event_notifier_rule_ht,
+ hash_trigger(trigger), match_trigger, trigger, &iter);
+
+ node = cds_lfht_iter_get_node(&iter);
+ if (!node) {
+ error_code_ret = LTTNG_ERR_TRIGGER_NOT_FOUND;
+ goto error;
+ }
+
+ token_event_rule_element = caa_container_of(node,
+ struct ltt_kernel_event_notifier_rule, ht_node);
+
+ ret = kernel_disable_event_notifier_rule(token_event_rule_element);
+ if (ret) {
+ error_code_ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+
+ trace_kernel_destroy_event_notifier_rule(token_event_rule_element);
+ error_code_ret = LTTNG_OK;
+
+error:
+ rcu_read_unlock();
+
+ return error_code_ret;
+}
+
+int kernel_get_notification_fd(void)
+{
+ return kernel_tracer_event_notifier_group_notification_fd;