X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-relayd%2Fmain.c;h=85c4fe9b9aca282ca2d35af5bfa18c910c261b55;hp=1683320188ef15bada61e84b4e2c7495acfc140e;hb=3aa937ab446c323bec53fc3581c424a3cc5c020e;hpb=46f4eaa5c2a4e5f106ca105e2a93018e8fd4e477 diff --git a/src/bin/lttng-relayd/main.c b/src/bin/lttng-relayd/main.c index 168332018..85c4fe9b9 100644 --- a/src/bin/lttng-relayd/main.c +++ b/src/bin/lttng-relayd/main.c @@ -74,6 +74,7 @@ #include "connection.h" #include "tracefile-array.h" #include "tcp_keep_alive.h" +#include "sessiond-trace-chunks.h" static const char *help_msg = #ifdef LTTNG_EMBED_HELP @@ -85,7 +86,7 @@ NULL enum relay_connection_status { RELAY_CONNECTION_STATUS_OK, - /* An error occured while processing an event on the connection. */ + /* An error occurred while processing an event on the connection. */ RELAY_CONNECTION_STATUS_ERROR, /* Connection closed/shutdown cleanly. */ RELAY_CONNECTION_STATUS_CLOSED, @@ -167,6 +168,8 @@ struct lttng_ht *sessions_ht; /* Relayd health monitoring */ struct health_app *health_relayd; +struct sessiond_trace_chunk_registry *sessiond_trace_chunk_registry; + static struct option long_options[] = { { "control-port", 1, 0, 'C', }, { "data-port", 1, 0, 'D', }, @@ -858,14 +861,6 @@ restart: revents = LTTNG_POLL_GETEV(&events, i); pollfd = LTTNG_POLL_GETFD(&events, i); - if (!revents) { - /* - * No activity for this FD (poll - * implementation). - */ - continue; - } - /* Thread quit pipe has been closed. Killing thread. */ ret = check_thread_quit_pipe(pollfd, revents); if (ret) { @@ -1083,6 +1078,11 @@ static int set_index_control_data(struct relay_index *index, return relay_index_set_data(index, &index_data); } +static bool session_streams_have_index(const struct relay_session *session) +{ + return session->minor >= 4 && !session->snapshot; +} + /* * Handle the RELAYD_CREATE_SESSION command. * @@ -1094,34 +1094,52 @@ static int relay_create_session(const struct lttcomm_relayd_hdr *recv_hdr, { int ret = 0; ssize_t send_ret; - struct relay_session *session; - struct lttcomm_relayd_status_session reply; - char session_name[LTTNG_NAME_MAX]; - char hostname[LTTNG_HOST_NAME_MAX]; + struct relay_session *session = NULL; + struct lttcomm_relayd_status_session reply = {}; + char session_name[LTTNG_NAME_MAX] = {}; + char hostname[LTTNG_HOST_NAME_MAX] = {}; uint32_t live_timer = 0; bool snapshot = false; + /* Left nil for peers < 2.11. */ + lttng_uuid sessiond_uuid = {}; + LTTNG_OPTIONAL(uint64_t) id_sessiond = {}; + LTTNG_OPTIONAL(uint64_t) current_chunk_id = {}; - memset(session_name, 0, LTTNG_NAME_MAX); - memset(hostname, 0, LTTNG_HOST_NAME_MAX); - - memset(&reply, 0, sizeof(reply)); - - switch (conn->minor) { - case 1: - case 2: - case 3: - break; - case 4: /* LTTng sessiond 2.4 */ - default: + if (conn->minor < 4) { + /* From 2.1 to 2.3 */ + ret = 0; + } else if (conn->minor >= 4 && conn->minor < 11) { + /* From 2.4 to 2.10 */ ret = cmd_create_session_2_4(payload, session_name, hostname, &live_timer, &snapshot); + } else { + bool has_current_chunk; + + /* From 2.11 to ... */ + ret = cmd_create_session_2_11(payload, session_name, + hostname, &live_timer, &snapshot, + &id_sessiond.value, sessiond_uuid, + &has_current_chunk, + ¤t_chunk_id.value); + if (lttng_uuid_is_nil(sessiond_uuid)) { + /* The nil UUID is reserved for pre-2.11 clients. */ + ERR("Illegal nil UUID announced by peer in create session command"); + ret = -1; + goto send_reply; + } + id_sessiond.is_set = true; + current_chunk_id.is_set = has_current_chunk; } + if (ret < 0) { goto send_reply; } session = session_create(session_name, hostname, live_timer, - snapshot, conn->major, conn->minor); + snapshot, sessiond_uuid, + id_sessiond.is_set ? &id_sessiond.value : NULL, + current_chunk_id.is_set ? ¤t_chunk_id.value : NULL, + conn->major, conn->minor); if (!session) { ret = -1; goto send_reply; @@ -1145,7 +1163,9 @@ send_reply: send_ret); ret = -1; } - + if (ret < 0 && session) { + session_put(session); + } return ret; } @@ -1195,6 +1215,7 @@ static int relay_add_stream(const struct lttcomm_relayd_hdr *recv_hdr, uint64_t stream_handle = -1ULL; char *path_name = NULL, *channel_name = NULL; uint64_t tracefile_size = 0, tracefile_count = 0; + struct relay_stream_chunk_id stream_chunk_id = { 0 }; if (!session || !conn->version_check_done) { ERR("Trying to add a stream before version check"); @@ -1202,17 +1223,22 @@ static int relay_add_stream(const struct lttcomm_relayd_hdr *recv_hdr, goto end_no_session; } - switch (session->minor) { - case 1: /* LTTng sessiond 2.1. Allocates path_name and channel_name. */ + if (session->minor == 1) { + /* For 2.1 */ ret = cmd_recv_stream_2_1(payload, &path_name, &channel_name); - break; - case 2: /* LTTng sessiond 2.2. Allocates path_name and channel_name. */ - default: + } else if (session->minor > 1 && session->minor < 11) { + /* From 2.2 to 2.10 */ ret = cmd_recv_stream_2_2(payload, &path_name, &channel_name, &tracefile_size, &tracefile_count); - break; + } else { + /* From 2.11 to ... */ + ret = cmd_recv_stream_2_11(payload, &path_name, + &channel_name, &tracefile_size, &tracefile_count, + &stream_chunk_id.value); + stream_chunk_id.is_set = true; } + if (ret < 0) { goto send_reply; } @@ -1229,7 +1255,8 @@ static int relay_add_stream(const struct lttcomm_relayd_hdr *recv_hdr, /* We pass ownership of path_name and channel_name. */ stream = stream_create(trace, stream_handle, path_name, - channel_name, tracefile_size, tracefile_count); + channel_name, tracefile_size, tracefile_count, + &stream_chunk_id); path_name = NULL; channel_name = NULL; @@ -1524,7 +1551,8 @@ end: * Return 0 on success, -1 on error. */ static -int create_rotate_index_file(struct relay_stream *stream) +int create_rotate_index_file(struct relay_stream *stream, + const char *stream_path) { int ret; uint32_t major, minor; @@ -1536,7 +1564,7 @@ int create_rotate_index_file(struct relay_stream *stream) } major = stream->trace->session->major; minor = stream->trace->session->minor; - stream->index_file = lttng_index_file_create(stream->path_name, + stream->index_file = lttng_index_file_create(stream_path, stream->channel_name, -1, -1, stream->tracefile_size, tracefile_array_get_file_index_head(stream->tfa), @@ -1554,10 +1582,12 @@ end: } static -int do_rotate_stream(struct relay_stream *stream) +int do_rotate_stream_data(struct relay_stream *stream) { int ret; + DBG("Rotating stream %" PRIu64 " data file", + stream->stream_handle); /* Perform the stream rotation. */ ret = utils_rotate_stream_file(stream->path_name, stream->channel_name, stream->tracefile_size, @@ -1569,19 +1599,17 @@ int do_rotate_stream(struct relay_stream *stream) goto end; } stream->tracefile_size_current = 0; - - /* Rotate also the index if the stream is not a metadata stream. */ - if (!stream->is_metadata) { - ret = create_rotate_index_file(stream); - if (ret < 0) { - ERR("Failed to rotate index file"); - goto end; - } - } - - stream->rotate_at_seq_num = -1ULL; stream->pos_after_last_complete_data_index = 0; + stream->data_rotated = true; + if (stream->data_rotated && stream->index_rotated) { + /* Rotation completed; reset its state. */ + DBG("Rotation completed for stream %" PRIu64, + stream->stream_handle); + stream->rotate_at_seq_num = -1ULL; + stream->data_rotated = false; + stream->index_rotated = false; + } end: return ret; } @@ -1593,9 +1621,7 @@ end: * connections are separate, the indexes as well as the commands arrive from * the control connection and we have no control over the order so we could be * in a situation where too much data has been received on the data connection - * before the rotation command on the control connection arrives. We don't need - * to update the index because its order is guaranteed with the rotation - * command message. + * before the rotation command on the control connection arrives. */ static int rotate_truncate_stream(struct relay_stream *stream) @@ -1626,7 +1652,7 @@ int rotate_truncate_stream(struct relay_stream *stream) /* * Rewind the current tracefile to the position at which the rotation - * should have occured. + * should have occurred. */ lseek_ret = lseek(stream->stream_fd->fd, stream->pos_after_last_complete_data_index, SEEK_SET); @@ -1694,12 +1720,6 @@ int rotate_truncate_stream(struct relay_stream *stream) goto end; } - ret = create_rotate_index_file(stream); - if (ret < 0) { - ERR("Rotate stream index file"); - goto end; - } - /* * Update the offset and FD of all the eventual indexes created by the * data connection before the rotation command arrived. @@ -1722,27 +1742,100 @@ end: } /* - * Check if a stream should perform a rotation (for session rotation). + * Check if a stream's index file should be rotated (for session rotation). * Must be called with the stream lock held. * * Return 0 on success, a negative value on error. */ static -int try_rotate_stream(struct relay_stream *stream) +int try_rotate_stream_index(struct relay_stream *stream) { int ret = 0; - /* No rotation expected. */ if (stream->rotate_at_seq_num == -1ULL) { + /* No rotation expected. */ + goto end; + } + + if (stream->index_rotated) { + /* Rotation of the index has already occurred. */ goto end; } - if (stream->prev_seq < stream->rotate_at_seq_num || - stream->prev_seq == -1ULL) { - DBG("Stream %" PRIu64 " no yet ready for rotation", + if (stream->prev_index_seq == -1ULL || + stream->prev_index_seq < stream->rotate_at_seq_num) { + DBG("Stream %" PRIu64 " index not yet ready for rotation (rotate_at_seq_num = %" PRIu64 ", prev_index_seq = %" PRIu64 ")", + stream->stream_handle, + stream->rotate_at_seq_num, + stream->prev_index_seq); + goto end; + } else if (stream->prev_index_seq != stream->rotate_at_seq_num) { + /* + * Unexpected, protocol error/bug. + * It could mean that we received a rotation position + * that is in the past. + */ + ERR("Stream %" PRIu64 " index is in an inconsistent state (rotate_at_seq_num = %" PRIu64 ", prev_data_seq = %" PRIu64 ", prev_index_seq = %" PRIu64 ")", + stream->stream_handle, + stream->rotate_at_seq_num, + stream->prev_data_seq, + stream->prev_index_seq); + ret = -1; + goto end; + } else { + DBG("Rotating stream %" PRIu64 " index file", stream->stream_handle); + ret = create_rotate_index_file(stream, stream->path_name); + stream->index_rotated = true; + + if (stream->data_rotated && stream->index_rotated) { + /* Rotation completed; reset its state. */ + DBG("Rotation completed for stream %" PRIu64, + stream->stream_handle); + stream->rotate_at_seq_num = -1ULL; + stream->data_rotated = false; + stream->index_rotated = false; + } + } + +end: + return ret; +} + +/* + * Check if a stream's data file (as opposed to index) should be rotated + * (for session rotation). + * Must be called with the stream lock held. + * + * Return 0 on success, a negative value on error. + */ +static +int try_rotate_stream_data(struct relay_stream *stream) +{ + int ret = 0; + + if (stream->rotate_at_seq_num == -1ULL) { + /* No rotation expected. */ + goto end; + } + + if (stream->data_rotated) { + /* Rotation of the data file has already occurred. */ goto end; - } else if (stream->prev_seq > stream->rotate_at_seq_num) { + } + + if (stream->prev_data_seq == -1ULL || + stream->prev_data_seq < stream->rotate_at_seq_num) { + DBG("Stream %" PRIu64 " not yet ready for rotation (rotate_at_seq_num = %" PRIu64 ", prev_data_seq = %" PRIu64 ")", + stream->stream_handle, + stream->rotate_at_seq_num, + stream->prev_data_seq); + goto end; + } else if (stream->prev_data_seq > stream->rotate_at_seq_num) { + /* + * prev_data_seq is checked here since indexes and rotation + * commands are serialized with respect to each other. + */ DBG("Rotation after too much data has been written in tracefile " "for stream %" PRIu64 ", need to truncate before " "rotating", stream->stream_handle); @@ -1751,11 +1844,20 @@ int try_rotate_stream(struct relay_stream *stream) ERR("Failed to truncate stream"); goto end; } + } else if (stream->prev_data_seq != stream->rotate_at_seq_num) { + /* + * Unexpected, protocol error/bug. + * It could mean that we received a rotation position + * that is in the past. + */ + ERR("Stream %" PRIu64 " data is in an inconsistent state (rotate_at_seq_num = %" PRIu64 ", prev_data_seq = %" PRIu64 ")", + stream->stream_handle, + stream->rotate_at_seq_num, + stream->prev_data_seq); + ret = -1; + goto end; } else { - /* stream->prev_seq == stream->rotate_at_seq_num */ - DBG("Stream %" PRIu64 " ready for rotation", - stream->stream_handle); - ret = do_rotate_stream(stream); + ret = do_rotate_stream_data(stream); } end: @@ -1826,7 +1928,7 @@ static int relay_recv_metadata(const struct lttcomm_relayd_hdr *recv_hdr, DBG2("Relay metadata written. Updated metadata_received %" PRIu64, metadata_stream->metadata_received); - ret = try_rotate_stream(metadata_stream); + ret = try_rotate_stream_data(metadata_stream); if (ret < 0) { goto end_put; } @@ -1921,6 +2023,7 @@ static int relay_data_pending(const struct lttcomm_relayd_hdr *recv_hdr, struct relay_stream *stream; ssize_t send_ret; int ret; + uint64_t stream_seq; DBG("Data pending command received"); @@ -1948,12 +2051,23 @@ static int relay_data_pending(const struct lttcomm_relayd_hdr *recv_hdr, pthread_mutex_lock(&stream->lock); - DBG("Data pending for stream id %" PRIu64 " prev_seq %" PRIu64 - " and last_seq %" PRIu64, msg.stream_id, - stream->prev_seq, msg.last_net_seq_num); + if (session_streams_have_index(session)) { + /* + * Ensure that both the index and stream data have been + * flushed up to the requested point. + */ + stream_seq = min(stream->prev_data_seq, stream->prev_index_seq); + } else { + stream_seq = stream->prev_data_seq; + } + DBG("Data pending for stream id %" PRIu64 ": prev_data_seq %" PRIu64 + ", prev_index_seq %" PRIu64 + ", and last_seq %" PRIu64, msg.stream_id, + stream->prev_data_seq, stream->prev_index_seq, + msg.last_net_seq_num); /* Avoid wrapping issue */ - if (((int64_t) (stream->prev_seq - msg.last_net_seq_num)) >= 0) { + if (((int64_t) (stream_seq - msg.last_net_seq_num)) >= 0) { /* Data has in fact been written and is NOT pending */ ret = 0; } else { @@ -2173,7 +2287,18 @@ static int relay_end_data_pending(const struct lttcomm_relayd_hdr *recv_hdr, } pthread_mutex_lock(&stream->lock); if (!stream->data_pending_check_done) { - if (!stream->closed || !(((int64_t) (stream->prev_seq - stream->last_net_seq_num)) >= 0)) { + uint64_t stream_seq; + + if (session_streams_have_index(conn->session)) { + /* + * Ensure that both the index and stream data have been + * flushed up to the requested point. + */ + stream_seq = min(stream->prev_data_seq, stream->prev_index_seq); + } else { + stream_seq = stream->prev_data_seq; + } + if (!stream->closed || !(((int64_t) (stream_seq - stream->last_net_seq_num)) >= 0)) { is_data_inflight = 1; DBG("Data is still in flight for stream %" PRIu64, stream->stream_handle); @@ -2304,12 +2429,23 @@ static int relay_recv_index(const struct lttcomm_relayd_hdr *recv_hdr, tracefile_array_commit_seq(stream->tfa); stream->index_received_seqcount++; stream->pos_after_last_complete_data_index += index->total_size; + stream->prev_index_seq = index_info.net_seq_num; + + ret = try_rotate_stream_index(stream); + if (ret < 0) { + goto end_stream_put; + } } else if (ret > 0) { /* no flush. */ ret = 0; } else { + /* + * ret < 0 + * + * relay_index_try_flush is responsible for the self-reference + * put of the index object on error. + */ ERR("relay_index_try_flush error %d", ret); - relay_index_put(index); ret = -1; } @@ -2460,7 +2596,8 @@ static int relay_rotate_session_stream(const struct lttcomm_relayd_hdr *recv_hdr * Update the trace path (just the folder, the stream name does not * change). */ - free(stream->path_name); + free(stream->prev_path_name); + stream->prev_path_name = stream->path_name; stream->path_name = create_output_path(new_path_view.data); if (!stream->path_name) { ERR("Failed to create a new output path"); @@ -2475,21 +2612,32 @@ static int relay_rotate_session_stream(const struct lttcomm_relayd_hdr *recv_hdr goto end_stream_unlock; } - stream->chunk_id = stream_info.new_chunk_id; + assert(stream->current_chunk_id.is_set); + stream->current_chunk_id.value = stream_info.new_chunk_id; if (stream->is_metadata) { + /* + * Metadata streams have no index; consider its rotation + * complete. + */ + stream->index_rotated = true; /* * The metadata stream is sent only over the control connection * so we know we have all the data to perform the stream * rotation. */ - ret = do_rotate_stream(stream); + ret = do_rotate_stream_data(stream); } else { stream->rotate_at_seq_num = stream_info.rotate_at_seq_num; - ret = try_rotate_stream(stream); - } - if (ret < 0) { - goto end_stream_unlock; + ret = try_rotate_stream_data(stream); + if (ret < 0) { + goto end_stream_unlock; + } + + ret = try_rotate_stream_index(stream); + if (ret < 0) { + goto end_stream_unlock; + } } end_stream_unlock: @@ -2514,359 +2662,6 @@ end_no_reply: return ret; } -/* - * relay_mkdir: Create a folder on the disk. - */ -static int relay_mkdir(const struct lttcomm_relayd_hdr *recv_hdr, - struct relay_connection *conn, - const struct lttng_buffer_view *payload) -{ - int ret; - struct relay_session *session = conn->session; - struct lttcomm_relayd_mkdir path_info_header; - struct lttcomm_relayd_generic_reply reply; - char *path = NULL; - size_t header_len; - ssize_t send_ret; - struct lttng_buffer_view path_view; - - if (!session || !conn->version_check_done) { - ERR("Trying to create a directory before version check"); - ret = -1; - goto end_no_session; - } - - if (session->major == 2 && session->minor < 11) { - /* - * This client is not supposed to use this command since - * it predates its introduction. - */ - ERR("relay_mkdir command is unsupported before LTTng 2.11"); - ret = -1; - goto end_no_session; - } - - header_len = sizeof(path_info_header); - if (payload->size < header_len) { - ERR("Unexpected payload size in \"relay_mkdir\": expected >= %zu bytes, got %zu bytes", - header_len, payload->size); - ret = -1; - goto end_no_session; - } - - memcpy(&path_info_header, payload->data, header_len); - - path_info_header.length = be32toh(path_info_header.length); - - if (payload->size < header_len + path_info_header.length) { - ERR("Unexpected payload size in \"relay_mkdir\" including path: expected >= %zu bytes, got %zu bytes", - header_len + path_info_header.length, payload->size); - ret = -1; - goto end_no_session; - } - - /* Ensure that it fits in local path length. */ - if (path_info_header.length >= LTTNG_PATH_MAX) { - ret = -ENAMETOOLONG; - ERR("Path name argument of mkdir command (%" PRIu32 " bytes) exceeds the maximal length allowed (%d bytes)", - path_info_header.length, LTTNG_PATH_MAX); - goto end; - } - - path_view = lttng_buffer_view_from_view(payload, header_len, - path_info_header.length); - - path = create_output_path(path_view.data); - if (!path) { - ERR("Failed to create output path"); - ret = -1; - goto end; - } - - ret = utils_mkdir_recursive(path, S_IRWXU | S_IRWXG, -1, -1); - if (ret < 0) { - ERR("relay creating output directory"); - goto end; - } - - ret = 0; - -end: - memset(&reply, 0, sizeof(reply)); - if (ret < 0) { - reply.ret_code = htobe32(LTTNG_ERR_UNK); - } else { - reply.ret_code = htobe32(LTTNG_OK); - } - send_ret = conn->sock->ops->sendmsg(conn->sock, &reply, sizeof(reply), 0); - if (send_ret < (ssize_t) sizeof(reply)) { - ERR("Failed to send \"mkdir\" command reply (ret = %zd)", send_ret); - ret = -1; - } - -end_no_session: - free(path); - return ret; -} - -static int validate_rotate_rename_path_length(const char *path_type, - uint32_t path_length) -{ - int ret = 0; - - if (path_length > LTTNG_PATH_MAX) { - ret = -ENAMETOOLONG; - ERR("rotate rename \"%s\" path name length (%" PRIu32 " bytes) exceeds the allowed size of %i bytes", - path_type, path_length, LTTNG_PATH_MAX); - } else if (path_length == 0) { - ret = -EINVAL; - ERR("rotate rename \"%s\" path name has an illegal length of 0", path_type); - } - return ret; -} - -/* - * relay_rotate_rename: rename the trace folder after a rotation is - * completed. We are not closing any fd here, just moving the folder, so it - * works even if data is still in-flight. - */ -static int relay_rotate_rename(const struct lttcomm_relayd_hdr *recv_hdr, - struct relay_connection *conn, - const struct lttng_buffer_view *payload) -{ - int ret; - ssize_t send_ret; - struct relay_session *session = conn->session; - struct lttcomm_relayd_generic_reply reply; - struct lttcomm_relayd_rotate_rename header; - size_t header_len; - size_t received_paths_size; - char *complete_old_path = NULL, *complete_new_path = NULL; - struct lttng_buffer_view old_path_view; - struct lttng_buffer_view new_path_view; - - if (!session || !conn->version_check_done) { - ERR("Trying to rename a trace folder before version check"); - ret = -1; - goto end_no_reply; - } - - if (session->major == 2 && session->minor < 11) { - ERR("relay_rotate_rename command is unsupported before LTTng 2.11"); - ret = -1; - goto end_no_reply; - } - - header_len = sizeof(header); - if (payload->size < header_len) { - ERR("Unexpected payload size in \"relay_rotate_rename\": expected >= %zu bytes, got %zu bytes", - header_len, payload->size); - ret = -1; - goto end_no_reply; - } - - memcpy(&header, payload->data, header_len); - - header.old_path_length = be32toh(header.old_path_length); - header.new_path_length = be32toh(header.new_path_length); - received_paths_size = header.old_path_length + header.new_path_length; - - if (payload->size < header_len + received_paths_size) { - ERR("Unexpected payload size in \"relay_rotate_rename\" including paths: expected >= %zu bytes, got %zu bytes", - header_len, payload->size); - ret = -1; - goto end_no_reply; - } - - /* Ensure the paths don't exceed their allowed size. */ - ret = validate_rotate_rename_path_length("old", header.old_path_length); - if (ret) { - goto end; - } - ret = validate_rotate_rename_path_length("new", header.new_path_length); - if (ret) { - goto end; - } - - old_path_view = lttng_buffer_view_from_view(payload, header_len, - header.old_path_length); - new_path_view = lttng_buffer_view_from_view(payload, - header_len + header.old_path_length, - header.new_path_length); - - /* Validate that both paths received are NULL terminated. */ - if (old_path_view.data[old_path_view.size - 1] != '\0') { - ERR("relay_rotate_rename command's \"old\" path is invalid (not NULL terminated)"); - ret = -1; - goto end; - } - if (new_path_view.data[new_path_view.size - 1] != '\0') { - ERR("relay_rotate_rename command's \"new\" path is invalid (not NULL terminated)"); - ret = -1; - goto end; - } - - complete_old_path = create_output_path(old_path_view.data); - if (!complete_old_path) { - ERR("Failed to build old output path in rotate_rename command"); - ret = -1; - goto end; - } - - complete_new_path = create_output_path(new_path_view.data); - if (!complete_new_path) { - ERR("Failed to build new output path in rotate_rename command"); - ret = -1; - goto end; - } - - ret = utils_mkdir_recursive(complete_new_path, S_IRWXU | S_IRWXG, - -1, -1); - if (ret < 0) { - ERR("Failed to mkdir() rotate_rename's \"new\" output directory at \"%s\"", - complete_new_path); - goto end; - } - - /* - * If a domain has not yet created its channel, the domain-specific - * folder might not exist, but this is not an error. - */ - ret = rename(complete_old_path, complete_new_path); - if (ret < 0 && errno != ENOENT) { - PERROR("Renaming chunk in rotate_rename command from \"%s\" to \"%s\"", - complete_old_path, complete_new_path); - goto end; - } - ret = 0; - -end: - memset(&reply, 0, sizeof(reply)); - if (ret < 0) { - reply.ret_code = htobe32(LTTNG_ERR_UNK); - } else { - reply.ret_code = htobe32(LTTNG_OK); - } - send_ret = conn->sock->ops->sendmsg(conn->sock, &reply, - sizeof(reply), 0); - if (send_ret < sizeof(reply)) { - ERR("Failed to send \"rotate rename\" command reply (ret = %zd)", - send_ret); - ret = -1; - } - -end_no_reply: - free(complete_old_path); - free(complete_new_path); - return ret; -} - -/* - * Check if all the streams in the session have completed the last rotation. - * The chunk_id value is used to distinguish the cases where a stream was - * closed on the consumerd before the rotation started but it still active on - * the relayd, and the case where a stream appeared on the consumerd/relayd - * after the last rotation started (in that case, it is already writing in the - * new chunk folder). - */ -static -int relay_rotate_pending(const struct lttcomm_relayd_hdr *recv_hdr, - struct relay_connection *conn, - const struct lttng_buffer_view *payload) -{ - struct relay_session *session = conn->session; - struct lttcomm_relayd_rotate_pending msg; - struct lttcomm_relayd_rotate_pending_reply reply; - struct lttng_ht_iter iter; - struct relay_stream *stream; - int ret = 0; - ssize_t send_ret; - uint64_t chunk_id; - bool rotate_pending = false; - - DBG("Rotate pending command received"); - - if (!session || !conn->version_check_done) { - ERR("Trying to check for data 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; - } - - if (payload->size < sizeof(msg)) { - ERR("Unexpected payload size in \"relay_rotate_pending\": expected >= %zu bytes, got %zu bytes", - sizeof(msg), payload->size); - ret = -1; - goto end_no_reply; - } - - memcpy(&msg, payload->data, sizeof(msg)); - - chunk_id = be64toh(msg.chunk_id); - - DBG("Evaluating rotate pending for chunk id %" PRIu64, chunk_id); - - /* - * Iterate over all the streams in the session and check if they are - * still waiting for data to perform their rotation. - */ - rcu_read_lock(); - cds_lfht_for_each_entry(relay_streams_ht->ht, &iter.iter, stream, - node.node) { - if (!stream_get(stream)) { - continue; - } - if (stream->trace->session != session) { - stream_put(stream); - continue; - } - pthread_mutex_lock(&stream->lock); - if (stream->rotate_at_seq_num != -1ULL) { - /* We have not yet performed the rotation. */ - rotate_pending = true; - DBG("Stream %" PRIu64 " is still rotating", - stream->stream_handle); - } else if (stream->chunk_id < chunk_id) { - /* - * Stream closed on the consumer but still active on the - * relay. - */ - rotate_pending = true; - DBG("Stream %" PRIu64 " did not exist on the consumer " - "when the last rotation started, but is" - "still waiting for data before getting" - "closed", - stream->stream_handle); - } - pthread_mutex_unlock(&stream->lock); - stream_put(stream); - if (rotate_pending) { - goto send_reply; - } - } - -send_reply: - rcu_read_unlock(); - memset(&reply, 0, sizeof(reply)); - reply.generic.ret_code = htobe32((uint32_t) LTTNG_OK); - reply.is_pending = (uint8_t) !!rotate_pending; - send_ret = conn->sock->ops->sendmsg(conn->sock, &reply, - sizeof(reply), 0); - if (send_ret < (ssize_t) sizeof(reply)) { - ERR("Failed to send \"rotate pending\" command reply (ret = %zd)", - send_ret); - ret = -1; - } - -end_no_reply: - return ret; -} - #define DBG_CMD(cmd_name, conn) \ DBG3("Processing \"%s\" command for socket %i", cmd_name, conn->sock->fd); @@ -2933,18 +2728,6 @@ static int relay_process_control_command(struct relay_connection *conn, DBG_CMD("RELAYD_ROTATE_STREAM", conn); ret = relay_rotate_session_stream(header, conn, payload); break; - case RELAYD_ROTATE_RENAME: - DBG_CMD("RELAYD_ROTATE_RENAME", conn); - ret = relay_rotate_rename(header, conn, payload); - break; - case RELAYD_ROTATE_PENDING: - DBG_CMD("RELAYD_ROTATE_PENDING", conn); - ret = relay_rotate_pending(header, conn, payload); - break; - case RELAYD_MKDIR: - DBG_CMD("RELAYD_MKDIR", conn); - ret = relay_mkdir(header, conn, payload); - break; case RELAYD_UPDATE_SYNC_INFO: default: ERR("Received unknown command (%u)", header->cmd); @@ -3177,7 +2960,34 @@ static int handle_index_data(struct relay_stream *stream, uint64_t net_seq_num, } if (rotate_index || !stream->index_file) { - ret = create_rotate_index_file(stream); + const char *stream_path; + + /* + * The data connection creates the stream's first index file. + * + * This can happen _after_ a ROTATE_STREAM command. In + * other words, the data of the first packet of this stream + * can be received after a ROTATE_STREAM command. + * + * The ROTATE_STREAM command changes the stream's path_name + * to point to the "next" chunk. If a rotation is pending for + * this stream, as indicated by "rotate_at_seq_num != -1ULL", + * it means that we are still receiving data that belongs in the + * stream's former path. + * + * In this very specific case, we must ensure that the index + * file is created in the streams's former path, + * "prev_path_name". + * + * All other rotations beyond the first one are not affected + * by this problem since the actual rotation operation creates + * the new chunk's index file. + */ + stream_path = stream->rotate_at_seq_num == -1ULL ? + stream->path_name: + stream->prev_path_name; + + ret = create_rotate_index_file(stream, stream_path); if (ret < 0) { ERR("Failed to rotate index"); /* Put self-ref for this index due to error. */ @@ -3205,9 +3015,13 @@ static int handle_index_data(struct relay_stream *stream, uint64_t net_seq_num, /* No flush. */ ret = 0; } else { - /* Put self-ref for this index due to error. */ - relay_index_put(index); - index = NULL; + /* + * ret < 0 + * + * relay_index_try_flush is responsible for the self-reference + * put of the index object on error. + */ + ERR("relay_index_try_flush error %d", ret); ret = -1; } end: @@ -3256,7 +3070,6 @@ static enum relay_connection_status relay_process_data_receive_header( DBG3("Partial reception of data connection header (received %" PRIu64 " bytes, %" PRIu64 " bytes left to receive, fd = %i)", state->received, state->left_to_receive, conn->sock->fd); - ret = 0; goto end; } @@ -3323,7 +3136,6 @@ static enum relay_connection_status relay_process_data_receive_header( conn->protocol.data.state.receive_payload.rotate_index = true; } - ret = 0; end_stream_unlock: pthread_mutex_unlock(&stream->lock); stream_put(stream); @@ -3346,10 +3158,14 @@ static enum relay_connection_status relay_process_data_receive_payload( uint64_t left_to_receive = state->left_to_receive; struct relay_session *session; + DBG3("Receiving data for stream id %" PRIu64 " seqnum %" PRIu64 ", %" PRIu64" bytes received, %" PRIu64 " bytes left to receive", + state->header.stream_id, state->header.net_seq_num, + state->received, left_to_receive); + stream = stream_get_by_id(state->header.stream_id); if (!stream) { /* Protocol error. */ - DBG("relay_process_data_receive_payload: Cannot find stream %" PRIu64, + ERR("relay_process_data_receive_payload: cannot find stream %" PRIu64, state->header.stream_id); status = RELAY_CONNECTION_STATUS_ERROR; goto end; @@ -3357,10 +3173,13 @@ static enum relay_connection_status relay_process_data_receive_payload( pthread_mutex_lock(&stream->lock); session = stream->trace->session; - - DBG3("Receiving data for stream id %" PRIu64 " seqnum %" PRIu64 ", %" PRIu64" bytes received, %" PRIu64 " bytes left to receive", - state->header.stream_id, state->header.net_seq_num, - state->received, left_to_receive); + if (!conn->session) { + ret = connection_set_session(conn, session); + if (ret) { + status = RELAY_CONNECTION_STATUS_ERROR; + goto end_stream_unlock; + } + } /* * The size of the "chunk" received on any iteration is bounded by: @@ -3435,7 +3254,7 @@ static enum relay_connection_status relay_process_data_receive_payload( } - if (session->minor >= 4 && !session->snapshot) { + if (session_streams_have_index(session)) { ret = handle_index_data(stream, state->header.net_seq_num, state->rotate_index, &index_flushed, state->header.data_size + state->header.padding_size); if (ret < 0) { @@ -3450,15 +3269,20 @@ static enum relay_connection_status relay_process_data_receive_payload( stream->tracefile_size_current += state->header.data_size + state->header.padding_size; - if (stream->prev_seq == -1ULL) { + if (stream->prev_data_seq == -1ULL) { new_stream = true; } if (index_flushed) { stream->pos_after_last_complete_data_index = stream->tracefile_size_current; + stream->prev_index_seq = state->header.net_seq_num; + ret = try_rotate_stream_index(stream); + if (ret < 0) { + goto end_stream_unlock; + } } - stream->prev_seq = state->header.net_seq_num; + stream->prev_data_seq = state->header.net_seq_num; /* * Resetting the protocol state (to RECEIVE_HEADER) will trash the @@ -3468,7 +3292,7 @@ static enum relay_connection_status relay_process_data_receive_payload( connection_reset_protocol_state(conn); state = NULL; - ret = try_rotate_stream(stream); + ret = try_rotate_stream_data(stream); if (ret < 0) { status = RELAY_CONNECTION_STATUS_ERROR; goto end_stream_unlock; @@ -3628,14 +3452,6 @@ restart: health_code_update(); - if (!revents) { - /* - * No activity for this FD (poll - * implementation). - */ - continue; - } - /* Thread quit pipe has been closed. Killing thread. */ ret = check_thread_quit_pipe(pollfd, revents); if (ret) { @@ -3688,6 +3504,26 @@ restart: status = relay_process_control(ctrl_conn); if (status != RELAY_CONNECTION_STATUS_OK) { + /* + * On socket error flag the session as aborted to force + * the cleanup of its stream otherwise it can leak + * during the lifetime of the relayd. + * + * This prevents situations in which streams can be + * left opened because an index was received, the + * control connection is closed, and the data + * connection is closed (uncleanly) before the packet's + * data provided. + * + * Since the control connection encountered an error, + * it is okay to be conservative and close the + * session right now as we can't rely on the protocol + * being respected anymore. + */ + if (status == RELAY_CONNECTION_STATUS_ERROR) { + session_abort(ctrl_conn->session); + } + /* Clear the connection on error or close. */ relay_thread_close_connection(&events, pollfd, @@ -3767,6 +3603,25 @@ restart: status = relay_process_data(data_conn); /* Connection closed or error. */ if (status != RELAY_CONNECTION_STATUS_OK) { + /* + * On socket error flag the session as aborted to force + * the cleanup of its stream otherwise it can leak + * during the lifetime of the relayd. + * + * This prevents situations in which streams can be + * left opened because an index was received, the + * control connection is closed, and the data + * connection is closed (uncleanly) before the packet's + * data provided. + * + * Since the data connection encountered an error, + * it is okay to be conservative and close the + * session right now as we can't rely on the protocol + * being respected anymore. + */ + if (status == RELAY_CONNECTION_STATUS_ERROR) { + session_abort(data_conn->session); + } relay_thread_close_connection(&events, pollfd, data_conn); /* @@ -3798,16 +3653,14 @@ restart: exit: error: - /* Cleanup reamaining connection object. */ + /* Cleanup remaining connection object. */ rcu_read_lock(); cds_lfht_for_each_entry(relay_connections_ht->ht, &iter.iter, destroy_conn, sock_n.node) { health_code_update(); - if (session_abort(destroy_conn->session)) { - assert(0); - } + session_abort(destroy_conn->session); /* * No need to grab another ref, because we own @@ -3910,6 +3763,13 @@ int main(int argc, char **argv) } } + sessiond_trace_chunk_registry = sessiond_trace_chunk_registry_create(); + if (!sessiond_trace_chunk_registry) { + ERR("Failed to initialize session daemon trace chunk registry"); + retval = -1; + goto exit_sessiond_trace_chunk_registry; + } + /* Initialize thread health monitoring */ health_relayd = health_app_create(NR_HEALTH_RELAYD_TYPES); if (!health_relayd) { @@ -4059,7 +3919,9 @@ exit_health_quit_pipe: exit_init_data: health_app_destroy(health_relayd); + sessiond_trace_chunk_registry_destroy(sessiond_trace_chunk_registry); exit_health_app_create: +exit_sessiond_trace_chunk_registry: exit_options: /* * Wait for all pending call_rcu work to complete before tearing