+ /*
+ * Payload size expected given the possible padding lengths in
+ * `struct lttcomm_relayd_rotate_streams`.
+ */
+ expected_payload_size_no_padding = (rotate_streams.stream_count *
+ sizeof(*rotate_streams.rotation_positions)) +
+ sizeof(struct lttcomm_relayd_rotate_streams_packed);
+ expected_payload_size_3_bytes_padding = (rotate_streams.stream_count *
+ sizeof(*rotate_streams.rotation_positions)) +
+ sizeof(struct lttcomm_relayd_rotate_streams_3_bytes_padding);
+ expected_payload_size_7_bytes_padding = (rotate_streams.stream_count *
+ sizeof(*rotate_streams.rotation_positions)) +
+ sizeof(struct lttcomm_relayd_rotate_streams_7_bytes_padding);
+
+ if (payload->size == expected_payload_size_no_padding) {
+ struct lttcomm_relayd_rotate_streams_packed packed_rotate_streams;
+
+ /*
+ * This handles cases where someone might build with
+ * -fpack-struct or any other toolchain that wouldn't produce
+ * padding to align `value`.
+ */
+ DBG("Received `struct lttcomm_relayd_rotate_streams` with no padding");
+
+ header_len = sizeof(packed_rotate_streams);
+ memcpy(&packed_rotate_streams, payload->data, header_len);
+
+ /* Unpack the packed structure to the natively-packed version. */
+ *_rotate_streams = (typeof(*_rotate_streams)) {
+ .stream_count = be32toh(packed_rotate_streams.stream_count),
+ .new_chunk_id = (typeof(_rotate_streams->new_chunk_id)) {
+ .is_set = !!packed_rotate_streams.new_chunk_id.is_set,
+ .value = be64toh(packed_rotate_streams.new_chunk_id.value),
+ }
+ };
+ } else if (payload->size == expected_payload_size_3_bytes_padding) {
+ struct lttcomm_relayd_rotate_streams_3_bytes_padding padded_rotate_streams;
+
+ DBG("Received `struct lttcomm_relayd_rotate_streams` with 3 bytes of padding (4-byte aligned peer)");
+
+ header_len = sizeof(padded_rotate_streams);
+ memcpy(&padded_rotate_streams, payload->data, header_len);
+
+ /* Unpack the 3-byte padded structure to the natively-packed version. */
+ *_rotate_streams = (typeof(*_rotate_streams)) {
+ .stream_count = be32toh(padded_rotate_streams.stream_count),
+ .new_chunk_id = (typeof(_rotate_streams->new_chunk_id)) {
+ .is_set = !!padded_rotate_streams.new_chunk_id.is_set,
+ .value = be64toh(padded_rotate_streams.new_chunk_id.value),
+ }
+ };
+ } else if (payload->size == expected_payload_size_7_bytes_padding) {
+ struct lttcomm_relayd_rotate_streams_7_bytes_padding padded_rotate_streams;
+
+ DBG("Received `struct lttcomm_relayd_rotate_streams` with 7 bytes of padding (8-byte aligned peer)");
+
+ header_len = sizeof(padded_rotate_streams);
+ memcpy(&padded_rotate_streams, payload->data, header_len);
+
+ /* Unpack the 7-byte padded structure to the natively-packed version. */
+ *_rotate_streams = (typeof(*_rotate_streams)) {
+ .stream_count = be32toh(padded_rotate_streams.stream_count),
+ .new_chunk_id = (typeof(_rotate_streams->new_chunk_id)) {
+ .is_set = !!padded_rotate_streams.new_chunk_id.is_set,
+ .value = be64toh(padded_rotate_streams.new_chunk_id.value),
+ }
+ };
+
+ header_len = sizeof(padded_rotate_streams);
+ } else {
+ ERR("Unexpected payload size in \"relay_rotate_session_stream\": expected %zu, %zu or %zu bytes, got %zu bytes",
+ expected_payload_size_no_padding,
+ expected_payload_size_3_bytes_padding,
+ expected_payload_size_7_bytes_padding,
+ payload->size);
+ goto error;
+ }
+
+ return header_len;
+error:
+ return -1;
+}
+
+/*
+ * relay_rotate_session_stream: rotate a stream to a new tracefile for the
+ * session rotation feature (not the tracefile rotation feature).
+ */
+static int relay_rotate_session_streams(
+ const struct lttcomm_relayd_hdr *recv_hdr,
+ struct relay_connection *conn,
+ const struct lttng_buffer_view *payload)
+{
+ int ret = 0;
+ uint32_t i;
+ ssize_t send_ret;
+ enum lttng_error_code reply_code = LTTNG_ERR_UNK;
+ struct relay_session *session = conn->session;
+ struct lttcomm_relayd_rotate_streams rotate_streams;
+ struct lttcomm_relayd_generic_reply reply = {};
+ struct relay_stream *stream = NULL;
+ struct lttng_trace_chunk *next_trace_chunk = NULL;
+ struct lttng_buffer_view stream_positions;
+ char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)];
+ const char *chunk_id_str = "none";
+ ssize_t header_len;
+
+ if (!session || !conn->version_check_done) {
+ ERR("Trying to rotate a stream before version check");
+ ret = -1;
+ goto end_no_reply;
+ }
+
+ if (session->major == 2 && session->minor < 11) {
+ ERR("Unsupported feature before 2.11");
+ ret = -1;
+ goto end_no_reply;
+ }
+
+ header_len = relay_unpack_rotate_streams_header(payload, &rotate_streams);
+ if (header_len < 0) {
+ ret = -1;
+ goto end_no_reply;
+ }
+