+
+/*
+ * Setup necessary data for kernel tracer action.
+ */
+LTTNG_HIDDEN
+int init_kernel_tracer(void)
+{
+ int ret;
+ bool is_root = !getuid();
+
+ /* Modprobe lttng kernel modules */
+ ret = modprobe_lttng_control();
+ if (ret < 0) {
+ goto error;
+ }
+
+ /* Open debugfs lttng */
+ kernel_tracer_fd = open(module_proc_lttng, O_RDWR);
+ if (kernel_tracer_fd < 0) {
+ DBG("Failed to open %s", module_proc_lttng);
+ goto error_open;
+ }
+
+ /* Validate kernel version */
+ ret = kernel_validate_version(&kernel_tracer_version,
+ &kernel_tracer_abi_version);
+ if (ret < 0) {
+ goto error_version;
+ }
+
+ ret = modprobe_lttng_data();
+ if (ret < 0) {
+ goto error_modules;
+ }
+
+ ret = kernel_supports_ring_buffer_snapshot_sample_positions();
+ if (ret < 0) {
+ goto error_modules;
+ }
+ if (ret < 1) {
+ WARN("Kernel tracer does not support buffer monitoring. "
+ "The monitoring timer of channels in the kernel domain "
+ "will be set to 0 (disabled).");
+ }
+
+ ret = kernel_create_trigger_group(&kernel_tracer_trigger_group_fd);
+ if (ret < 0) {
+ /* TODO: error handling if it is not supported etc. */
+ WARN("Failed trigger group creation");
+ kernel_tracer_trigger_group_fd = -1;
+ /* This is not fatal */
+ } else {
+ ret = kernel_create_trigger_group_notification_fd(&kernel_tracer_trigger_group_notification_fd);
+ if (ret < 0) {
+ goto error_modules;
+ }
+ }
+
+ CDS_INIT_LIST_HEAD(&kernel_tracer_token_list.head);
+
+ DBG("Kernel tracer fd %d", kernel_tracer_fd);
+ DBG("Kernel tracer trigger group fd %d", kernel_tracer_trigger_group_fd);
+ DBG("Kernel tracer trigger group notificationi fd %d", kernel_tracer_trigger_group_notification_fd);
+
+ ret = syscall_init_table(kernel_tracer_fd);
+ if (ret < 0) {
+ ERR("Unable to populate syscall table. Syscall tracing won't "
+ "work for this session daemon.");
+ }
+
+ return 0;
+
+error_version:
+ modprobe_remove_lttng_control();
+ ret = close(kernel_tracer_fd);
+ if (ret) {
+ PERROR("close");
+ }
+ kernel_tracer_fd = -1;
+ return LTTNG_ERR_KERN_VERSION;
+
+error_modules:
+ ret = close(kernel_tracer_fd);
+ if (ret) {
+ PERROR("close");
+ }
+
+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)
+{
+ int ret;
+
+ struct ltt_kernel_token_event_rule *rule, *rtmp;
+ cds_list_for_each_entry_safe(rule, rtmp, &kernel_tracer_token_list.head, list) {
+ kernel_disable_token_event_rule(rule);
+ trace_kernel_destroy_token_event_rule(rule);
+ }
+
+ DBG2("Closing kernel trigger group notification fd");
+ if (kernel_tracer_trigger_group_notification_fd >= 0) {
+ ret = close(kernel_tracer_trigger_group_notification_fd);
+ if (ret) {
+ PERROR("close");
+ }
+ kernel_tracer_trigger_group_notification_fd = -1;
+ }
+
+ /* TODO: do we iterate over the list to remove all token? */
+ DBG2("Closing kernel trigger group fd");
+ if (kernel_tracer_trigger_group_fd >= 0) {
+ ret = close(kernel_tracer_trigger_group_fd);
+ if (ret) {
+ PERROR("close");
+ }
+ kernel_tracer_trigger_group_fd = -1;
+ }
+
+ DBG2("Closing kernel fd");
+ if (kernel_tracer_fd >= 0) {
+ ret = close(kernel_tracer_fd);
+ if (ret) {
+ PERROR("close");
+ }
+ kernel_tracer_fd = -1;
+ }
+
+
+ DBG("Unloading kernel modules");
+ modprobe_remove_lttng_all();
+ 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_trigger_group_notification_fd(
+ int *trigger_group_notification_fd)
+{
+ enum lttng_error_code ret = LTTNG_OK;
+ int local_fd = -1;
+
+ assert(trigger_group_notification_fd);
+
+ ret = kernctl_create_trigger_group_notification_fd(kernel_tracer_trigger_group_fd);
+ if (ret < 0) {
+ PERROR("ioctl kernel create trigger group");
+ ret = -1;
+ goto error;
+ }
+
+ /* Store locally */
+ local_fd = ret;
+
+ /* Prevent fd duplication after execlp() */
+ ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC);
+ if (ret < 0) {
+ PERROR("fcntl session fd");
+ }
+
+ DBG("Kernel trigger group notification created (fd: %d)",
+ local_fd);
+ ret = 0;
+
+error:
+ *trigger_group_notification_fd = local_fd;
+ return ret;
+}
+
+enum lttng_error_code kernel_destroy_trigger_group_notification_fd(
+ int trigger_group_notification_fd)
+{
+ enum lttng_error_code ret = LTTNG_OK;
+ DBG("Closing trigger group notification fd %d", trigger_group_notification_fd);
+ if (trigger_group_notification_fd >= 0) {
+ ret = close(trigger_group_notification_fd);
+ if (ret) {
+ PERROR("close");
+ }
+ }
+ return ret;
+}
+
+static int kernel_create_token_event_rule(struct lttng_event_rule *rule,
+ const struct lttng_credentials *creds, uint64_t token)
+{
+ int err, fd;
+ enum lttng_error_code ret;
+ struct ltt_kernel_token_event_rule *event;
+ struct lttng_kernel_trigger trigger;
+
+ assert(rule);
+
+ ret = trace_kernel_create_token_event_rule(rule, token, &event);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ trace_kernel_init_trigger_from_event_rule(event->event_rule, &trigger);
+ trigger.id = event->token;
+
+ fd = kernctl_create_trigger(kernel_tracer_trigger_group_fd, &trigger);
+ if (fd < 0) {
+ switch (-fd) {
+ case EEXIST:
+ ret = LTTNG_ERR_KERN_EVENT_EXIST;
+ break;
+ case ENOSYS:
+ WARN("Trigger type not implemented");
+ ret = LTTNG_ERR_KERN_EVENT_ENOSYS;
+ break;
+ case ENOENT:
+ WARN("Event %s not found!", trigger.name);
+ ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+ break;
+ default:
+ ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+ PERROR("create trigger ioctl");
+ }
+ goto free_event;
+ }
+
+ event->fd = fd;
+ /* Prevent fd duplication after execlp() */
+ err = fcntl(event->fd, F_SETFD, FD_CLOEXEC);
+ if (err < 0) {
+ PERROR("fcntl session fd");
+ }
+
+ if (event->filter) {
+ err = kernctl_filter(event->fd, event->filter);
+ if (err < 0) {
+ switch (-err) {
+ case ENOMEM:
+ ret = LTTNG_ERR_FILTER_NOMEM;
+ break;
+ default:
+ ret = LTTNG_ERR_FILTER_INVAL;
+ break;
+ }
+ goto filter_error;
+ }
+ }
+
+ if (lttng_event_rule_get_type(event->event_rule) ==
+ LTTNG_EVENT_RULE_TYPE_UPROBE) {
+ ret = userspace_probe_event_rule_add_callsites(
+ rule, creds, event->fd);
+ if (ret) {
+ goto add_callsite_error;
+ }
+ }
+
+ err = kernctl_enable(event->fd);
+ if (err < 0) {
+ switch (-err) {
+ case EEXIST:
+ ret = LTTNG_ERR_KERN_EVENT_EXIST;
+ break;
+ default:
+ PERROR("enable kernel trigger");
+ ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+ break;
+ }
+ goto enable_error;
+ }
+
+ /* Add event to event list */
+ cds_list_add(&event->list, &kernel_tracer_token_list.head);
+
+ DBG("Trigger %s created (fd: %d)", trigger.name, event->fd);
+
+ return 0;
+
+add_callsite_error:
+enable_error:
+filter_error:
+ {
+ int closeret;
+
+ closeret = close(event->fd);
+ if (closeret) {
+ PERROR("close event fd");
+ }
+ }
+free_event:
+ free(event);
+error:
+ return ret;
+}
+
+enum lttng_error_code kernel_update_tokens(void)
+{
+ enum lttng_error_code ret = LTTNG_OK;
+ enum lttng_trigger_status t_status;
+ struct ltt_kernel_token_event_rule *token_event_rule_element;
+ struct lttng_triggers *triggers;
+ unsigned int count;
+
+ /* TODO error handling */
+
+ /* Get list of token trigger from the notification thread here */
+ rcu_read_lock();
+ pthread_mutex_lock(¬ification_trigger_tokens_ht_lock);
+ ret = notification_thread_command_get_tokens(notification_thread_handle, &triggers);
+ if (ret != LTTNG_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ assert(triggers);
+
+ t_status = lttng_triggers_get_count(triggers, &count);
+ if (t_status != LTTNG_TRIGGER_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ for (unsigned int i = 0; i < count; i++) {
+ struct lttng_condition *condition;
+ struct lttng_event_rule *event_rule;
+ struct lttng_trigger *trigger;
+ struct ltt_kernel_token_event_rule *k_token;
+ const struct lttng_credentials *creds;
+ uint64_t token;
+
+ trigger = lttng_triggers_get_pointer_of_index(triggers, i);
+ assert(trigger);
+
+ /* TODO: error checking and type checking */
+ token = lttng_trigger_get_key(trigger);
+ condition = lttng_trigger_get_condition(trigger);
+ (void) lttng_condition_event_rule_get_rule_no_const(condition, &event_rule);
+
+ if (lttng_event_rule_get_domain_type(event_rule) != LTTNG_DOMAIN_KERNEL) {
+ /* Skip ust related trigger */
+ continue;
+ }
+
+ creds = lttng_trigger_get_credentials(trigger);
+ /* Iterate over all known token trigger */
+ k_token = trace_kernel_find_trigger_by_token(&kernel_tracer_token_list, token);
+ if (!k_token) {
+ ret = kernel_create_token_event_rule(event_rule, creds, token);
+ if (ret < 0) {
+ goto end;
+ }
+ }
+ }
+
+ /* Remove all unknown trigger from the app
+ * TODO find a way better way then this, do it on the unregister command
+ * and be specific on the token to remove instead of going over all
+ * trigger known to the app. This is sub optimal.
+ */
+ cds_list_for_each_entry (token_event_rule_element, &kernel_tracer_token_list.head,
+ list) {
+ uint64_t token;
+ bool found = false;
+
+ token = token_event_rule_element->token;
+
+ /*
+ * Check if the app event trigger still exists on the
+ * notification side.
+ * TODO: might want to change the backing data struct of the
+ * lttng_triggers object to allow quick lookup?
+ * For kernel mostly all of this can be removed once we delete
+ * on a per trigger basis.
+ */
+
+ for (unsigned int i = 0; i < count; i++) {
+ struct lttng_trigger *trigger;
+ uint64_t inner_token;
+
+ trigger = lttng_triggers_get_pointer_of_index(
+ triggers, i);
+ assert(trigger);
+
+ inner_token = lttng_trigger_get_key(trigger);
+
+ if (inner_token == token) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ /* Still valid */
+ continue;
+ }
+
+ kernel_disable_token_event_rule(token_event_rule_element);
+ trace_kernel_destroy_token_event_rule(token_event_rule_element);
+ }
+end:
+ rcu_read_unlock();
+ pthread_mutex_unlock(¬ification_trigger_tokens_ht_lock);
+ return ret;
+
+}
+
+int kernel_get_notification_fd(void)
+{
+ return kernel_tracer_trigger_group_notification_fd;
+}