+/*
+ * Command LTTNG_SNAPSHOT_ADD_OUTPUT from the lttng ctl library.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR code.
+ */
+int cmd_snapshot_add_output(struct ltt_session *session,
+ struct lttng_snapshot_output *output, uint32_t *id)
+{
+ int ret;
+ struct snapshot_output *new_output;
+
+ assert(session);
+ assert(output);
+
+ DBG("Cmd snapshot add output for session %s", session->name);
+
+ /*
+ * Can't create an output if the session is not set in no-output mode.
+ */
+ if (session->output_traces) {
+ ret = LTTNG_ERR_NOT_SNAPSHOT_SESSION;
+ goto error;
+ }
+
+ /* Only one output is allowed until we have the "tee" feature. */
+ if (session->snapshot.nb_output == 1) {
+ ret = LTTNG_ERR_SNAPSHOT_OUTPUT_EXIST;
+ goto error;
+ }
+
+ new_output = snapshot_output_alloc();
+ if (!new_output) {
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+
+ ret = snapshot_output_init(output->max_size, output->name,
+ output->ctrl_url, output->data_url, session->consumer, new_output,
+ &session->snapshot);
+ if (ret < 0) {
+ if (ret == -ENOMEM) {
+ ret = LTTNG_ERR_NOMEM;
+ } else {
+ ret = LTTNG_ERR_INVALID;
+ }
+ goto free_error;
+ }
+
+ rcu_read_lock();
+ snapshot_add_output(&session->snapshot, new_output);
+ if (id) {
+ *id = new_output->id;
+ }
+ rcu_read_unlock();
+
+ return LTTNG_OK;
+
+free_error:
+ snapshot_output_destroy(new_output);
+error:
+ return ret;
+}
+
+/*
+ * Command LTTNG_SNAPSHOT_DEL_OUTPUT from lib lttng ctl.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR code.
+ */
+int cmd_snapshot_del_output(struct ltt_session *session,
+ struct lttng_snapshot_output *output)
+{
+ int ret;
+ struct snapshot_output *sout = NULL;
+
+ assert(session);
+ assert(output);
+
+ rcu_read_lock();
+
+ /*
+ * Permission denied to create an output if the session is not
+ * set in no output mode.
+ */
+ if (session->output_traces) {
+ ret = LTTNG_ERR_NOT_SNAPSHOT_SESSION;
+ goto error;
+ }
+
+ if (output->id) {
+ DBG("Cmd snapshot del output id %" PRIu32 " for session %s", output->id,
+ session->name);
+ sout = snapshot_find_output_by_id(output->id, &session->snapshot);
+ } else if (*output->name != '\0') {
+ DBG("Cmd snapshot del output name %s for session %s", output->name,
+ session->name);
+ sout = snapshot_find_output_by_name(output->name, &session->snapshot);
+ }
+ if (!sout) {
+ ret = LTTNG_ERR_INVALID;
+ goto error;
+ }
+
+ snapshot_delete_output(&session->snapshot, sout);
+ snapshot_output_destroy(sout);
+ ret = LTTNG_OK;
+
+error:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Command LTTNG_SNAPSHOT_LIST_OUTPUT from lib lttng ctl.
+ *
+ * If no output is available, outputs is untouched and 0 is returned.
+ *
+ * Return the size of the newly allocated outputs or a negative LTTNG_ERR code.
+ */
+ssize_t cmd_snapshot_list_outputs(struct ltt_session *session,
+ struct lttng_snapshot_output **outputs)
+{
+ int ret, idx = 0;
+ struct lttng_snapshot_output *list = NULL;
+ struct lttng_ht_iter iter;
+ struct snapshot_output *output;
+
+ assert(session);
+ assert(outputs);
+
+ DBG("Cmd snapshot list outputs for session %s", session->name);
+
+ /*
+ * Permission denied to create an output if the session is not
+ * set in no output mode.
+ */
+ if (session->output_traces) {
+ ret = -LTTNG_ERR_NOT_SNAPSHOT_SESSION;
+ goto end;
+ }
+
+ if (session->snapshot.nb_output == 0) {
+ ret = 0;
+ goto end;
+ }
+
+ list = zmalloc(session->snapshot.nb_output * sizeof(*list));
+ if (!list) {
+ ret = -LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ /* Copy list from session to the new list object. */
+ rcu_read_lock();
+ cds_lfht_for_each_entry(session->snapshot.output_ht->ht, &iter.iter,
+ output, node.node) {
+ assert(output->consumer);
+ list[idx].id = output->id;
+ list[idx].max_size = output->max_size;
+ if (lttng_strncpy(list[idx].name, output->name,
+ sizeof(list[idx].name))) {
+ ret = -LTTNG_ERR_INVALID;
+ goto error;
+ }
+ if (output->consumer->type == CONSUMER_DST_LOCAL) {
+ if (lttng_strncpy(list[idx].ctrl_url,
+ output->consumer->dst.trace_path,
+ sizeof(list[idx].ctrl_url))) {
+ ret = -LTTNG_ERR_INVALID;
+ goto error;
+ }
+ } else {
+ /* Control URI. */
+ ret = uri_to_str_url(&output->consumer->dst.net.control,
+ list[idx].ctrl_url, sizeof(list[idx].ctrl_url));
+ if (ret < 0) {
+ ret = -LTTNG_ERR_NOMEM;
+ goto error;
+ }
+
+ /* Data URI. */
+ ret = uri_to_str_url(&output->consumer->dst.net.data,
+ list[idx].data_url, sizeof(list[idx].data_url));
+ if (ret < 0) {
+ ret = -LTTNG_ERR_NOMEM;
+ goto error;
+ }
+ }
+ idx++;
+ }
+
+ *outputs = list;
+ list = NULL;
+ ret = session->snapshot.nb_output;
+error:
+ rcu_read_unlock();
+ free(list);
+end:
+ return ret;
+}
+
+/*
+ * Check if we can regenerate the metadata for this session.
+ * Only kernel, UST per-uid and non-live sessions are supported.
+ *
+ * Return 0 if the metadata can be generated, a LTTNG_ERR code otherwise.
+ */
+static
+int check_regenerate_metadata_support(struct ltt_session *session)
+{
+ int ret;
+
+ assert(session);
+
+ if (session->live_timer != 0) {
+ ret = LTTNG_ERR_LIVE_SESSION;
+ goto end;
+ }
+ if (!session->active) {
+ ret = LTTNG_ERR_SESSION_NOT_STARTED;
+ goto end;
+ }
+ if (session->ust_session) {
+ switch (session->ust_session->buffer_type) {
+ case LTTNG_BUFFER_PER_UID:
+ break;
+ case LTTNG_BUFFER_PER_PID:
+ ret = LTTNG_ERR_PER_PID_SESSION;
+ goto end;
+ default:
+ assert(0);
+ ret = LTTNG_ERR_UNK;
+ goto end;
+ }
+ }
+ if (session->consumer->type == CONSUMER_DST_NET &&
+ session->consumer->relay_minor_version < 8) {
+ ret = LTTNG_ERR_RELAYD_VERSION_FAIL;
+ goto end;
+ }
+ ret = 0;
+
+end:
+ return ret;
+}
+
+static
+int clear_metadata_file(int fd)
+{
+ int ret;
+
+ ret = lseek(fd, 0, SEEK_SET);
+ if (ret < 0) {
+ PERROR("lseek");
+ goto end;
+ }
+
+ ret = ftruncate(fd, 0);
+ if (ret < 0) {
+ PERROR("ftruncate");
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+static
+int ust_regenerate_metadata(struct ltt_ust_session *usess)
+{
+ 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;
+ }
+ ret = 0;
+
+ 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;
+}
+
+int cmd_register_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_buffer_view view;
+ struct lttng_dynamic_buffer trigger_buffer;
+
+ lttng_dynamic_buffer_init(&trigger_buffer);
+ trigger_len = (size_t) cmd_ctx->lsm->u.trigger.length;
+ ret = lttng_dynamic_buffer_set_size(&trigger_buffer, trigger_len);
+ if (ret) {
+ ret = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ sock_recv_len = lttcomm_recv_unix_sock(sock, trigger_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;
+ }
+
+ view = lttng_buffer_view_from_dynamic_buffer(&trigger_buffer, 0, -1);
+ if (lttng_trigger_create_from_buffer(&view, &trigger) !=
+ trigger_len) {
+ ERR("Invalid trigger payload received in \"register trigger\" command");
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+
+ ret = notification_thread_command_register_trigger(notification_thread,
+ trigger);
+end:
+ lttng_dynamic_buffer_reset(&trigger_buffer);
+ 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_buffer_view view;
+ struct lttng_dynamic_buffer trigger_buffer;
+
+ lttng_dynamic_buffer_init(&trigger_buffer);
+ trigger_len = (size_t) cmd_ctx->lsm->u.trigger.length;
+ ret = lttng_dynamic_buffer_set_size(&trigger_buffer, trigger_len);
+ if (ret) {
+ ret = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ sock_recv_len = lttcomm_recv_unix_sock(sock, trigger_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;
+ }
+
+ view = lttng_buffer_view_from_dynamic_buffer(&trigger_buffer, 0, -1);
+ if (lttng_trigger_create_from_buffer(&view, &trigger) !=
+ trigger_len) {
+ ERR("Invalid trigger payload received in \"unregister trigger\" command");
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+
+ ret = notification_thread_command_unregister_trigger(notification_thread,
+ trigger);
+end:
+ lttng_dynamic_buffer_reset(&trigger_buffer);
+ return ret;
+}
+
+/*
+ * Send relayd sockets from snapshot output to consumer. Ignore request if the
+ * snapshot output is *not* set with a remote destination.
+ *
+ * Return 0 on success or a LTTNG_ERR code.
+ */
+static int set_relayd_for_snapshot(struct consumer_output *consumer,
+ struct snapshot_output *snap_output, struct ltt_session *session)
+{
+ int ret = LTTNG_OK;
+ struct lttng_ht_iter iter;
+ struct consumer_socket *socket;
+
+ assert(consumer);
+ assert(snap_output);
+ assert(session);
+
+ DBG2("Set relayd object from snapshot output");
+
+ /* Ignore if snapshot consumer output is not network. */
+ if (snap_output->consumer->type != CONSUMER_DST_NET) {
+ goto error;
+ }
+
+ /*
+ * For each consumer socket, create and send the relayd object of the
+ * snapshot output.
+ */
+ rcu_read_lock();
+ cds_lfht_for_each_entry(snap_output->consumer->socks->ht, &iter.iter,
+ socket, node.node) {
+ ret = send_consumer_relayd_sockets(0, session->id,
+ snap_output->consumer, socket,
+ session->name, session->hostname,
+ session->live_timer);
+ if (ret != LTTNG_OK) {
+ rcu_read_unlock();
+ goto error;
+ }
+ }
+ rcu_read_unlock();
+
+error:
+ return ret;
+}
+
+/*
+ * Record a kernel snapshot.
+ *
+ * Return LTTNG_OK on success or a LTTNG_ERR code.
+ */
+static int record_kernel_snapshot(struct ltt_kernel_session *ksess,
+ struct snapshot_output *output, struct ltt_session *session,
+ int wait, uint64_t nb_packets_per_stream)
+{
+ int ret;
+
+ assert(ksess);
+ assert(output);
+ assert(session);
+
+
+ /*
+ * Copy kernel session sockets so we can communicate with the right
+ * consumer for the snapshot record command.
+ */
+ ret = consumer_copy_sockets(output->consumer, ksess->consumer);
+ if (ret < 0) {
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+
+ ret = set_relayd_for_snapshot(ksess->consumer, output, session);
+ if (ret != LTTNG_OK) {
+ goto error_snapshot;
+ }
+
+ ret = kernel_snapshot_record(ksess, output, wait, nb_packets_per_stream);
+ if (ret != LTTNG_OK) {
+ goto error_snapshot;
+ }
+
+ ret = LTTNG_OK;
+ goto end;
+
+error_snapshot:
+ /* Clean up copied sockets so this output can use some other later on. */
+ consumer_destroy_output_sockets(output->consumer);
+error:
+end:
+ return ret;
+}
+
+/*
+ * Record a UST snapshot.
+ *
+ * Return 0 on success or a LTTNG_ERR error code.
+ */
+static int record_ust_snapshot(struct ltt_ust_session *usess,
+ struct snapshot_output *output, struct ltt_session *session,
+ int wait, uint64_t nb_packets_per_stream)
+{
+ int ret;
+
+ assert(usess);
+ assert(output);
+ assert(session);
+
+ /*
+ * Copy UST session sockets so we can communicate with the right
+ * consumer for the snapshot record command.
+ */
+ ret = consumer_copy_sockets(output->consumer, usess->consumer);
+ if (ret < 0) {
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+
+ ret = set_relayd_for_snapshot(usess->consumer, output, session);
+ if (ret != LTTNG_OK) {
+ goto error_snapshot;
+ }
+
+ ret = ust_app_snapshot_record(usess, output, wait, nb_packets_per_stream);
+ if (ret < 0) {
+ switch (-ret) {
+ case EINVAL:
+ ret = LTTNG_ERR_INVALID;
+ break;
+ default:
+ ret = LTTNG_ERR_SNAPSHOT_FAIL;
+ break;
+ }
+ goto error_snapshot;
+ }
+
+ ret = LTTNG_OK;
+
+error_snapshot:
+ /* Clean up copied sockets so this output can use some other later on. */
+ consumer_destroy_output_sockets(output->consumer);
+error:
+ return ret;
+}
+
+static
+uint64_t get_session_size_one_more_packet_per_stream(struct ltt_session *session,
+ uint64_t cur_nr_packets)
+{
+ uint64_t tot_size = 0;
+
+ if (session->kernel_session) {
+ struct ltt_kernel_channel *chan;
+ struct ltt_kernel_session *ksess = session->kernel_session;
+
+ cds_list_for_each_entry(chan, &ksess->channel_list.head, list) {
+ if (cur_nr_packets >= chan->channel->attr.num_subbuf) {
+ /*
+ * Don't take channel into account if we
+ * already grab all its packets.
+ */
+ continue;
+ }
+ tot_size += chan->channel->attr.subbuf_size
+ * chan->stream_count;
+ }
+ }
+
+ if (session->ust_session) {
+ struct ltt_ust_session *usess = session->ust_session;
+
+ tot_size += ust_app_get_size_one_more_packet_per_stream(usess,
+ cur_nr_packets);
+ }
+
+ return tot_size;
+}
+
+/*
+ * Calculate the number of packets we can grab from each stream that
+ * fits within the overall snapshot max size.
+ *
+ * Returns -1 on error, 0 means infinite number of packets, else > 0 is
+ * the number of packets per stream.
+ *
+ * TODO: this approach is not perfect: we consider the worse case
+ * (packet filling the sub-buffers) as an upper bound, but we could do
+ * better if we do this calculation while we actually grab the packet
+ * content: we would know how much padding we don't actually store into
+ * the file.
+ *
+ * This algorithm is currently bounded by the number of packets per
+ * stream.
+ *
+ * Since we call this algorithm before actually grabbing the data, it's
+ * an approximation: for instance, applications could appear/disappear
+ * in between this call and actually grabbing data.
+ */
+static
+int64_t get_session_nb_packets_per_stream(struct ltt_session *session, uint64_t max_size)
+{
+ int64_t size_left;
+ uint64_t cur_nb_packets = 0;
+
+ if (!max_size) {
+ return 0; /* Infinite */
+ }
+
+ size_left = max_size;
+ for (;;) {
+ uint64_t one_more_packet_tot_size;
+
+ one_more_packet_tot_size = get_session_size_one_more_packet_per_stream(session,
+ cur_nb_packets);
+ if (!one_more_packet_tot_size) {
+ /* We are already grabbing all packets. */
+ break;
+ }
+ size_left -= one_more_packet_tot_size;
+ if (size_left < 0) {
+ break;
+ }
+ cur_nb_packets++;
+ }
+ if (!cur_nb_packets) {
+ /* Not enough room to grab one packet of each stream, error. */
+ return -1;
+ }
+ return cur_nb_packets;
+}
+
+/*
+ * Command LTTNG_SNAPSHOT_RECORD from lib lttng ctl.
+ *
+ * The wait parameter is ignored so this call always wait for the snapshot to
+ * complete before returning.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR code.
+ */
+int cmd_snapshot_record(struct ltt_session *session,
+ struct lttng_snapshot_output *output, int wait)
+{
+ int ret = LTTNG_OK;
+ unsigned int use_tmp_output = 0;
+ struct snapshot_output tmp_output;
+ unsigned int snapshot_success = 0;
+ char datetime[16];
+
+ assert(session);
+ assert(output);
+
+ DBG("Cmd snapshot record for session %s", session->name);
+
+ /* Get the datetime for the snapshot output directory. */
+ ret = utils_get_current_time_str("%Y%m%d-%H%M%S", datetime,
+ sizeof(datetime));
+ if (!ret) {
+ ret = LTTNG_ERR_INVALID;
+ goto error;
+ }
+
+ /*
+ * Permission denied to create an output if the session is not
+ * set in no output mode.
+ */
+ if (session->output_traces) {
+ ret = LTTNG_ERR_NOT_SNAPSHOT_SESSION;
+ goto error;
+ }
+
+ /* The session needs to be started at least once. */
+ if (!session->has_been_started) {
+ ret = LTTNG_ERR_START_SESSION_ONCE;
+ goto error;
+ }
+
+ /* Use temporary output for the session. */
+ if (*output->ctrl_url != '\0') {
+ ret = snapshot_output_init(output->max_size, output->name,
+ output->ctrl_url, output->data_url, session->consumer,
+ &tmp_output, NULL);
+ if (ret < 0) {
+ if (ret == -ENOMEM) {
+ ret = LTTNG_ERR_NOMEM;
+ } else {
+ ret = LTTNG_ERR_INVALID;
+ }
+ goto error;
+ }
+ /* Use the global session count for the temporary snapshot. */
+ tmp_output.nb_snapshot = session->snapshot.nb_snapshot;
+
+ /* Use the global datetime */
+ memcpy(tmp_output.datetime, datetime, sizeof(datetime));
+ use_tmp_output = 1;
+ }
+
+ if (use_tmp_output) {
+ int64_t nb_packets_per_stream;
+
+ nb_packets_per_stream = get_session_nb_packets_per_stream(session,
+ tmp_output.max_size);
+ if (nb_packets_per_stream < 0) {
+ ret = LTTNG_ERR_MAX_SIZE_INVALID;
+ goto error;
+ }
+
+ if (session->kernel_session) {
+ ret = record_kernel_snapshot(session->kernel_session,
+ &tmp_output, session,
+ wait, nb_packets_per_stream);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ }
+
+ if (session->ust_session) {
+ ret = record_ust_snapshot(session->ust_session,
+ &tmp_output, session,
+ wait, nb_packets_per_stream);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ }
+
+ snapshot_success = 1;
+ } else {
+ struct snapshot_output *sout;
+ struct lttng_ht_iter iter;
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(session->snapshot.output_ht->ht,
+ &iter.iter, sout, node.node) {
+ int64_t nb_packets_per_stream;
+
+ /*
+ * Make a local copy of the output and assign the possible
+ * temporary value given by the caller.
+ */
+ memset(&tmp_output, 0, sizeof(tmp_output));
+ memcpy(&tmp_output, sout, sizeof(tmp_output));
+
+ if (output->max_size != (uint64_t) -1ULL) {
+ tmp_output.max_size = output->max_size;
+ }
+
+ nb_packets_per_stream = get_session_nb_packets_per_stream(session,
+ tmp_output.max_size);
+ if (nb_packets_per_stream < 0) {
+ ret = LTTNG_ERR_MAX_SIZE_INVALID;
+ rcu_read_unlock();
+ goto error;
+ }
+
+ /* Use temporary name. */
+ if (*output->name != '\0') {
+ if (lttng_strncpy(tmp_output.name, output->name,
+ sizeof(tmp_output.name))) {
+ ret = LTTNG_ERR_INVALID;
+ rcu_read_unlock();
+ goto error;
+ }
+ }
+
+ tmp_output.nb_snapshot = session->snapshot.nb_snapshot;
+ memcpy(tmp_output.datetime, datetime, sizeof(datetime));
+
+ if (session->kernel_session) {
+ ret = record_kernel_snapshot(session->kernel_session,
+ &tmp_output, session,
+ wait, nb_packets_per_stream);
+ if (ret != LTTNG_OK) {
+ rcu_read_unlock();
+ goto error;
+ }
+ }
+
+ if (session->ust_session) {
+ ret = record_ust_snapshot(session->ust_session,
+ &tmp_output, session,
+ wait, nb_packets_per_stream);
+ if (ret != LTTNG_OK) {
+ rcu_read_unlock();
+ goto error;
+ }
+ }
+ snapshot_success = 1;
+ }
+ rcu_read_unlock();
+ }
+
+ if (snapshot_success) {
+ session->snapshot.nb_snapshot++;
+ } else {
+ ret = LTTNG_ERR_SNAPSHOT_FAIL;
+ }
+
+error:
+ return ret;
+}
+
+/*
+ * Command LTTNG_SET_SESSION_SHM_PATH processed by the client thread.
+ */
+int cmd_set_session_shm_path(struct ltt_session *session,
+ const char *shm_path)
+{
+ /* Safety net */
+ assert(session);
+
+ /*
+ * Can only set shm path before session is started.
+ */
+ if (session->has_been_started) {
+ return LTTNG_ERR_SESSION_STARTED;
+ }
+
+ strncpy(session->shm_path, shm_path,
+ sizeof(session->shm_path));
+ session->shm_path[sizeof(session->shm_path) - 1] = '\0';
+
+ return 0;
+}
+