return ret;
}
+static ssize_t relay_unpack_rotate_streams_header(
+ const struct lttng_buffer_view *payload,
+ struct lttcomm_relayd_rotate_streams *_rotate_streams)
+{
+ struct lttcomm_relayd_rotate_streams rotate_streams;
+ /*
+ * Set to the smallest version (packed) of `lttcomm_relayd_rotate_streams`.
+ * This is the smallest version of this structure, but it can be larger;
+ * this variable is updated once the proper size of the structure is known.
+ *
+ * See comment at the declaration of this structure for more information.
+ */
+ ssize_t header_len = sizeof(struct lttcomm_relayd_rotate_streams_packed);
+ size_t expected_payload_size_no_padding,
+ expected_payload_size_3_bytes_padding,
+ expected_payload_size_7_bytes_padding;
+
+ if (payload->size < header_len) {
+ ERR("Unexpected payload size in \"relay_rotate_session_stream\": expected >= %zu bytes, got %zu bytes",
+ header_len, payload->size);
+ goto error;
+ }
+
+ /*
+ * Some versions incorrectly omitted the LTTNG_PACKED annotation on the
+ * `new_chunk_id` optional field of struct lttcomm_relayd_rotate_streams.
+ *
+ * We start by "unpacking" `stream_count` to figure out the padding length
+ * emited by our peer.
+ */
+ memcpy(&rotate_streams.stream_count, payload->data,
+ sizeof(rotate_streams.stream_count));
+ rotate_streams = (typeof(rotate_streams)) {
+ .stream_count = be32toh(rotate_streams.stream_count),
+ };
+
+ /*
+ * 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).
struct lttcomm_relayd_rotate_streams rotate_streams;
struct lttcomm_relayd_generic_reply reply = {};
struct relay_stream *stream = NULL;
- const size_t header_len = sizeof(struct lttcomm_relayd_rotate_streams);
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");
goto end_no_reply;
}
- if (payload->size < header_len) {
- ERR("Unexpected payload size in \"relay_rotate_session_stream\": expected >= %zu bytes, got %zu bytes",
- header_len, payload->size);
+ header_len = relay_unpack_rotate_streams_header(payload, &rotate_streams);
+ if (header_len < 0) {
ret = -1;
goto end_no_reply;
}
- memcpy(&rotate_streams, payload->data, header_len);
-
- /* Convert header to host endianness. */
- rotate_streams = (typeof(rotate_streams)) {
- .stream_count = be32toh(rotate_streams.stream_count),
- .new_chunk_id = (typeof(rotate_streams.new_chunk_id)) {
- .is_set = !!rotate_streams.new_chunk_id.is_set,
- .value = be64toh(rotate_streams.new_chunk_id.value),
- }
- };
-
if (rotate_streams.new_chunk_id.is_set) {
/*
* Retrieve the trace chunk the stream must transition to. As
chunk_id_str);
stream_positions = lttng_buffer_view_from_view(payload,
- sizeof(rotate_streams), -1);
+ header_len, -1);
if (!stream_positions.data ||
stream_positions.size <
(rotate_streams.stream_count *
return ret;
}
-
-
/*
* relay_create_trace_chunk: create a new trace chunk
*/
uint64_t rotate_at_seq_num;
} LTTNG_PACKED;
+/*
+ * For certain releases, the LTTNG_PACKED annotation was missing on the
+ * `new_chunk_id` field which causes padding to be added between the
+ * "optional" structure's `is_set` and `value` fields.
+ *
+ * Three alignment cases are handled:
+ * - `value` is aligned to the next byte boundary after `is_set`
+ * no padding is produced, see
+ * `struct lttcomm_relayd_rotate_streams_packed`,
+ * - `value` is aligned to the next 4-byte boundary after `is_set`
+ * (e.g. x86), 3 bytes of padding are produced, see
+ * `struct lttcomm_relayd_rotate_streams_3_bytes_padding`,
+ * - `value` is aligned to the next 8-byte boundary after `is_set`
+ * (e.g. x86-64), 7 bytes of padding are produced, see
+ * `struct lttcomm_relayd_rotate_streams_7_bytes_padding`.
+ *
+ * Note that since this structure's advertised size is used to determine
+ * the size of the padding it includes, it can't be extended with new
+ * optional fields. A new command would be needed.
+ */
struct lttcomm_relayd_rotate_streams {
uint32_t stream_count;
/*
* Streams can be rotated outside of a chunk but not be parented to
* a new chunk.
+ *
+ * Improperly packed, but left as-is for backwards compatibility
+ * with unpatched relay daemons.
*/
LTTNG_OPTIONAL_COMM(uint64_t) new_chunk_id;
/* `stream_count` positions follow. */
struct lttcomm_relayd_stream_rotation_position rotation_positions[];
} LTTNG_PACKED;
+struct lttcomm_relayd_rotate_streams_packed {
+ uint32_t stream_count;
+ LTTNG_OPTIONAL_COMM(uint64_t) LTTNG_PACKED new_chunk_id;
+ struct lttcomm_relayd_stream_rotation_position rotation_positions[];
+} LTTNG_PACKED;
+
+struct lttcomm_relayd_rotate_streams_3_bytes_padding {
+ uint32_t stream_count;
+ struct {
+ union {
+ uint8_t is_set;
+ uint32_t padding;
+ };
+ uint64_t value;
+ } LTTNG_PACKED new_chunk_id;
+ struct lttcomm_relayd_stream_rotation_position rotation_positions[];
+} LTTNG_PACKED;
+
+struct lttcomm_relayd_rotate_streams_7_bytes_padding {
+ uint32_t stream_count;
+ struct {
+ union {
+ uint8_t is_set;
+ uint64_t padding;
+ };
+ uint64_t value;
+ } LTTNG_PACKED new_chunk_id;
+ struct lttcomm_relayd_stream_rotation_position rotation_positions[];
+} LTTNG_PACKED;
+
struct lttcomm_relayd_create_trace_chunk {
uint64_t chunk_id;
/* Seconds since EPOCH. */