+static
+struct lttng_event_notifier_notification *recv_one_event_notifier_notification(
+ int notification_pipe_read_fd, enum lttng_domain_type domain)
+{
+ int ret;
+ uint64_t token;
+ struct lttng_event_notifier_notification *notification = NULL;
+ char *capture_buffer = NULL;
+ size_t capture_buffer_size;
+ void *reception_buffer;
+ size_t reception_size;
+
+ struct lttng_ust_abi_event_notifier_notification ust_notification;
+ struct lttng_kernel_abi_event_notifier_notification kernel_notification;
+
+ /* Init lttng_event_notifier_notification */
+ switch(domain) {
+ case LTTNG_DOMAIN_UST:
+ reception_buffer = (void *) &ust_notification;
+ reception_size = sizeof(ust_notification);
+ break;
+ case LTTNG_DOMAIN_KERNEL:
+ reception_buffer = (void *) &kernel_notification;
+ reception_size = sizeof(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:
+ token = ust_notification.token;
+ capture_buffer_size = ust_notification.capture_buf_size;
+ break;
+ case LTTNG_DOMAIN_KERNEL:
+ token = kernel_notification.token;
+ capture_buffer_size = kernel_notification.capture_buf_size;
+ break;
+ default:
+ abort();
+ }
+
+ if (capture_buffer_size == 0) {
+ capture_buffer = NULL;
+ goto skip_capture;
+ }
+
+ if (capture_buffer_size > MAX_CAPTURE_SIZE) {
+ ERR("[notification-thread] Event notifier has a capture payload size which exceeds the maximum allowed size: capture_payload_size = %zu bytes, max allowed size = %d bytes",
+ capture_buffer_size, MAX_CAPTURE_SIZE);
+ goto end;
+ }
+
+ capture_buffer = zmalloc(capture_buffer_size);
+ if (!capture_buffer) {
+ ERR("[notification-thread] Failed to allocate capture buffer");
+ goto end;
+ }
+
+ /* Fetch additional payload (capture). */
+ ret = lttng_read(notification_pipe_read_fd, capture_buffer, capture_buffer_size);
+ if (ret != capture_buffer_size) {
+ ERR("[notification-thread] Failed to read from event source pipe (fd = %i)",
+ notification_pipe_read_fd);
+ goto end;
+ }
+
+skip_capture:
+ notification = lttng_event_notifier_notification_create(token, domain,
+ capture_buffer, capture_buffer_size);
+ if (notification == NULL) {
+ goto end;
+ }
+
+ /*
+ * Ownership transfered to the lttng_event_notifier_notification object.
+ */
+ capture_buffer = NULL;
+
+end:
+ free(capture_buffer);
+ return notification;
+}
+
+static
+int dispatch_one_event_notifier_notification(struct notification_thread_state *state,
+ struct lttng_event_notifier_notification *notification)
+{
+ struct cds_lfht_node *node;
+ struct cds_lfht_iter iter;
+ struct notification_trigger_tokens_ht_element *element;
+ struct lttng_evaluation *evaluation = NULL;
+ enum action_executor_status executor_status;
+ struct notification_client_list *client_list = NULL;
+ int ret;
+ unsigned int capture_count = 0;
+
+ /* Find triggers associated with this token. */
+ rcu_read_lock();
+ cds_lfht_lookup(state->trigger_tokens_ht,
+ hash_key_u64(¬ification->tracer_token, lttng_ht_seed),
+ match_trigger_token, ¬ification->tracer_token, &iter);
+ node = cds_lfht_iter_get_node(&iter);
+ if (caa_unlikely(!node)) {
+ /*
+ * This is not an error, slow consumption of the tracer
+ * notifications can lead to situations where a trigger is
+ * removed but we still get tracer notifications matching a
+ * trigger that no longer exists.
+ */
+ ret = 0;
+ goto end_unlock;
+ }
+
+ element = caa_container_of(node,
+ struct notification_trigger_tokens_ht_element,
+ node);
+
+ if (lttng_condition_event_rule_matches_get_capture_descriptor_count(
+ lttng_trigger_get_const_condition(element->trigger),
+ &capture_count) != LTTNG_CONDITION_STATUS_OK) {
+ ERR("Failed to get capture count");
+ ret = -1;
+ goto end;
+ }
+
+ if (!notification->capture_buffer && capture_count != 0) {
+ ERR("Expected capture but capture buffer is null");
+ ret = -1;
+ goto end;
+ }
+
+ evaluation = lttng_evaluation_event_rule_matches_create(
+ container_of(lttng_trigger_get_const_condition(
+ element->trigger),
+ struct lttng_condition_event_rule_matches,
+ parent),
+ notification->capture_buffer,
+ notification->capture_buf_size, false);
+
+ if (evaluation == NULL) {
+ ERR("[notification-thread] Failed to create event rule hit evaluation while creating and enqueuing action executor job");
+ ret = -1;
+ goto end_unlock;
+ }
+ client_list = get_client_list_from_condition(state,
+ lttng_trigger_get_const_condition(element->trigger));
+ executor_status = action_executor_enqueue_trigger(state->executor,
+ element->trigger, evaluation, NULL, client_list);
+ switch (executor_status) {
+ case ACTION_EXECUTOR_STATUS_OK:
+ ret = 0;
+ break;
+ case ACTION_EXECUTOR_STATUS_OVERFLOW:
+ {
+ struct notification_client_list_element *client_list_element,
+ *tmp;
+
+ /*
+ * Not a fatal error; this is expected and simply means the
+ * executor has too much work queued already.
+ */
+ ret = 0;
+
+ /* No clients subscribed to notifications for this trigger. */
+ if (!client_list) {
+ break;
+ }
+
+ /* Warn clients that a notification (or more) was dropped. */
+ pthread_mutex_lock(&client_list->lock);
+ cds_list_for_each_entry_safe(client_list_element, tmp,
+ &client_list->clients_list, node) {
+ enum client_transmission_status transmission_status;
+ struct notification_client *client =
+ client_list_element->client;
+
+ pthread_mutex_lock(&client->lock);
+ ret = client_notification_overflow(client);
+ if (ret) {
+ /* Fatal error. */
+ goto next_client;
+ }
+
+ transmission_status =
+ client_flush_outgoing_queue(client);
+ ret = client_handle_transmission_status(
+ client, transmission_status, state);
+ if (ret) {
+ /* Fatal error. */
+ goto next_client;
+ }
+next_client:
+ pthread_mutex_unlock(&client->lock);
+ if (ret) {
+ break;
+ }
+ }
+
+ pthread_mutex_unlock(&client_list->lock);
+ break;
+ }
+ case ACTION_EXECUTOR_STATUS_INVALID:
+ case ACTION_EXECUTOR_STATUS_ERROR:
+ /* Fatal error, shut down everything. */
+ ERR("Fatal error encoutered while enqueuing action to the action executor");
+ ret = -1;
+ goto end_unlock;
+ default:
+ /* Unhandled error. */
+ abort();
+ }
+
+end_unlock:
+ notification_client_list_put(client_list);
+ rcu_read_unlock();
+end:
+ return ret;
+}
+
+static
+int handle_one_event_notifier_notification(
+ struct notification_thread_state *state,
+ int pipe, enum lttng_domain_type domain)
+{
+ int ret = 0;
+ struct lttng_event_notifier_notification *notification = NULL;
+
+ notification = recv_one_event_notifier_notification(pipe, domain);
+ if (notification == NULL) {
+ /* Reception failed, don't consider it fatal. */
+ ERR("[notification-thread] Error receiving an event notifier notification from tracer: fd = %i, domain = %s",
+ pipe, lttng_domain_type_str(domain));
+ goto end;
+ }
+
+ ret = dispatch_one_event_notifier_notification(state, notification);
+ if (ret) {
+ ERR("[notification-thread] Error dispatching an event notifier notification from tracer: fd = %i, domain = %s",
+ pipe, lttng_domain_type_str(domain));
+ goto end;
+ }
+
+end:
+ lttng_event_notifier_notification_destroy(notification);
+ return ret;
+}
+
+int handle_notification_thread_event_notification(struct notification_thread_state *state,
+ int pipe, enum lttng_domain_type domain)
+{
+ return handle_one_event_notifier_notification(state, pipe, domain);
+}
+