From: Mathieu Desnoyers Date: Thu, 12 Dec 2019 17:22:14 +0000 (-0500) Subject: trace-chunk: Introduce chunk "path", relayd session "ongoing_rotation", sessiond... X-Git-Tag: v2.12.0-rc1~127 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=a7ceb342d473cc37e00d74c45b04b5378965e055;p=lttng-tools.git trace-chunk: Introduce chunk "path", relayd session "ongoing_rotation", sessiond session "rotated" This commit introduces new attributes in 3 different structures because those are used in surrounding areas of the code. Introduce a trace chunk path attribute which tracks where the chunk currently keeps its files. It allows updating the current chunk location with the "rename" API without having to rely on changing the chunk name override, which is an attribute we may want to keep using when we archive the chunk. Separating the "path" from the "name override" attribute allows easy manipulation of the chunk output without having to keep the name override around in the caller code. Introduce an "ongoing_rotation" relayd session attribute to allow live viewers to retry while a rotation is ongoing. This ongoing rotation attribute is introduced in the same commit as the path chunk attribute because they are used in a surrounding area of the code. Introduce a "rotated" sessiond session attribute in this commit because it is used in a surrounding area of the code. This rotated attribute tracks whether an explicit rotation has been performed on the session. It is a preparation step for the clear feature, which will use this to figure out whether it needs to clear the "root" output path (no rotation prior to clear") or if it needs to clear to a new path. Before introduction of the clear feature, a chunk_id of 0 was used to identify this, but because clear will increment the chunk id without changing the path, we need to track this explicitly through the "rotated" session flag. Signed-off-by: Mathieu Desnoyers Change-Id: Ifdecc66cb4849f3e5f7476ab7db48d8f7532a6d3 Signed-off-by: Jérémie Galarneau --- diff --git a/src/bin/lttng-relayd/main.c b/src/bin/lttng-relayd/main.c index d874a9c50..749feb6f6 100644 --- a/src/bin/lttng-relayd/main.c +++ b/src/bin/lttng-relayd/main.c @@ -2459,6 +2459,7 @@ static int relay_create_trace_chunk(const struct lttcomm_relayd_hdr *recv_hdr, enum lttng_error_code reply_code = LTTNG_OK; enum lttng_trace_chunk_status chunk_status; struct lttng_directory_handle *session_output = NULL; + const char *new_path; if (!session || !conn->version_check_done) { ERR("Trying to create a trace chunk before version check"); @@ -2485,8 +2486,29 @@ static int relay_create_trace_chunk(const struct lttcomm_relayd_hdr *recv_hdr, msg->creation_timestamp = be64toh(msg->creation_timestamp); msg->override_name_length = be32toh(msg->override_name_length); + if (session->current_trace_chunk && + !lttng_trace_chunk_get_name_overridden(session->current_trace_chunk)) { + chunk_status = lttng_trace_chunk_rename_path(session->current_trace_chunk, + DEFAULT_CHUNK_TMP_OLD_DIRECTORY); + if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ERR("Failed to rename old chunk"); + ret = -1; + reply_code = LTTNG_ERR_UNK; + goto end; + } + } + session->ongoing_rotation = true; + if (!session->current_trace_chunk) { + if (!session->has_rotated) { + new_path = ""; + } else { + new_path = NULL; + } + } else { + new_path = DEFAULT_CHUNK_TMP_NEW_DIRECTORY; + } chunk = lttng_trace_chunk_create( - msg->chunk_id, msg->creation_timestamp); + msg->chunk_id, msg->creation_timestamp, new_path); if (!chunk) { ERR("Failed to create trace chunk in trace chunk creation command"); ret = -1; @@ -2581,6 +2603,9 @@ static int relay_create_trace_chunk(const struct lttcomm_relayd_hdr *recv_hdr, conn->session->current_trace_chunk; conn->session->current_trace_chunk = published_chunk; published_chunk = NULL; + if (!conn->session->pending_closure_trace_chunk) { + session->ongoing_rotation = false; + } end_unlock_session: pthread_mutex_unlock(&conn->session->lock); end: @@ -2624,6 +2649,7 @@ static int relay_close_trace_chunk(const struct lttcomm_relayd_hdr *recv_hdr, size_t path_length = 0; const char *chunk_name = NULL; struct lttng_dynamic_buffer reply_payload; + const char *new_path; lttng_dynamic_buffer_init(&reply_payload); @@ -2683,6 +2709,43 @@ static int relay_close_trace_chunk(const struct lttcomm_relayd_hdr *recv_hdr, goto end_unlock_session; } + if (session->current_trace_chunk && session->current_trace_chunk != chunk && + !lttng_trace_chunk_get_name_overridden(session->current_trace_chunk)) { + if (close_command.is_set && + close_command.value == LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE && + !session->has_rotated) { + /* New chunk stays in session output directory. */ + new_path = ""; + } else { + /* Use chunk name for new chunk. */ + new_path = NULL; + } + /* Rename new chunk path. */ + chunk_status = lttng_trace_chunk_rename_path(session->current_trace_chunk, + new_path); + if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ret = -1; + goto end; + } + session->ongoing_rotation = false; + } + if ((!close_command.is_set || + close_command.value == LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) && + !lttng_trace_chunk_get_name_overridden(chunk)) { + const char *old_path; + + if (!session->has_rotated) { + old_path = ""; + } else { + old_path = NULL; + } + /* We need to move back the .tmp_old_chunk to its rightful place. */ + chunk_status = lttng_trace_chunk_rename_path(chunk, old_path); + if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ret = -1; + goto end; + } + } chunk_status = lttng_trace_chunk_set_close_timestamp( chunk, close_timestamp); if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { diff --git a/src/bin/lttng-relayd/session.h b/src/bin/lttng-relayd/session.h index aa2a58a98..43f76a4aa 100644 --- a/src/bin/lttng-relayd/session.h +++ b/src/bin/lttng-relayd/session.h @@ -134,6 +134,11 @@ struct relay_session { struct cds_list_head viewer_session_node; struct lttng_trace_chunk *current_trace_chunk; struct lttng_trace_chunk *pending_closure_trace_chunk; + /* + * Prevent live viewers from taking of copy of the chunk + * while new chunk has a temporary directory name. + */ + bool ongoing_rotation; struct rcu_head rcu_node; /* For call_rcu teardown. */ }; diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index 6f824bf31..db07082db 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -3328,9 +3328,7 @@ int cmd_destroy_session(struct ltt_session *session, session->rotate_size = 0; } - if (session->most_recent_chunk_id.is_set && - session->most_recent_chunk_id.value != 0 && - session->current_trace_chunk && session->output_traces) { + if (session->rotated && session->current_trace_chunk && session->output_traces) { /* * Perform a last rotation on destruction if rotations have * occurred during the session's lifetime. diff --git a/src/bin/lttng-sessiond/session.c b/src/bin/lttng-sessiond/session.c index 0da2058fd..88bbe1309 100644 --- a/src/bin/lttng-sessiond/session.c +++ b/src/bin/lttng-sessiond/session.c @@ -589,6 +589,7 @@ struct lttng_trace_chunk *session_create_new_trace_chunk( }; uint64_t next_chunk_id; const struct consumer_output *output; + const char *new_path; if (consumer_output_override) { output = consumer_output_override; @@ -612,8 +613,26 @@ struct lttng_trace_chunk *session_create_new_trace_chunk( next_chunk_id = session->most_recent_chunk_id.is_set ? session->most_recent_chunk_id.value + 1 : 0; + if (session->current_trace_chunk && + !lttng_trace_chunk_get_name_overridden(session->current_trace_chunk)) { + chunk_status = lttng_trace_chunk_rename_path(session->current_trace_chunk, + DEFAULT_CHUNK_TMP_OLD_DIRECTORY); + if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { + goto error; + } + } + if (!session->current_trace_chunk) { + if (!session->rotated) { + new_path = ""; + } else { + new_path = NULL; + } + } else { + new_path = DEFAULT_CHUNK_TMP_NEW_DIRECTORY; + } + trace_chunk = lttng_trace_chunk_create(next_chunk_id, - chunk_creation_ts); + chunk_creation_ts, new_path); if (!trace_chunk) { goto error; } @@ -678,6 +697,7 @@ int session_close_trace_chunk(struct ltt_session *session, struct consumer_socket *socket; enum lttng_trace_chunk_status chunk_status; const time_t chunk_close_timestamp = time(NULL); + const char *new_path; chunk_status = lttng_trace_chunk_set_close_command( trace_chunk, close_command); @@ -692,6 +712,44 @@ int session_close_trace_chunk(struct ltt_session *session, ret = -1; goto end; } + + if (close_command == LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE && !session->rotated) { + /* New chunk stays in session output directory. */ + new_path = ""; + } else { + /* Use chunk name for new chunk. */ + new_path = NULL; + } + if (session->current_trace_chunk && + !lttng_trace_chunk_get_name_overridden(session->current_trace_chunk)) { + /* Rename new chunk path. */ + chunk_status = lttng_trace_chunk_rename_path(session->current_trace_chunk, + new_path); + if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ret = -1; + goto end; + } + } + if (!lttng_trace_chunk_get_name_overridden(trace_chunk) && + close_command == LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) { + const char *old_path; + + if (!session->rotated) { + old_path = ""; + } else { + old_path = NULL; + } + /* We need to move back the .tmp_old_chunk to its rightful place. */ + chunk_status = lttng_trace_chunk_rename_path(trace_chunk, + old_path); + if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ret = -1; + goto end; + } + } + if (close_command == LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED) { + session->rotated = true; + } chunk_status = lttng_trace_chunk_set_close_timestamp(trace_chunk, chunk_close_timestamp); if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { diff --git a/src/bin/lttng-sessiond/session.h b/src/bin/lttng-sessiond/session.h index da3805658..606c42269 100644 --- a/src/bin/lttng-sessiond/session.h +++ b/src/bin/lttng-sessiond/session.h @@ -181,6 +181,10 @@ struct ltt_session { * subsequent rotate (without prior start) will return an error. */ bool cleared_after_last_stop; + /* + * True if the session has had an explicit non-quiet rotation. + */ + bool rotated; /* * Condition and trigger for size-based rotations. */ diff --git a/src/common/consumer/consumer.c b/src/common/consumer/consumer.c index 26209279e..0263489dd 100644 --- a/src/common/consumer/consumer.c +++ b/src/common/consumer/consumer.c @@ -4603,7 +4603,7 @@ enum lttcomm_return_code lttng_consumer_create_trace_chunk( * and LTTNG_CONSUMER_DESTROY_TRACE_CHUNK commands. */ created_chunk = lttng_trace_chunk_create(chunk_id, - chunk_creation_timestamp); + chunk_creation_timestamp, NULL); if (!created_chunk) { ERR("Failed to create trace chunk"); ret_code = LTTCOMM_CONSUMERD_CREATE_TRACE_CHUNK_FAILED; diff --git a/src/common/defaults.h b/src/common/defaults.h index 155739f46..2c3e80f43 100644 --- a/src/common/defaults.h +++ b/src/common/defaults.h @@ -353,7 +353,8 @@ * Name of the intermediate directory used to rename the trace chunk of a * session's first rotation. */ -#define DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY ".tmp_rename_chunk" +#define DEFAULT_CHUNK_TMP_OLD_DIRECTORY ".tmp_old_chunk" +#define DEFAULT_CHUNK_TMP_NEW_DIRECTORY ".tmp_new_chunk" #define DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY "archives" /* diff --git a/src/common/trace-chunk.c b/src/common/trace-chunk.c index c549f46b3..266a02ee6 100644 --- a/src/common/trace-chunk.c +++ b/src/common/trace-chunk.c @@ -55,18 +55,26 @@ enum trace_chunk_mode { * since only one thread may access a chunk during its destruction (the last * to release its reference to the chunk). */ -typedef void (*chunk_close_command)(struct lttng_trace_chunk *trace_chunk); +typedef int (*chunk_command)(struct lttng_trace_chunk *trace_chunk); /* Move a completed trace chunk to the 'completed' trace archive folder. */ static -void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk); +int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk *trace_chunk); +static +enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock( + struct lttng_trace_chunk *chunk, const char *path); struct chunk_credentials { bool use_current_user; struct lttng_credentials user; }; -/* NOTE: Make sure to update lttng_trace_chunk_copy if you modify this. */ +/* + * NOTE: Make sure to update: + * - lttng_trace_chunk_copy(), + * - lttng_trace_chunk_registry_element_create_from_chunk() + * if you modify this structure. + */ struct lttng_trace_chunk { pthread_mutex_t lock; struct urcu_ref ref; @@ -87,6 +95,7 @@ struct lttng_trace_chunk { bool in_registry_element; bool name_overridden; char *name; + char *path; /* An unset id means the chunk is anonymous. */ LTTNG_OPTIONAL(uint64_t) id; LTTNG_OPTIONAL(time_t) timestamp_creation; @@ -123,9 +132,9 @@ char *close_command_names[] = { }; static const -chunk_close_command close_command_funcs[] = { +chunk_command close_command_post_release_funcs[] = { [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] = - lttng_trace_chunk_move_to_completed, + lttng_trace_chunk_move_to_completed_post_release, }; static @@ -242,6 +251,8 @@ void lttng_trace_chunk_fini(struct lttng_trace_chunk *chunk) } free(chunk->name); chunk->name = NULL; + free(chunk->path); + chunk->path = NULL; lttng_dynamic_pointer_array_reset(&chunk->top_level_directories); lttng_dynamic_pointer_array_reset(&chunk->files); pthread_mutex_destroy(&chunk->lock); @@ -271,7 +282,7 @@ struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous(void) LTTNG_HIDDEN struct lttng_trace_chunk *lttng_trace_chunk_create( - uint64_t chunk_id, time_t chunk_creation_time) + uint64_t chunk_id, time_t chunk_creation_time, const char *path) { struct lttng_trace_chunk *chunk; char chunk_creation_datetime_buf[16] = {}; @@ -309,6 +320,19 @@ struct lttng_trace_chunk *lttng_trace_chunk_create( goto error; } } + if (path) { + chunk->path = strdup(path); + if (!chunk->path) { + goto error; + } + } else { + if (chunk->name) { + chunk->path = strdup(chunk->name); + if (!chunk->path) { + goto error; + } + } + } DBG("Chunk name set to \"%s\"", chunk->name ? : "(none)"); end: @@ -352,6 +376,13 @@ struct lttng_trace_chunk *lttng_trace_chunk_copy( goto error_unlock; } } + if (source_chunk->path) { + new_chunk->path = strdup(source_chunk->path); + if (!new_chunk->path) { + ERR("Failed to copy source trace chunk path in %s()", + __FUNCTION__); + } + } new_chunk->id = source_chunk->id; new_chunk->timestamp_creation = source_chunk->timestamp_creation; new_chunk->timestamp_close = source_chunk->timestamp_close; @@ -520,9 +551,10 @@ enum lttng_trace_chunk_status lttng_trace_chunk_override_name( struct lttng_trace_chunk *chunk, const char *name) { - char *new_name; enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + char *new_name, *new_path; + DBG("Override trace chunk name from %s to %s", chunk->name, name); if (!is_valid_chunk_name(name)) { ERR("Attempted to set an invalid name on a trace chunk: name = %s", name ? : "NULL"); @@ -537,6 +569,7 @@ enum lttng_trace_chunk_status lttng_trace_chunk_override_name( status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; goto end_unlock; } + new_name = strdup(name); if (!new_name) { ERR("Failed to allocate new trace chunk name"); @@ -545,13 +578,239 @@ enum lttng_trace_chunk_status lttng_trace_chunk_override_name( } free(chunk->name); chunk->name = new_name; + + new_path = strdup(name); + if (!new_path) { + ERR("Failed to allocate new trace chunk path"); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end_unlock; + } + free(chunk->path); + chunk->path = new_path; + chunk->name_overridden = true; -end_unlock: +end_unlock: pthread_mutex_unlock(&chunk->lock); end: return status; } +static +enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock( + struct lttng_trace_chunk *chunk, const char *path) + +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + struct lttng_directory_handle *rename_directory = NULL; + char *new_path, *old_path; + int ret; + + if (chunk->name_overridden) { + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + + old_path = chunk->path; + DBG("lttng_trace_chunk_rename_path from %s to %s", old_path, path); + + if ((!old_path && !path) || + (old_path && path && !strcmp(old_path, path))) { + goto end; + } + /* + * Use chunk name as path if NULL path is specified. + */ + if (!path) { + path = chunk->name; + } + + /* Renaming from "" to "" is not accepted. */ + if (path[0] == '\0' && old_path[0] == '\0') { + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + + /* + * If a rename is performed on a chunk for which the chunk_directory + * is not set (yet), or the session_output_directory is not set + * (interacting with a relay daemon), there is no rename to perform. + */ + if (!chunk->chunk_directory || + !chunk->session_output_directory) { + goto skip_move; + } + + if (old_path[0] != '\0' && path[0] != '\0') { + /* Rename chunk directory. */ + ret = lttng_directory_handle_rename_as_user( + chunk->session_output_directory, + old_path, + chunk->session_output_directory, + path, + LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ? + NULL : + &chunk->credentials.value.user); + if (ret) { + PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"", + old_path, path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + rename_directory = lttng_directory_handle_create_from_handle( + path, + chunk->session_output_directory); + if (!rename_directory) { + ERR("Failed to get handle to trace chunk rename directory"); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + + /* Release old handle. */ + lttng_directory_handle_put(chunk->chunk_directory); + /* + * Transfer new handle reference to chunk as the current chunk + * handle. + */ + chunk->chunk_directory = rename_directory; + rename_directory = NULL; + } else if (old_path[0] == '\0') { + size_t i, count = lttng_dynamic_pointer_array_get_count( + &chunk->top_level_directories); + + ret = lttng_directory_handle_create_subdirectory_as_user( + chunk->session_output_directory, + path, + DIR_CREATION_MODE, + LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ? + NULL : + &chunk->credentials.value.user); + if (ret) { + PERROR("Failed to create trace chunk rename directory \"%s\"", + path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + + rename_directory = lttng_directory_handle_create_from_handle( + path, chunk->session_output_directory); + if (!rename_directory) { + ERR("Failed to get handle to trace chunk rename directory"); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + + /* Move toplevel directories. */ + for (i = 0; i < count; i++) { + const char *top_level_name = + lttng_dynamic_pointer_array_get_pointer( + &chunk->top_level_directories, i); + + ret = lttng_directory_handle_rename_as_user( + chunk->chunk_directory, + top_level_name, + rename_directory, + top_level_name, + LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ? + NULL : + &chunk->credentials.value.user); + if (ret) { + PERROR("Failed to move \"%s\" to trace chunk rename directory", + top_level_name); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + } + /* Release old handle. */ + lttng_directory_handle_put(chunk->chunk_directory); + /* + * Transfer new handle reference to chunk as the current chunk + * handle. + */ + chunk->chunk_directory = rename_directory; + rename_directory = NULL; + } else { + size_t i, count = lttng_dynamic_pointer_array_get_count( + &chunk->top_level_directories); + const bool reference_acquired = lttng_directory_handle_get( + chunk->session_output_directory); + + assert(reference_acquired); + rename_directory = chunk->session_output_directory; + + /* Move toplevel directories. */ + for (i = 0; i < count; i++) { + const char *top_level_name = + lttng_dynamic_pointer_array_get_pointer( + &chunk->top_level_directories, i); + + ret = lttng_directory_handle_rename_as_user( + chunk->chunk_directory, + top_level_name, + rename_directory, + top_level_name, + LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ? + NULL : + &chunk->credentials.value.user); + if (ret) { + PERROR("Failed to move \"%s\" to trace chunk rename directory", + top_level_name); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + } + /* Release old handle. */ + lttng_directory_handle_put(chunk->chunk_directory); + /* + * Transfer new handle reference to chunk as the current chunk + * handle. + */ + chunk->chunk_directory = rename_directory; + rename_directory = NULL; + + /* Remove old directory. */ + status = lttng_directory_handle_remove_subdirectory( + chunk->session_output_directory, + old_path); + if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ERR("Error removing subdirectory '%s' file when deleting chunk", + old_path); + ret = -1; + goto end; + } + } + +skip_move: + if (path) { + new_path = strdup(path); + if (!new_path) { + ERR("Failed to allocate new trace chunk path"); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + } else { + new_path = NULL; + } + free(chunk->path); + chunk->path = new_path; +end: + lttng_directory_handle_put(rename_directory); + return status; +} + +LTTNG_HIDDEN +enum lttng_trace_chunk_status lttng_trace_chunk_rename_path( + struct lttng_trace_chunk *chunk, const char *path) + +{ + enum lttng_trace_chunk_status status; + + pthread_mutex_lock(&chunk->lock); + status = lttng_trace_chunk_rename_path_no_lock(chunk, path); + pthread_mutex_unlock(&chunk->lock); + + return status; +} + LTTNG_HIDDEN enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials( struct lttng_trace_chunk *chunk, @@ -641,32 +900,39 @@ enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner( status = LTTNG_TRACE_CHUNK_STATUS_ERROR; goto end; } - - if (chunk->name) { - /* - * A nameless chunk does not need its own output directory. - * The session's output directory will be used. - */ + if (chunk->path[0] != '\0') { ret = lttng_directory_handle_create_subdirectory_as_user( session_output_directory, - chunk->name, + chunk->path, DIR_CREATION_MODE, !chunk->credentials.value.use_current_user ? &chunk->credentials.value.user : NULL); if (ret) { PERROR("Failed to create chunk output directory \"%s\"", - chunk->name); + chunk->path); status = LTTNG_TRACE_CHUNK_STATUS_ERROR; goto end; } - } - chunk_directory_handle = lttng_directory_handle_create_from_handle( - chunk->name, - session_output_directory); - if (!chunk_directory_handle) { - /* The function already logs on all error paths. */ - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; + chunk_directory_handle = + lttng_directory_handle_create_from_handle( + chunk->path, + session_output_directory); + if (!chunk_directory_handle) { + /* The function already logs on all error paths. */ + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + } else { + /* + * A nameless chunk does not need its own output directory. + * The session's output directory will be used. + */ + const bool reference_acquired = + lttng_directory_handle_get( + session_output_directory); + + assert(reference_acquired); + chunk_directory_handle = session_output_directory; } chunk->chunk_directory = chunk_directory_handle; chunk_directory_handle = NULL; @@ -972,7 +1238,7 @@ int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk, if (!chunk->credentials.is_set) { /* * Fatal error, credentials must be set before a - * directory is created. + * file is unlinked. */ ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"", file_path); @@ -999,12 +1265,50 @@ end: return status; } -static -void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) +LTTNG_HIDDEN +int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk *chunk, + const char *path) { int ret; - char *directory_to_rename = NULL; - bool free_directory_to_rename = false; + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + DBG("Recursively removing trace chunk directory \"%s\"", path); + pthread_mutex_lock(&chunk->lock); + if (!chunk->credentials.is_set) { + /* + * Fatal error, credentials must be set before a + * directory is removed. + */ + ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"", + path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + if (!chunk->chunk_directory) { + ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory", + path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + ret = lttng_directory_handle_remove_subdirectory_recursive_as_user( + chunk->chunk_directory, path, + chunk->credentials.value.use_current_user ? + NULL : &chunk->credentials.value.user, + LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG); + if (ret < 0) { + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } +end: + pthread_mutex_unlock(&chunk->lock); + return status; +} + +static +int lttng_trace_chunk_move_to_completed_post_release( + struct lttng_trace_chunk *trace_chunk) +{ + int ret = 0; char *archived_chunk_name = NULL; const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id); const time_t creation_timestamp = @@ -1012,6 +1316,7 @@ void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) const time_t close_timestamp = LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close); struct lttng_directory_handle *archived_chunks_directory = NULL; + enum lttng_trace_chunk_status status; if (!trace_chunk->mode.is_set || trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER || @@ -1025,76 +1330,13 @@ void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) assert(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER); assert(!trace_chunk->name_overridden); - - /* - * The fist trace chunk of a session is directly output to the - * session's output folder. In this case, the top level directories - * must be moved to a temporary folder before that temporary directory - * is renamed to match the chunk's name. - */ - if (chunk_id == 0) { - struct lttng_directory_handle *temporary_rename_directory = - NULL; - size_t i, count = lttng_dynamic_pointer_array_get_count( - &trace_chunk->top_level_directories); - - ret = lttng_directory_handle_create_subdirectory_as_user( - trace_chunk->session_output_directory, - DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY, - DIR_CREATION_MODE, - !trace_chunk->credentials.value.use_current_user ? - &trace_chunk->credentials.value.user : NULL); - if (ret) { - PERROR("Failed to create temporary trace chunk rename directory \"%s\"", - DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY); - } - - temporary_rename_directory = lttng_directory_handle_create_from_handle( - DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY, - trace_chunk->session_output_directory); - if (!temporary_rename_directory) { - ERR("Failed to get handle to temporary trace chunk rename directory"); - goto end; - } - - for (i = 0; i < count; i++) { - const char *top_level_name = - lttng_dynamic_pointer_array_get_pointer( - &trace_chunk->top_level_directories, i); - - ret = lttng_directory_handle_rename_as_user( - trace_chunk->session_output_directory, - top_level_name, - temporary_rename_directory, - top_level_name, - LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ? - NULL : - &trace_chunk->credentials.value.user); - if (ret) { - PERROR("Failed to move \"%s\" to temporary trace chunk rename directory", - top_level_name); - lttng_directory_handle_put( - temporary_rename_directory); - goto end; - } - } - lttng_directory_handle_put(temporary_rename_directory); - directory_to_rename = DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY; - free_directory_to_rename = false; - } else { - directory_to_rename = generate_chunk_name(chunk_id, - creation_timestamp, NULL); - if (!directory_to_rename) { - ERR("Failed to generate initial trace chunk name while renaming trace chunk"); - goto end; - } - free_directory_to_rename = true; - } + assert(trace_chunk->path); archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp, &close_timestamp); if (!archived_chunk_name) { ERR("Failed to generate archived trace chunk name while renaming trace chunk"); + ret = -1; goto end; } @@ -1116,12 +1358,29 @@ void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) trace_chunk->session_output_directory); if (!archived_chunks_directory) { PERROR("Failed to get handle to archived trace chunks directory"); + ret = -1; goto end; } + /* + * Make sure chunk is renamed to old directory if not already done by + * the creation of the next chunk. This happens if a rotation is + * performed while tracing is stopped. + */ + if (!trace_chunk->path || strcmp(trace_chunk->path, + DEFAULT_CHUNK_TMP_OLD_DIRECTORY)) { + status = lttng_trace_chunk_rename_path_no_lock(trace_chunk, + DEFAULT_CHUNK_TMP_OLD_DIRECTORY); + if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY); + ret = -1; + goto end; + } + } + ret = lttng_directory_handle_rename_as_user( trace_chunk->session_output_directory, - directory_to_rename, + trace_chunk->path, archived_chunks_directory, archived_chunk_name, LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ? @@ -1129,15 +1388,14 @@ void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) &trace_chunk->credentials.value.user); if (ret) { PERROR("Failed to rename folder \"%s\" to \"%s\"", - directory_to_rename, archived_chunk_name); + trace_chunk->path, + archived_chunk_name); } end: lttng_directory_handle_put(archived_chunks_directory); free(archived_chunk_name); - if (free_directory_to_rename) { - free(directory_to_rename); - } + return ret; } LTTNG_HIDDEN @@ -1233,7 +1491,11 @@ void lttng_trace_chunk_release(struct urcu_ref *ref) ref); if (chunk->close_command.is_set) { - close_command_funcs[chunk->close_command.value](chunk); + if (close_command_post_release_funcs[ + chunk->close_command.value](chunk)) { + ERR("Trace chunk post-release command %s has failed.", + close_command_names[chunk->close_command.value]); + } } if (chunk->in_registry_element) { @@ -1333,10 +1595,11 @@ lttng_trace_chunk_registry_element_create_from_chunk( chunk->chunk_directory = NULL; } /* - * The original chunk becomes invalid; the name attribute is transferred - * to the new chunk instance. + * The original chunk becomes invalid; the name and path attributes are + * transferred to the new chunk instance. */ chunk->name = NULL; + chunk->path = NULL; element->chunk.in_registry_element = true; end: return element; diff --git a/src/common/trace-chunk.h b/src/common/trace-chunk.h index 7135b0193..c2fcd17e9 100644 --- a/src/common/trace-chunk.h +++ b/src/common/trace-chunk.h @@ -84,7 +84,8 @@ struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous(void); LTTNG_HIDDEN struct lttng_trace_chunk *lttng_trace_chunk_create( uint64_t chunk_id, - time_t chunk_creation_time); + time_t chunk_creation_time, + const char *path); /* * Copy a trace chunk. The copy that is returned is always a _user_ @@ -123,6 +124,10 @@ LTTNG_HIDDEN enum lttng_trace_chunk_status lttng_trace_chunk_override_name( struct lttng_trace_chunk *chunk, const char *name); +LTTNG_HIDDEN +enum lttng_trace_chunk_status lttng_trace_chunk_rename_path( + struct lttng_trace_chunk *chunk, const char *path); + LTTNG_HIDDEN enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials( struct lttng_trace_chunk *chunk,