+ * Validate the trigger credentials against the command credentials.
+ * Only the root user can unregister a trigger with non-matching
+ * credentials.
+ */
+ if (!lttng_credentials_is_equal_uid(
+ lttng_trigger_get_credentials(trigger),
+ cmd_creds)) {
+ if (lttng_credentials_get_uid(cmd_creds) != 0) {
+ ERR("Trigger credentials do not match the command credentials: trigger name = '%s', trigger owner uid = %d, command creds uid = %d",
+ trigger_name, (int) trigger_owner,
+ (int) lttng_credentials_get_uid(cmd_creds));
+ ret_code = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+ }
+
+ /* Fetch the sessiond side trigger object. */
+ ret_code = notification_thread_command_get_trigger(
+ notification_thread, trigger, &sessiond_trigger);
+ if (ret_code != LTTNG_OK) {
+ DBG("Failed to get trigger from notification thread during unregister: trigger name = '%s', trigger owner uid = %d, error code = %d",
+ trigger_name, (int) trigger_owner, ret_code);
+ goto end;
+ }
+
+ assert(sessiond_trigger);
+
+ /*
+ * From this point on, no matter what, consider the trigger
+ * unregistered.
+ *
+ * We set the unregistered state of the sessiond side trigger object in
+ * the client thread since we want to minimize the possibility of the
+ * notification thread being stalled due to a long execution of an
+ * action that required the trigger lock.
+ */
+ lttng_trigger_set_as_unregistered(sessiond_trigger);
+
+ ret_code = notification_thread_command_unregister_trigger(notification_thread,
+ trigger);
+ if (ret_code != LTTNG_OK) {
+ DBG("Failed to unregister trigger from notification thread: trigger name = '%s', trigger owner uid = %d, error code = %d",
+ trigger_name, (int) trigger_owner, ret_code);
+ goto end;
+ }
+
+ /*
+ * Synchronize tracers if the trigger removes an event notifier.
+ * Do this even if the trigger unregistration failed to at least stop
+ * the tracers from producing notifications associated with this
+ * event notifier.
+ */
+ if (lttng_trigger_needs_tracer_notifier(trigger)) {
+ ret_code = synchronize_tracer_notifier_unregister(trigger);
+ if (ret_code != LTTNG_OK) {
+ ERR("Error unregistering trigger to tracer.");
+ goto end;
+ }
+
+ }
+
+end:
+ lttng_trigger_put(sessiond_trigger);
+ return ret_code;
+}
+
+enum lttng_error_code cmd_list_triggers(struct command_ctx *cmd_ctx,
+ struct notification_thread_handle *notification_thread,
+ struct lttng_triggers **return_triggers)
+{
+ int ret;
+ enum lttng_error_code ret_code;
+ struct lttng_triggers *triggers = NULL;
+
+ /* Get the set of triggers from the notification thread. */
+ ret_code = notification_thread_command_list_triggers(
+ notification_thread, cmd_ctx->creds.uid, &triggers);
+ if (ret_code != LTTNG_OK) {
+ goto end;
+ }
+
+ ret = lttng_triggers_remove_hidden_triggers(triggers);
+ if (ret) {
+ ret_code = LTTNG_ERR_UNK;
+ goto end;
+ }
+
+ *return_triggers = triggers;
+ triggers = NULL;
+ ret_code = LTTNG_OK;
+end:
+ lttng_triggers_destroy(triggers);
+ return ret_code;
+}
+
+enum lttng_error_code cmd_execute_error_query(const struct lttng_credentials *cmd_creds,
+ const struct lttng_error_query *query,
+ struct lttng_error_query_results **_results,
+ struct notification_thread_handle *notification_thread)
+{
+ enum lttng_error_code ret_code;
+ const struct lttng_trigger *query_target_trigger;
+ const struct lttng_action *query_target_action = NULL;
+ struct lttng_trigger *matching_trigger = NULL;
+ const char *trigger_name;
+ uid_t trigger_owner;
+ enum lttng_trigger_status trigger_status;
+ struct lttng_error_query_results *results = NULL;
+
+ switch (lttng_error_query_get_target_type(query)) {
+ case LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER:
+ query_target_trigger = lttng_error_query_trigger_borrow_target(query);
+ break;
+ case LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION:
+ query_target_trigger =
+ lttng_error_query_condition_borrow_target(query);
+ break;
+ case LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION:
+ query_target_trigger = lttng_error_query_action_borrow_trigger_target(
+ query);
+ break;
+ default:
+ abort();
+ }
+
+ assert(query_target_trigger);
+
+ ret_code = notification_thread_command_get_trigger(notification_thread,
+ query_target_trigger, &matching_trigger);
+ if (ret_code != LTTNG_OK) {
+ goto end;
+ }
+
+ /* No longer needed. */
+ query_target_trigger = NULL;
+
+ if (lttng_error_query_get_target_type(query) ==
+ LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION) {
+ /* Get the sessiond-side version of the target action. */
+ query_target_action =
+ lttng_error_query_action_borrow_action_target(
+ query, matching_trigger);
+ }
+
+ trigger_status = lttng_trigger_get_name(matching_trigger, &trigger_name);
+ trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ?
+ trigger_name : "(anonymous)";
+ trigger_status = lttng_trigger_get_owner_uid(matching_trigger,
+ &trigger_owner);
+ assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+ results = lttng_error_query_results_create();
+ if (!results) {
+ ret_code = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ DBG("Running \"execute error query\" command: trigger name = '%s', trigger owner uid = %d, command creds uid = %d",
+ trigger_name, (int) trigger_owner,
+ (int) lttng_credentials_get_uid(cmd_creds));
+
+ /*
+ * Validate the trigger credentials against the command credentials.
+ * Only the root user can target a trigger with non-matching
+ * credentials.
+ */
+ if (!lttng_credentials_is_equal_uid(
+ lttng_trigger_get_credentials(matching_trigger),
+ cmd_creds)) {
+ if (lttng_credentials_get_uid(cmd_creds) != 0) {
+ ERR("Trigger credentials do not match the command credentials: trigger name = '%s', trigger owner uid = %d, command creds uid = %d",
+ trigger_name, (int) trigger_owner,
+ (int) lttng_credentials_get_uid(cmd_creds));
+ ret_code = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+ }
+
+ switch (lttng_error_query_get_target_type(query)) {
+ case LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER:
+ trigger_status = lttng_trigger_add_error_results(
+ matching_trigger, results);
+
+ switch (trigger_status) {
+ case LTTNG_TRIGGER_STATUS_OK:
+ break;
+ default:
+ ret_code = LTTNG_ERR_UNK;
+ goto end;
+ }
+
+ break;
+ case LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION:
+ {
+ trigger_status = lttng_trigger_condition_add_error_results(
+ matching_trigger, results);
+
+ switch (trigger_status) {
+ case LTTNG_TRIGGER_STATUS_OK:
+ break;
+ default:
+ ret_code = LTTNG_ERR_UNK;
+ goto end;
+ }
+
+ break;
+ }
+ case LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION:
+ {
+ const enum lttng_action_status action_status =
+ lttng_action_add_error_query_results(
+ query_target_action, results);
+
+ switch (action_status) {
+ case LTTNG_ACTION_STATUS_OK:
+ break;
+ default:
+ ret_code = LTTNG_ERR_UNK;
+ goto end;
+ }
+
+ break;
+ }
+ default:
+ abort();
+ break;
+ }
+
+ *_results = results;
+ results = NULL;
+ ret_code = LTTNG_OK;
+end:
+ lttng_trigger_put(matching_trigger);
+ lttng_error_query_results_destroy(results);
+ return ret_code;
+}
+
+/*
+ * Send relayd sockets from snapshot output to consumer. Ignore request if the
+ * snapshot output is *not* set with a remote destination.
+ *
+ * Return LTTNG_OK on success or a LTTNG_ERR code.
+ */
+static enum lttng_error_code set_relayd_for_snapshot(
+ struct consumer_output *output,
+ const struct ltt_session *session)
+{
+ enum lttng_error_code status = LTTNG_OK;
+ struct lttng_ht_iter iter;
+ struct consumer_socket *socket;
+ LTTNG_OPTIONAL(uint64_t) current_chunk_id = {};
+ const char *base_path;
+
+ assert(output);
+ assert(session);
+
+ DBG2("Set relayd object from snapshot output");
+
+ if (session->current_trace_chunk) {
+ enum lttng_trace_chunk_status chunk_status =
+ lttng_trace_chunk_get_id(
+ session->current_trace_chunk,
+ ¤t_chunk_id.value);
+
+ if (chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK) {
+ current_chunk_id.is_set = true;
+ } else {
+ ERR("Failed to get current trace chunk id");
+ status = LTTNG_ERR_UNK;
+ goto error;
+ }
+ }
+
+ /* Ignore if snapshot consumer output is not network. */
+ if (output->type != CONSUMER_DST_NET) {
+ goto error;
+ }
+
+ /*
+ * The snapshot record URI base path overrides the session
+ * base path.
+ */
+ if (output->dst.net.control.subdir[0] != '\0') {
+ base_path = output->dst.net.control.subdir;
+ } else {
+ base_path = session->base_path;
+ }
+
+ /*
+ * For each consumer socket, create and send the relayd object of the