+ int ret = 0;
+ struct buffer_reg_uid *uid_reg = NULL;
+ struct buffer_reg_session *session_reg = NULL;
+
+ rcu_read_lock();
+ cds_list_for_each_entry(uid_reg, &usess->buffer_reg_uid_list, lnode) {
+ struct ust_registry_session *registry;
+ struct ust_registry_channel *chan;
+ struct lttng_ht_iter iter_chan;
+
+ session_reg = uid_reg->registry;
+ registry = session_reg->reg.ust;
+
+ pthread_mutex_lock(®istry->lock);
+ registry->metadata_len_sent = 0;
+ memset(registry->metadata, 0, registry->metadata_alloc_len);
+ registry->metadata_len = 0;
+ registry->metadata_version++;
+ if (registry->metadata_fd > 0) {
+ /* Clear the metadata file's content. */
+ ret = clear_metadata_file(registry->metadata_fd);
+ if (ret) {
+ pthread_mutex_unlock(®istry->lock);
+ goto end;
+ }
+ }
+
+ ret = ust_metadata_session_statedump(registry, NULL,
+ registry->major, registry->minor);
+ if (ret) {
+ pthread_mutex_unlock(®istry->lock);
+ ERR("Failed to generate session metadata (err = %d)",
+ ret);
+ goto end;
+ }
+ cds_lfht_for_each_entry(registry->channels->ht, &iter_chan.iter,
+ chan, node.node) {
+ struct ust_registry_event *event;
+ struct lttng_ht_iter iter_event;
+
+ ret = ust_metadata_channel_statedump(registry, chan);
+ if (ret) {
+ pthread_mutex_unlock(®istry->lock);
+ ERR("Failed to generate channel metadata "
+ "(err = %d)", ret);
+ goto end;
+ }
+ cds_lfht_for_each_entry(chan->ht->ht, &iter_event.iter,
+ event, node.node) {
+ ret = ust_metadata_event_statedump(registry,
+ chan, event);
+ if (ret) {
+ pthread_mutex_unlock(®istry->lock);
+ ERR("Failed to generate event metadata "
+ "(err = %d)", ret);
+ goto end;
+ }
+ }
+ }
+ pthread_mutex_unlock(®istry->lock);
+ }
+
+end:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Command LTTNG_REGENERATE_METADATA from the lttng-ctl library.
+ *
+ * Ask the consumer to truncate the existing metadata file(s) and
+ * then regenerate the metadata. Live and per-pid sessions are not
+ * supported and return an error.
+ *
+ * Return 0 on success or else a LTTNG_ERR code.
+ */
+int cmd_regenerate_metadata(struct ltt_session *session)
+{
+ int ret;
+
+ assert(session);
+
+ ret = check_regenerate_metadata_support(session);
+ if (ret) {
+ goto end;
+ }
+
+ if (session->kernel_session) {
+ ret = kernctl_session_regenerate_metadata(
+ session->kernel_session->fd);
+ if (ret < 0) {
+ ERR("Failed to regenerate the kernel metadata");
+ goto end;
+ }
+ }
+
+ if (session->ust_session) {
+ ret = ust_regenerate_metadata(session->ust_session);
+ if (ret < 0) {
+ ERR("Failed to regenerate the UST metadata");
+ goto end;
+ }
+ }
+ DBG("Cmd metadata regenerate for session %s", session->name);
+ ret = LTTNG_OK;
+
+end:
+ return ret;
+}
+
+/*
+ * Command LTTNG_REGENERATE_STATEDUMP from the lttng-ctl library.
+ *
+ * Ask the tracer to regenerate a new statedump.
+ *
+ * Return 0 on success or else a LTTNG_ERR code.
+ */
+int cmd_regenerate_statedump(struct ltt_session *session)
+{
+ int ret;
+
+ assert(session);
+
+ if (!session->active) {
+ ret = LTTNG_ERR_SESSION_NOT_STARTED;
+ goto end;
+ }
+
+ if (session->kernel_session) {
+ ret = kernctl_session_regenerate_statedump(
+ session->kernel_session->fd);
+ /*
+ * Currently, the statedump in kernel can only fail if out
+ * of memory.
+ */
+ if (ret < 0) {
+ if (ret == -ENOMEM) {
+ ret = LTTNG_ERR_REGEN_STATEDUMP_NOMEM;
+ } else {
+ ret = LTTNG_ERR_REGEN_STATEDUMP_FAIL;
+ }
+ ERR("Failed to regenerate the kernel statedump");
+ goto end;
+ }
+ }
+
+ if (session->ust_session) {
+ ret = ust_app_regenerate_statedump_all(session->ust_session);
+ /*
+ * Currently, the statedump in UST always returns 0.
+ */
+ if (ret < 0) {
+ ret = LTTNG_ERR_REGEN_STATEDUMP_FAIL;
+ ERR("Failed to regenerate the UST statedump");
+ goto end;
+ }
+ }
+ DBG("Cmd regenerate statedump for session %s", session->name);
+ ret = LTTNG_OK;
+
+end:
+ return ret;
+}
+
+/*
+ * On success LTTNG_OK. On error, returns lttng_error code.
+ */
+static enum lttng_error_code prepare_trigger_object(struct lttng_trigger *trigger, const struct lttng_credentials *cmd_creds)
+{
+ enum lttng_error_code ret;
+ /* Internal object of the trigger might have to "generate" and
+ * "populate" internal field e.g filter bytecode
+ */
+ struct lttng_condition *condition = NULL;
+
+ condition = lttng_trigger_get_condition(trigger);
+ if (!condition) {
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+
+ switch (lttng_condition_get_type(condition)) {
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ {
+ struct lttng_event_rule *event_rule;
+ lttng_condition_event_rule_get_rule_mutable(
+ condition, &event_rule);
+ ret = lttng_event_rule_generate_filter_bytecode(
+ event_rule, lttng_credentials_get_uid(cmd_creds), lttng_credentials_get_gid(cmd_creds));
+ if (ret != LTTNG_OK) {
+ goto end;
+ }
+
+ /* Generate the capture bytecode set */
+ ret = lttng_condition_event_rule_generate_capture_descriptor_bytecode_set(
+ condition, &trigger->capture_bytecode_set);
+ if (ret != LTTNG_OK) {
+ goto end;
+ }
+
+ ret = LTTNG_OK;
+ break;
+ }
+ default:
+ {
+ ret = LTTNG_OK;
+ break;
+ }
+ }
+
+end:
+ return ret;
+}
+
+int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock,
+ struct notification_thread_handle *notification_thread,
+ struct lttng_trigger **return_trigger)
+{
+ int ret;
+ size_t trigger_len;
+ ssize_t sock_recv_len;
+ struct lttng_trigger *trigger = NULL;
+ struct lttng_payload trigger_payload;
+ struct lttng_credentials cmd_creds = {
+ .uid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.uid),
+ .gid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.gid),
+ };
+
+ lttng_payload_init(&trigger_payload);
+ trigger_len = (size_t) cmd_ctx->lsm.u.trigger.length;
+ ret = lttng_dynamic_buffer_set_size(
+ &trigger_payload.buffer, trigger_len);
+ if (ret) {
+ ret = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ sock_recv_len = lttcomm_recv_unix_sock(
+ sock, trigger_payload.buffer.data, trigger_len);
+ if (sock_recv_len < 0 || sock_recv_len != trigger_len) {
+ ERR("Failed to receive \"register trigger\" command payload");
+ /* TODO: should this be a new error enum ? */
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+
+ /* Receive fds, if any. */
+ if (cmd_ctx->lsm.fd_count > 0) {
+ ret = lttcomm_recv_payload_fds_unix_sock(
+ sock, cmd_ctx->lsm.fd_count, &trigger_payload);
+ if (ret > 0 && ret != cmd_ctx->lsm.fd_count * sizeof(int)) {
+ ret = LTTNG_ERR_INVALID_PROTOCOL;
+ goto end;
+ } else if (ret <= 0) {
+ ret = LTTNG_ERR_FATAL;
+ goto end;
+ }
+ }
+
+ /* Deserialize trigger. */
+ {
+ struct lttng_payload_view view =
+ lttng_payload_view_from_payload(
+ &trigger_payload, 0, -1);
+
+ if (lttng_trigger_create_from_payload(&view, &trigger) !=
+ trigger_len) {
+ ERR("Invalid trigger payload received in \"register trigger\" command");
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+ }
+
+ /*
+ * Validate the trigger credentials against the command credentials.
+ * Only the root user can register 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");
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+ }
+
+ /*
+ * Since we return the trigger object, take a reference to it
+ * Caller is responsible for calling lttng_destroy_trigger on it.
+ * This thread does not OWN the trigger.
+ */
+ lttng_trigger_get(trigger);
+
+ /* Prepare internal trigger object if needed on reception.
+ * Handles also special treatment for certain internal object of the
+ * trigger (e.g uprobe event rule binary fd.
+ */
+ ret = prepare_trigger_object(trigger, &cmd_creds);
+ if (ret != LTTNG_OK) {
+ goto end;
+ }
+
+ /* Inform the notification thread */
+ ret = notification_thread_command_register_trigger(notification_thread,
+ trigger);
+ if (ret != LTTNG_OK) {
+ goto end;
+ }
+
+ /* Synchronize tracers, only if needed */
+ /* TODO: maybe extract somewhere else */
+ {
+ struct lttng_condition *condition = NULL;
+ condition = lttng_trigger_get_condition(trigger);
+ if (!condition) {
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+
+ if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
+ const struct lttng_event_rule *rule = NULL;
+ (void) lttng_condition_event_rule_get_rule(condition, &rule);
+ if (!rule) {
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+ if (lttng_event_rule_get_domain_type(rule) == LTTNG_DOMAIN_KERNEL) {
+ /* TODO: get the token value from the
+ * notification thread and only perform an
+ * enable and a disable.... This is NOT
+ * OPTIMIZED AT ALL
+ */
+ ret = kernel_register_trigger(trigger, &cmd_creds);
+ if (ret != LTTNG_OK) {
+ enum lttng_error_code notif_thread_unregister_ret =
+ notification_thread_command_unregister_trigger(
+ notification_thread,
+ trigger);
+ if (notif_thread_unregister_ret != LTTNG_OK) {
+ ERR("Error unregistering notification thread trigger after kernel registration failure.");
+ }
+ goto end;
+
+ }
+ } else {
+ /* TODO: get the token value from the
+ * notification thread and only perform an
+ * enable and a disable.... This is NOT
+ * OPTIMIZED AT ALL
+ */
+ ust_app_global_update_all_tokens();
+ /* Agent handling */
+ if (lttng_event_rule_is_agent(rule)) {
+ struct agent *agt;
+ const char *pattern;
+ enum lttng_domain_type domain_type;
+ domain_type = lttng_event_rule_get_domain_type(
+ rule);
+ (void) lttng_event_rule_tracepoint_get_pattern(
+ rule, &pattern);
+ agt = trigger_find_agent(domain_type);
+ if (!agt) {
+ agt = agent_create(domain_type);
+ if (!agt) {
+ ret = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+ agent_add(agt, trigger_agents_ht_by_domain);
+ }
+
+ ret = trigger_agent_enable(
+ trigger, agt);
+ if (ret != LTTNG_OK) {
+ goto end;
+ }
+ }
+ }
+ }
+ }
+
+ /* Return an image of the updated object to the client */
+ *return_trigger = trigger;
+ /* Ownership of trigger was transferred to caller. */
+ trigger = NULL;
+end:
+ lttng_trigger_destroy(trigger);
+ lttng_payload_reset(&trigger_payload);
+ return ret;
+}
+
+int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock,
+ struct notification_thread_handle *notification_thread)
+{
+ int ret;
+ size_t trigger_len;
+ ssize_t sock_recv_len;
+ struct lttng_trigger *trigger = NULL;
+ struct lttng_payload trigger_payload;
+ struct lttng_credentials cmd_creds = {
+ .uid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.uid),
+ .gid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.gid),
+ };
+
+ lttng_payload_init(&trigger_payload);
+ trigger_len = (size_t) cmd_ctx->lsm.u.trigger.length;
+ ret = lttng_dynamic_buffer_set_size(
+ &trigger_payload.buffer, trigger_len);
+ if (ret) {
+ ret = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ sock_recv_len = lttcomm_recv_unix_sock(
+ sock, trigger_payload.buffer.data, trigger_len);
+ if (sock_recv_len < 0 || sock_recv_len != trigger_len) {
+ ERR("Failed to receive \"unregister trigger\" command payload");
+ /* TODO: should this be a new error enum ? */
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+
+ /* Receive fds, if any. */
+ if (cmd_ctx->lsm.fd_count > 0) {
+ ret = lttcomm_recv_payload_fds_unix_sock(
+ sock, cmd_ctx->lsm.fd_count, &trigger_payload);
+ if (ret > 0 && ret != cmd_ctx->lsm.fd_count * sizeof(int)) {
+ ret = LTTNG_ERR_INVALID_PROTOCOL;
+ goto end;
+ } else if (ret <= 0) {
+ ret = LTTNG_ERR_FATAL;
+ goto end;
+ }
+ }
+
+ {
+ struct lttng_payload_view view =
+ lttng_payload_view_from_payload(
+ &trigger_payload, 0, -1);
+
+ if (lttng_trigger_create_from_payload(&view, &trigger) !=
+ trigger_len) {
+ ERR("Invalid trigger payload received in \"unregister trigger\" command");
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+ }
+
+ /*
+ * 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");
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+ }
+
+ ret = prepare_trigger_object(trigger, &cmd_creds);
+ if (ret != LTTNG_OK) {
+ goto end;
+ }
+
+ ret = notification_thread_command_unregister_trigger(notification_thread,
+ trigger);
+
+ /* Synchronize tracers, only if needed */
+ /* TODO: maybe extract somewhere else */
+ {
+ struct lttng_condition *condition = NULL;
+ condition = lttng_trigger_get_condition(trigger);
+ if (!condition) {
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+
+ if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
+ const struct lttng_event_rule *rule = NULL;
+ (void) lttng_condition_event_rule_get_rule(condition, &rule);
+ if (!rule) {
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+ if (lttng_event_rule_get_domain_type(rule) == LTTNG_DOMAIN_KERNEL) {
+ /* TODO: get the token value from the
+ * notification thread and only perform an
+ * enable and a disable.... This is NOT
+ * OPTIMIZED AT ALL
+ */
+ ret = kernel_unregister_trigger(trigger);
+ } else {
+ /* TODO: get the token value from the
+ * notification thread and only perform an
+ * enable and a disable.... This is NOT
+ * OPTIMIZED AT ALL
+ */
+ ust_app_global_update_all_tokens();
+ if (lttng_event_rule_is_agent(rule)) {
+ struct agent *agt;
+ const char *pattern;
+ enum lttng_domain_type domain_type;
+
+ domain_type = lttng_event_rule_get_domain_type(
+ rule);
+ (void) lttng_event_rule_tracepoint_get_pattern(
+ rule, &pattern);
+
+ agt = trigger_find_agent(domain_type);
+ if (!agt) {
+ ret = LTTNG_ERR_UST_EVENT_NOT_FOUND;
+ goto end;
+ }
+ ret = trigger_agent_disable(
+ trigger, agt);
+ if (ret != LTTNG_OK) {
+ goto end;
+ }
+ }
+ }
+ }
+ }
+
+end:
+ lttng_trigger_destroy(trigger);
+ lttng_payload_reset(&trigger_payload);
+ return ret;
+}
+
+int cmd_list_triggers(struct command_ctx *cmd_ctx,
+ struct notification_thread_handle *notification_thread,
+ struct lttng_triggers **return_triggers)
+{
+ int ret = 0;
+ enum lttng_error_code ret_code;
+ struct lttng_triggers *triggers = NULL;
+
+ /* Get list of token trigger from the notification thread here */
+ ret_code = notification_thread_command_list_triggers(notification_thread, cmd_ctx->creds.uid, &triggers);
+ if (ret_code != LTTNG_OK) {
+ ret = ret_code;
+ goto end;
+ }
+
+ /* Return a "view" of the current triggers */
+ *return_triggers = triggers;
+ triggers = NULL;
+ ret = LTTNG_OK;
+end:
+ lttng_triggers_destroy(triggers);
+ return ret;
+}
+/*
+ * 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
+ * snapshot output.
+ */
+ rcu_read_lock();
+ cds_lfht_for_each_entry(output->socks->ht, &iter.iter,
+ socket, node.node) {
+ pthread_mutex_lock(socket->lock);
+ status = send_consumer_relayd_sockets(0, session->id,
+ output, socket,
+ session->name, session->hostname,
+ base_path,
+ session->live_timer,
+ current_chunk_id.is_set ? ¤t_chunk_id.value : NULL,
+ session->creation_time,
+ session->name_contains_creation_time);
+ pthread_mutex_unlock(socket->lock);
+ if (status != LTTNG_OK) {
+ rcu_read_unlock();
+ goto error;
+ }
+ }
+ rcu_read_unlock();
+
+error:
+ return status;
+}
+
+/*
+ * Record a kernel snapshot.
+ *
+ * Return LTTNG_OK on success or a LTTNG_ERR code.
+ */
+static enum lttng_error_code record_kernel_snapshot(
+ struct ltt_kernel_session *ksess,
+ const struct consumer_output *output,
+ const struct ltt_session *session,
+ int wait, uint64_t nb_packets_per_stream)
+{
+ enum lttng_error_code status;
+
+ assert(ksess);
+ assert(output);
+ assert(session);
+
+ status = kernel_snapshot_record(
+ ksess, output, wait, nb_packets_per_stream);
+ return status;
+}
+
+/*
+ * Record a UST snapshot.
+ *
+ * Returns LTTNG_OK on success or a LTTNG_ERR error code.
+ */
+static enum lttng_error_code record_ust_snapshot(struct ltt_ust_session *usess,
+ const struct consumer_output *output,
+ const struct ltt_session *session,
+ int wait, uint64_t nb_packets_per_stream)
+{
+ enum lttng_error_code status;