+ int64_t nb_packets_per_stream;
+ char snapshot_chunk_name[LTTNG_NAME_MAX];
+ int ret;
+ enum lttng_error_code ret_code = LTTNG_OK;
+ struct lttng_trace_chunk *snapshot_trace_chunk;
+ struct consumer_output *original_ust_consumer_output = NULL;
+ struct consumer_output *original_kernel_consumer_output = NULL;
+ struct consumer_output *snapshot_ust_consumer_output = NULL;
+ struct consumer_output *snapshot_kernel_consumer_output = NULL;
+
+ ret = snprintf(snapshot_chunk_name, sizeof(snapshot_chunk_name),
+ "%s-%s-%" PRIu64,
+ snapshot_output->name,
+ snapshot_output->datetime,
+ snapshot_output->nb_snapshot);
+ if (ret < 0 || ret >= sizeof(snapshot_chunk_name)) {
+ ERR("Failed to format snapshot name");
+ ret_code = LTTNG_ERR_INVALID;
+ goto error;
+ }
+ DBG("Recording snapshot \"%s\" for session \"%s\" with chunk name \"%s\"",
+ snapshot_output->name, session->name,
+ snapshot_chunk_name);
+ if (!session->kernel_session && !session->ust_session) {
+ ERR("Failed to record snapshot as no channels exist");
+ ret_code = LTTNG_ERR_NO_CHANNEL;
+ goto error;
+ }
+
+ if (session->kernel_session) {
+ original_kernel_consumer_output =
+ session->kernel_session->consumer;
+ snapshot_kernel_consumer_output =
+ consumer_copy_output(snapshot_output->consumer);
+ ret = consumer_copy_sockets(snapshot_kernel_consumer_output,
+ original_kernel_consumer_output);
+ if (ret < 0) {
+ ERR("Failed to copy consumer sockets from snapshot output configuration");
+ ret_code = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+ ret_code = set_relayd_for_snapshot(
+ snapshot_kernel_consumer_output, session);
+ if (ret_code != LTTNG_OK) {
+ ERR("Failed to setup relay daemon for kernel tracer snapshot");
+ goto error;
+ }
+ session->kernel_session->consumer =
+ snapshot_kernel_consumer_output;
+ }
+ if (session->ust_session) {
+ original_ust_consumer_output = session->ust_session->consumer;
+ snapshot_ust_consumer_output =
+ consumer_copy_output(snapshot_output->consumer);
+ ret = consumer_copy_sockets(snapshot_ust_consumer_output,
+ original_ust_consumer_output);
+ if (ret < 0) {
+ ERR("Failed to copy consumer sockets from snapshot output configuration");
+ ret_code = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+ ret_code = set_relayd_for_snapshot(
+ snapshot_ust_consumer_output, session);
+ if (ret_code != LTTNG_OK) {
+ ERR("Failed to setup relay daemon for userspace tracer snapshot");
+ goto error;
+ }
+ session->ust_session->consumer =
+ snapshot_ust_consumer_output;
+ }
+
+ snapshot_trace_chunk = session_create_new_trace_chunk(session,
+ snapshot_kernel_consumer_output ?:
+ snapshot_ust_consumer_output,
+ consumer_output_get_base_path(
+ snapshot_output->consumer),
+ snapshot_chunk_name);
+ if (!snapshot_trace_chunk) {
+ ERR("Failed to create temporary trace chunk to record a snapshot of session \"%s\"",
+ session->name);
+ ret_code = LTTNG_ERR_CREATE_DIR_FAIL;
+ goto error;
+ }
+ assert(!session->current_trace_chunk);
+ ret = session_set_trace_chunk(session, snapshot_trace_chunk, NULL);
+ lttng_trace_chunk_put(snapshot_trace_chunk);
+ snapshot_trace_chunk = NULL;
+ if (ret) {
+ ERR("Failed to set temporary trace chunk to record a snapshot of session \"%s\"",
+ session->name);
+ ret_code = LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER;
+ goto error;
+ }
+
+ nb_packets_per_stream = get_session_nb_packets_per_stream(session,
+ snapshot_output->max_size);
+ if (nb_packets_per_stream < 0) {
+ ret_code = LTTNG_ERR_MAX_SIZE_INVALID;
+ goto error;
+ }
+
+ if (session->kernel_session) {
+ ret_code = record_kernel_snapshot(session->kernel_session,
+ snapshot_kernel_consumer_output, session,
+ wait, nb_packets_per_stream);
+ if (ret_code != LTTNG_OK) {
+ goto error;
+ }
+ }
+
+ if (session->ust_session) {
+ ret_code = record_ust_snapshot(session->ust_session,
+ snapshot_ust_consumer_output, session,
+ wait, nb_packets_per_stream);
+ if (ret_code != LTTNG_OK) {
+ goto error;
+ }
+ }
+
+ if (session_close_trace_chunk(
+ session, session->current_trace_chunk, NULL)) {
+ /*
+ * Don't goto end; make sure the chunk is closed for the session
+ * to allow future snapshots.
+ */
+ ERR("Failed to close snapshot trace chunk of session \"%s\"",
+ session->name);
+ ret_code = LTTNG_ERR_CLOSE_TRACE_CHUNK_FAIL_CONSUMER;
+ }
+ if (session_set_trace_chunk(session, NULL, NULL)) {
+ ERR("Failed to release the current trace chunk of session \"%s\"",
+ session->name);
+ ret_code = LTTNG_ERR_UNK;
+ }
+error:
+ if (original_ust_consumer_output) {
+ session->ust_session->consumer = original_ust_consumer_output;
+ }
+ if (original_kernel_consumer_output) {
+ session->kernel_session->consumer =
+ original_kernel_consumer_output;
+ }
+ consumer_output_put(snapshot_ust_consumer_output);
+ consumer_output_put(snapshot_kernel_consumer_output);
+ return ret_code;
+}
+
+/*
+ * 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)
+{
+ enum lttng_error_code cmd_ret = LTTNG_OK;
+ int ret;
+ unsigned int snapshot_success = 0;
+ char datetime[16];
+ struct snapshot_output *tmp_output = NULL;
+
+ assert(session);