+ tmp_output->nb_snapshot = session->snapshot.nb_snapshot;
+
+ /* Use the global datetime */
+ memcpy(tmp_output->datetime, datetime, sizeof(datetime));
+ cmd_ret = snapshot_record(session, tmp_output, wait);
+ if (cmd_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) {
+ struct snapshot_output output_copy;
+
+ /*
+ * Make a local copy of the output and override output
+ * parameters with those provided as part of the
+ * command.
+ */
+ memcpy(&output_copy, sout, sizeof(output_copy));
+
+ if (output->max_size != (uint64_t) -1ULL) {
+ output_copy.max_size = output->max_size;
+ }
+
+ output_copy.nb_snapshot = session->snapshot.nb_snapshot;
+ memcpy(output_copy.datetime, datetime,
+ sizeof(datetime));
+
+ /* Use temporary name. */
+ if (*output->name != '\0') {
+ if (lttng_strncpy(output_copy.name,
+ output->name,
+ sizeof(output_copy.name))) {
+ cmd_ret = LTTNG_ERR_INVALID;
+ rcu_read_unlock();
+ goto error;
+ }
+ }
+
+ cmd_ret = snapshot_record(session, &output_copy, wait);
+ if (cmd_ret != LTTNG_OK) {
+ rcu_read_unlock();
+ goto error;
+ }
+ snapshot_success = 1;
+ }
+ rcu_read_unlock();
+ }
+
+ if (snapshot_success) {
+ session->snapshot.nb_snapshot++;
+ } else {
+ cmd_ret = LTTNG_ERR_SNAPSHOT_FAIL;
+ }
+
+error:
+ if (tmp_output) {
+ snapshot_output_destroy(tmp_output);
+ }
+ return cmd_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;
+}
+
+/*
+ * Command LTTNG_ROTATE_SESSION from the lttng-ctl library.
+ *
+ * Ask the consumer to rotate the session output directory.
+ * The session lock must be held.
+ *
+ * Returns LTTNG_OK on success or else a negative LTTng error code.
+ */
+int cmd_rotate_session(struct ltt_session *session,
+ struct lttng_rotate_session_return *rotate_return,
+ bool quiet_rotation,
+ enum lttng_trace_chunk_command_type command)
+{
+ int ret;
+ uint64_t ongoing_rotation_chunk_id;
+ enum lttng_error_code cmd_ret = LTTNG_OK;
+ struct lttng_trace_chunk *chunk_being_archived = NULL;
+ struct lttng_trace_chunk *new_trace_chunk = NULL;
+ enum lttng_trace_chunk_status chunk_status;
+ bool failed_to_rotate = false;
+ enum lttng_error_code rotation_fail_code = LTTNG_OK;
+
+ assert(session);
+
+ if (!session->has_been_started) {
+ cmd_ret = LTTNG_ERR_START_SESSION_ONCE;
+ goto end;
+ }
+
+ /*
+ * Explicit rotation is not supported for live sessions.
+ * However, live sessions can perform a quiet rotation on
+ * destroy.
+ * Rotation is not supported for snapshot traces (no output).
+ */
+ if ((!quiet_rotation && session->live_timer) ||
+ !session->output_traces) {
+ cmd_ret = LTTNG_ERR_ROTATION_NOT_AVAILABLE;
+ goto end;
+ }
+
+ /* Unsupported feature in lttng-relayd before 2.11. */
+ if (!quiet_rotation && session->consumer->type == CONSUMER_DST_NET &&
+ (session->consumer->relay_major_version == 2 &&
+ session->consumer->relay_minor_version < 11)) {
+ cmd_ret = LTTNG_ERR_ROTATION_NOT_AVAILABLE_RELAY;
+ goto end;
+ }
+
+ /* Unsupported feature in lttng-modules before 2.8 (lack of sequence number). */
+ if (session->kernel_session && !kernel_supports_ring_buffer_packet_sequence_number()) {
+ cmd_ret = LTTNG_ERR_ROTATION_NOT_AVAILABLE_KERNEL;
+ goto end;
+ }
+
+ if (session->rotation_state == LTTNG_ROTATION_STATE_ONGOING) {
+ DBG("Refusing to launch a rotation; a rotation is already in progress for session %s",
+ session->name);
+ cmd_ret = LTTNG_ERR_ROTATION_PENDING;
+ goto end;
+ }
+
+ /*
+ * After a stop, we only allow one rotation to occur, the other ones are
+ * useless until a new start.
+ */
+ if (session->rotated_after_last_stop) {
+ DBG("Session \"%s\" was already rotated after stop, refusing rotation",
+ session->name);
+ cmd_ret = LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP;
+ goto end;
+ }
+
+ /*
+ * After a stop followed by a clear, disallow following rotations a they would
+ * generate empty chunks.
+ */
+ if (session->cleared_after_last_stop) {
+ DBG("Session \"%s\" was already cleared after stop, refusing rotation",
+ session->name);
+ cmd_ret = LTTNG_ERR_ROTATION_AFTER_STOP_CLEAR;
+ goto end;
+ }
+
+ if (session->active) {
+ new_trace_chunk = session_create_new_trace_chunk(session, NULL,
+ NULL, NULL);
+ if (!new_trace_chunk) {
+ cmd_ret = LTTNG_ERR_CREATE_DIR_FAIL;
+ goto error;
+ }