X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fcommon%2Ftrace-chunk.c;h=c549f46b33c855334814355ef8a6e37f6cacadf2;hp=451832fde245bbad1e35839f0a71e05f50454532;hb=0e2d816a62eccd0d159ec254e87ff80a7e6cf1bb;hpb=93bed9fe8f48c11b7bb1224db36d82404cea080d diff --git a/src/common/trace-chunk.c b/src/common/trace-chunk.c index 451832fde..c549f46b3 100644 --- a/src/common/trace-chunk.c +++ b/src/common/trace-chunk.c @@ -66,6 +66,7 @@ struct chunk_credentials { struct lttng_credentials user; }; +/* NOTE: Make sure to update lttng_trace_chunk_copy if you modify this. */ struct lttng_trace_chunk { pthread_mutex_t lock; struct urcu_ref ref; @@ -73,26 +74,33 @@ struct lttng_trace_chunk { /* * First-level directories created within the trace chunk. * Elements are of type 'char *'. + * + * Only used by _owner_ mode chunks. */ struct lttng_dynamic_pointer_array top_level_directories; + /* + * All files contained within the trace chunk. + * Array of paths (char *). + */ + struct lttng_dynamic_pointer_array files; /* Is contained within an lttng_trace_chunk_registry_element? */ bool in_registry_element; - bool name_overriden; + bool name_overridden; char *name; /* An unset id means the chunk is anonymous. */ LTTNG_OPTIONAL(uint64_t) id; LTTNG_OPTIONAL(time_t) timestamp_creation; LTTNG_OPTIONAL(time_t) timestamp_close; LTTNG_OPTIONAL(struct chunk_credentials) credentials; - LTTNG_OPTIONAL(struct lttng_directory_handle) session_output_directory; - LTTNG_OPTIONAL(struct lttng_directory_handle) chunk_directory; + struct lttng_directory_handle *session_output_directory; + struct lttng_directory_handle *chunk_directory; LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type) close_command; }; /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */ struct lttng_trace_chunk_registry_element { - uint64_t session_id; struct lttng_trace_chunk chunk; + uint64_t session_id; /* Weak and only set when added. */ struct lttng_trace_chunk_registry *registry; struct cds_lfht_node trace_chunk_registry_ht_node; @@ -104,11 +112,17 @@ struct lttng_trace_chunk_registry { struct cds_lfht *ht; }; -const char *close_command_names[] = { +static const +char *close_command_names[] = { [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] = "move to completed chunk folder", + [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION] = + "no operation", + [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE] = + "delete", }; +static const chunk_close_command close_command_funcs[] = { [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] = lttng_trace_chunk_move_to_completed, @@ -165,8 +179,9 @@ char *generate_chunk_name(uint64_t chunk_id, time_t creation_timestamp, { int ret = 0; char *new_name= NULL; - char start_datetime[sizeof("YYYYmmddTHHMMSS+HHMM")] = {}; - char end_datetime_suffix[sizeof("-YYYYmmddTHHMMSS+HHMM")] = {}; + char start_datetime[ISO8601_STR_LEN] = {}; + /* Add 1 for a '-' prefix. */ + char end_datetime_suffix[ISO8601_STR_LEN + 1] = {}; ret = time_to_iso8601_str( creation_timestamp, @@ -180,7 +195,7 @@ char *generate_chunk_name(uint64_t chunk_id, time_t creation_timestamp, ret = time_to_iso8601_str( *close_timestamp, end_datetime_suffix + 1, - sizeof(end_datetime_suffix)); + sizeof(end_datetime_suffix) - 1); if (ret) { ERR("Failed to format trace chunk end date time"); goto error; @@ -210,21 +225,25 @@ void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk) urcu_ref_init(&chunk->ref); pthread_mutex_init(&chunk->lock, NULL); lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free); + lttng_dynamic_pointer_array_init(&chunk->files, free); } static void lttng_trace_chunk_fini(struct lttng_trace_chunk *chunk) { - if (chunk->session_output_directory.is_set) { - lttng_directory_handle_fini( - &chunk->session_output_directory.value); + if (chunk->session_output_directory) { + lttng_directory_handle_put( + chunk->session_output_directory); + chunk->session_output_directory = NULL; } - if (chunk->chunk_directory.is_set) { - lttng_directory_handle_fini(&chunk->chunk_directory.value); + if (chunk->chunk_directory) { + lttng_directory_handle_put(chunk->chunk_directory); + chunk->chunk_directory = NULL; } free(chunk->name); chunk->name = NULL; lttng_dynamic_pointer_array_reset(&chunk->top_level_directories); + lttng_dynamic_pointer_array_reset(&chunk->files); pthread_mutex_destroy(&chunk->lock); } @@ -299,6 +318,69 @@ error: return NULL; } +LTTNG_HIDDEN +struct lttng_trace_chunk *lttng_trace_chunk_copy( + struct lttng_trace_chunk *source_chunk) +{ + struct lttng_trace_chunk *new_chunk = lttng_trace_chunk_allocate(); + + if (!new_chunk) { + goto end; + } + + pthread_mutex_lock(&source_chunk->lock); + /* + * A new chunk is always a user; it shall create no new trace + * subdirectories. + */ + new_chunk->mode = (typeof(new_chunk->mode)) { + .is_set = true, + .value = TRACE_CHUNK_MODE_USER, + }; + /* + * top_level_directories is not copied as it is never used + * by _user_ mode chunks. + */ + /* The new chunk is not part of a registry (yet, at least). */ + new_chunk->in_registry_element = false; + new_chunk->name_overridden = source_chunk->name_overridden; + if (source_chunk->name) { + new_chunk->name = strdup(source_chunk->name); + if (!new_chunk->name) { + ERR("Failed to copy source trace chunk name in %s()", + __FUNCTION__); + goto error_unlock; + } + } + new_chunk->id = source_chunk->id; + new_chunk->timestamp_creation = source_chunk->timestamp_creation; + new_chunk->timestamp_close = source_chunk->timestamp_close; + new_chunk->credentials = source_chunk->credentials; + if (source_chunk->session_output_directory) { + const bool reference_acquired = lttng_directory_handle_get( + source_chunk->session_output_directory); + + assert(reference_acquired); + new_chunk->session_output_directory = + source_chunk->session_output_directory; + } + if (source_chunk->chunk_directory) { + const bool reference_acquired = lttng_directory_handle_get( + source_chunk->chunk_directory); + + assert(reference_acquired); + new_chunk->chunk_directory = source_chunk->chunk_directory; + } + new_chunk->close_command = source_chunk->close_command; + pthread_mutex_unlock(&source_chunk->lock); +end: + return new_chunk; +error_unlock: + pthread_mutex_unlock(&source_chunk->lock); + lttng_trace_chunk_put(new_chunk); + return NULL; +} + LTTNG_HIDDEN enum lttng_trace_chunk_status lttng_trace_chunk_get_id( struct lttng_trace_chunk *chunk, uint64_t *id) @@ -366,12 +448,14 @@ enum lttng_trace_chunk_status lttng_trace_chunk_set_close_timestamp( goto end; } LTTNG_OPTIONAL_SET(&chunk->timestamp_close, close_ts); - free(chunk->name); - chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id), - LTTNG_OPTIONAL_GET(chunk->timestamp_creation), - &close_ts); - if (!chunk->name) { - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + if (!chunk->name_overridden) { + free(chunk->name); + chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id), + LTTNG_OPTIONAL_GET(chunk->timestamp_creation), + &close_ts); + if (!chunk->name) { + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + } } end: pthread_mutex_unlock(&chunk->lock); @@ -381,13 +465,13 @@ end: LTTNG_HIDDEN enum lttng_trace_chunk_status lttng_trace_chunk_get_name( struct lttng_trace_chunk *chunk, const char **name, - bool *name_overriden) + bool *name_overridden) { enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; pthread_mutex_lock(&chunk->lock); - if (name_overriden) { - *name_overriden = chunk->name_overriden; + if (name_overridden) { + *name_overridden = chunk->name_overridden; } if (!chunk->name) { status = LTTNG_TRACE_CHUNK_STATUS_NONE; @@ -399,6 +483,38 @@ end: return status; } +LTTNG_HIDDEN +bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk *chunk) +{ + bool name_overridden; + + pthread_mutex_lock(&chunk->lock); + name_overridden = chunk->name_overridden; + pthread_mutex_unlock(&chunk->lock); + return name_overridden; +} + +static +bool is_valid_chunk_name(const char *name) +{ + size_t len; + + if (!name) { + return false; + } + + len = lttng_strnlen(name, LTTNG_NAME_MAX); + if (len == 0 || len == LTTNG_NAME_MAX) { + return false; + } + + if (strchr(name, '/') || strchr(name, '.')) { + return false; + } + + return true; +} + LTTNG_HIDDEN enum lttng_trace_chunk_status lttng_trace_chunk_override_name( struct lttng_trace_chunk *chunk, const char *name) @@ -407,7 +523,7 @@ enum lttng_trace_chunk_status lttng_trace_chunk_override_name( char *new_name; enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - if (!name || !*name || strnlen(name, LTTNG_NAME_MAX) == LTTNG_NAME_MAX) { + if (!is_valid_chunk_name(name)) { ERR("Attempted to set an invalid name on a trace chunk: name = %s", name ? : "NULL"); status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT; @@ -429,7 +545,7 @@ enum lttng_trace_chunk_status lttng_trace_chunk_override_name( } free(chunk->name); chunk->name = new_name; - chunk->name_overriden = true; + chunk->name_overridden = true; end_unlock: pthread_mutex_unlock(&chunk->lock); end: @@ -508,7 +624,8 @@ enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner( { int ret; enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - struct lttng_directory_handle chunk_directory_handle; + struct lttng_directory_handle *chunk_directory_handle = NULL; + bool reference_acquired; pthread_mutex_lock(&chunk->lock); if (chunk->mode.is_set) { @@ -543,18 +660,20 @@ enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner( goto end; } } - ret = lttng_directory_handle_init_from_handle(&chunk_directory_handle, + chunk_directory_handle = lttng_directory_handle_create_from_handle( chunk->name, session_output_directory); - if (ret) { + if (!chunk_directory_handle) { /* The function already logs on all error paths. */ status = LTTNG_TRACE_CHUNK_STATUS_ERROR; goto end; } - LTTNG_OPTIONAL_SET(&chunk->session_output_directory, - lttng_directory_handle_move(session_output_directory)); - LTTNG_OPTIONAL_SET(&chunk->chunk_directory, - lttng_directory_handle_move(&chunk_directory_handle)); + chunk->chunk_directory = chunk_directory_handle; + chunk_directory_handle = NULL; + reference_acquired = lttng_directory_handle_get( + session_output_directory); + assert(reference_acquired); + chunk->session_output_directory = session_output_directory; LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER); end: pthread_mutex_unlock(&chunk->lock); @@ -567,6 +686,7 @@ enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user( struct lttng_directory_handle *chunk_directory) { enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + bool reference_acquired; pthread_mutex_lock(&chunk->lock); if (chunk->mode.is_set) { @@ -578,8 +698,9 @@ enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user( status = LTTNG_TRACE_CHUNK_STATUS_ERROR; goto end; } - LTTNG_OPTIONAL_SET(&chunk->chunk_directory, - lttng_directory_handle_move(chunk_directory)); + reference_acquired = lttng_directory_handle_get(chunk_directory); + assert(reference_acquired); + chunk->chunk_directory = chunk_directory; LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER); end: pthread_mutex_unlock(&chunk->lock); @@ -587,19 +708,19 @@ end: } LTTNG_HIDDEN -enum lttng_trace_chunk_status lttng_trace_chunk_get_chunk_directory_handle( +enum lttng_trace_chunk_status lttng_trace_chunk_borrow_chunk_directory_handle( struct lttng_trace_chunk *chunk, const struct lttng_directory_handle **handle) { enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; pthread_mutex_lock(&chunk->lock); - if (!chunk->chunk_directory.is_set) { + if (!chunk->chunk_directory) { status = LTTNG_TRACE_CHUNK_STATUS_NONE; goto end; } - *handle = &chunk->chunk_directory.value; + *handle = chunk->chunk_directory; end: pthread_mutex_unlock(&chunk->lock); return status; @@ -633,7 +754,7 @@ int add_top_level_directory_unique(struct lttng_trace_chunk *chunk, } if (!found) { - char *copy = strndup(new_path, new_path_top_level_len); + char *copy = lttng_strndup(new_path, new_path_top_level_len); DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"", new_path, chunk->name ? : "(unnamed)"); @@ -681,7 +802,7 @@ enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory( status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; goto end; } - if (!chunk->chunk_directory.is_set) { + if (!chunk->chunk_directory) { ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory", path); status = LTTNG_TRACE_CHUNK_STATUS_ERROR; @@ -694,7 +815,7 @@ enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory( goto end; } ret = lttng_directory_handle_create_subdirectory_recursive_as_user( - &chunk->chunk_directory.value, path, + chunk->chunk_directory, path, DIR_CREATION_MODE, chunk->credentials.value.use_current_user ? NULL : &chunk->credentials.value.user); @@ -714,10 +835,84 @@ end: return status; } +/* + * TODO: Implement O(1) lookup. + */ +static +bool lttng_trace_chunk_find_file(struct lttng_trace_chunk *chunk, + const char *path, size_t *index) +{ + size_t i, count; + + count = lttng_dynamic_pointer_array_get_count(&chunk->files); + for (i = 0; i < count; i++) { + const char *iter_path = + lttng_dynamic_pointer_array_get_pointer( + &chunk->files, i); + if (!strcmp(iter_path, path)) { + if (index) { + *index = i; + } + return true; + } + } + return false; +} + +static +enum lttng_trace_chunk_status lttng_trace_chunk_add_file( + struct lttng_trace_chunk *chunk, + const char *path) +{ + char *copy; + int ret; + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + if (lttng_trace_chunk_find_file(chunk, path, NULL)) { + return LTTNG_TRACE_CHUNK_STATUS_OK; + } + DBG("Adding new file \"%s\" to trace chunk \"%s\"", + path, chunk->name ? : "(unnamed)"); + copy = strdup(path); + if (!copy) { + PERROR("Failed to copy path"); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + ret = lttng_dynamic_pointer_array_add_pointer( + &chunk->files, copy); + if (ret) { + ERR("Allocation failure while adding file to a trace chunk"); + free(copy); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } +end: + return status; +} + +static +void lttng_trace_chunk_remove_file( + struct lttng_trace_chunk *chunk, + const char *path) +{ + size_t index; + bool found; + int ret; + + found = lttng_trace_chunk_find_file(chunk, path, &index); + if (!found) { + return; + } + ret = lttng_dynamic_pointer_array_remove_pointer( + &chunk->files, index); + assert(!ret); +} + LTTNG_HIDDEN enum lttng_trace_chunk_status lttng_trace_chunk_open_file( struct lttng_trace_chunk *chunk, const char *file_path, - int flags, mode_t mode, int *out_fd) + int flags, mode_t mode, int *out_fd, bool expect_no_file) { int ret; enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; @@ -734,20 +929,29 @@ enum lttng_trace_chunk_status lttng_trace_chunk_open_file( status = LTTNG_TRACE_CHUNK_STATUS_ERROR; goto end; } - if (!chunk->chunk_directory.is_set) { + if (!chunk->chunk_directory) { ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory", file_path); status = LTTNG_TRACE_CHUNK_STATUS_ERROR; goto end; } + status = lttng_trace_chunk_add_file(chunk, file_path); + if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { + goto end; + } ret = lttng_directory_handle_open_file_as_user( - &chunk->chunk_directory.value, file_path, flags, mode, + chunk->chunk_directory, file_path, flags, mode, chunk->credentials.value.use_current_user ? NULL : &chunk->credentials.value.user); if (ret < 0) { - ERR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d", + if (errno == ENOENT && expect_no_file) { + status = LTTNG_TRACE_CHUNK_STATUS_NO_FILE; + } else { + PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d", file_path, flags, (int) mode); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + } + lttng_trace_chunk_remove_file(chunk, file_path); goto end; } *out_fd = ret; @@ -775,20 +979,21 @@ int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk, status = LTTNG_TRACE_CHUNK_STATUS_ERROR; goto end; } - if (!chunk->chunk_directory.is_set) { + if (!chunk->chunk_directory) { ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory", file_path); status = LTTNG_TRACE_CHUNK_STATUS_ERROR; goto end; } ret = lttng_directory_handle_unlink_file_as_user( - &chunk->chunk_directory.value, file_path, + chunk->chunk_directory, file_path, chunk->credentials.value.use_current_user ? NULL : &chunk->credentials.value.user); if (ret < 0) { status = LTTNG_TRACE_CHUNK_STATUS_ERROR; goto end; } + lttng_trace_chunk_remove_file(chunk, file_path); end: pthread_mutex_unlock(&chunk->lock); return status; @@ -800,19 +1005,26 @@ void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) int ret; char *directory_to_rename = NULL; bool free_directory_to_rename = false; - const int session_dirfd = - trace_chunk->session_output_directory.value.dirfd; char *archived_chunk_name = NULL; const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id); const time_t creation_timestamp = LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation); const time_t close_timestamp = LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close); - LTTNG_OPTIONAL(struct lttng_directory_handle) archived_chunks_directory; + struct lttng_directory_handle *archived_chunks_directory = NULL; + + if (!trace_chunk->mode.is_set || + trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER || + !trace_chunk->session_output_directory) { + /* + * This command doesn't need to run if the output is remote + * or if the trace chunk is not owned by this process. + */ + goto end; + } - assert(trace_chunk->mode.is_set); assert(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER); - assert(!trace_chunk->name_overriden); + assert(!trace_chunk->name_overridden); /* * The fist trace chunk of a session is directly output to the @@ -821,12 +1033,13 @@ void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) * is renamed to match the chunk's name. */ if (chunk_id == 0) { - struct lttng_directory_handle temporary_rename_directory; + struct lttng_directory_handle *temporary_rename_directory = + NULL; size_t i, count = lttng_dynamic_pointer_array_get_count( - &trace_chunk->top_level_directories); + &trace_chunk->top_level_directories); ret = lttng_directory_handle_create_subdirectory_as_user( - &trace_chunk->session_output_directory.value, + trace_chunk->session_output_directory, DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY, DIR_CREATION_MODE, !trace_chunk->credentials.value.use_current_user ? @@ -836,35 +1049,36 @@ void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY); } - ret = lttng_directory_handle_init_from_handle(&temporary_rename_directory, + temporary_rename_directory = lttng_directory_handle_create_from_handle( DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY, - &trace_chunk->session_output_directory.value); - if (ret) { + 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 int temp_dirfd = temporary_rename_directory.dirfd; const char *top_level_name = lttng_dynamic_pointer_array_get_pointer( &trace_chunk->top_level_directories, i); - /* - * FIXME replace renamat() use by directory handle - * wrapper for non-POSIX 2008 systems. - */ - ret = renameat(session_dirfd, top_level_name, - temp_dirfd, top_level_name); + 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_fini( - &temporary_rename_directory); + lttng_directory_handle_put( + temporary_rename_directory); goto end; } } - lttng_directory_handle_fini(&temporary_rename_directory); + lttng_directory_handle_put(temporary_rename_directory); directory_to_rename = DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY; free_directory_to_rename = false; } else { @@ -872,6 +1086,7 @@ void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) 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; } @@ -884,7 +1099,7 @@ void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) } ret = lttng_directory_handle_create_subdirectory_as_user( - &trace_chunk->session_output_directory.value, + trace_chunk->session_output_directory, DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY, DIR_CREATION_MODE, !trace_chunk->credentials.value.use_current_user ? @@ -896,38 +1111,53 @@ void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) goto end; } - ret = lttng_directory_handle_init_from_handle( - &archived_chunks_directory.value, + archived_chunks_directory = lttng_directory_handle_create_from_handle( DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY, - &trace_chunk->session_output_directory.value); - if (ret) { + trace_chunk->session_output_directory); + if (!archived_chunks_directory) { PERROR("Failed to get handle to archived trace chunks directory"); goto end; } - archived_chunks_directory.is_set = true; - /* - * FIXME replace renamat() use by directory handle - * wrapper for non-POSIX 2008 systems. - */ - ret = renameat(session_dirfd, directory_to_rename, - archived_chunks_directory.value.dirfd, - archived_chunk_name); + ret = lttng_directory_handle_rename_as_user( + trace_chunk->session_output_directory, + directory_to_rename, + archived_chunks_directory, + archived_chunk_name, + LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ? + NULL : + &trace_chunk->credentials.value.user); if (ret) { PERROR("Failed to rename folder \"%s\" to \"%s\"", directory_to_rename, archived_chunk_name); } end: - if (archived_chunks_directory.is_set) { - lttng_directory_handle_fini(&archived_chunks_directory.value); - } + lttng_directory_handle_put(archived_chunks_directory); free(archived_chunk_name); if (free_directory_to_rename) { free(directory_to_rename); } } +LTTNG_HIDDEN +enum lttng_trace_chunk_status lttng_trace_chunk_get_close_command( + struct lttng_trace_chunk *chunk, + enum lttng_trace_chunk_command_type *command_type) +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + pthread_mutex_lock(&chunk->lock); + if (chunk->close_command.is_set) { + *command_type = chunk->close_command.value; + status = LTTNG_TRACE_CHUNK_STATUS_OK; + } else { + status = LTTNG_TRACE_CHUNK_STATUS_NONE; + } + pthread_mutex_unlock(&chunk->lock); + return status; +} + LTTNG_HIDDEN enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command( struct lttng_trace_chunk *chunk, @@ -938,7 +1168,7 @@ enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command( if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED || close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) { status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT; - goto end_unlock; + goto end; } pthread_mutex_lock(&chunk->lock); @@ -950,12 +1180,36 @@ enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command( DBG("Setting trace chunk close command to \"%s\"", close_command_names[close_command]); } - LTTNG_OPTIONAL_SET(&chunk->close_command, close_command); + /* + * Unset close command for no-op for backward compatibility with relayd + * 2.11. + */ + if (close_command != LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) { + LTTNG_OPTIONAL_SET(&chunk->close_command, close_command); + } else { + LTTNG_OPTIONAL_UNSET(&chunk->close_command); + } pthread_mutex_unlock(&chunk->lock); -end_unlock: +end: return status; } +LTTNG_HIDDEN +const char *lttng_trace_chunk_command_type_get_name( + enum lttng_trace_chunk_command_type command) +{ + switch (command) { + case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED: + return "move to completed trace chunk folder"; + case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION: + return "no operation"; + case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE: + return "delete"; + default: + abort(); + } +} + LTTNG_HIDDEN bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk) { @@ -1034,7 +1288,7 @@ end: return registry; error: lttng_trace_chunk_registry_destroy(registry); - goto end; + return NULL; } LTTNG_HIDDEN @@ -1067,15 +1321,16 @@ lttng_trace_chunk_registry_element_create_from_chunk( element->chunk = *chunk; lttng_trace_chunk_init(&element->chunk); - if (chunk->session_output_directory.is_set) { - element->chunk.session_output_directory.value = - lttng_directory_handle_move( - &chunk->session_output_directory.value); + if (chunk->session_output_directory) { + /* Transferred ownership. */ + element->chunk.session_output_directory = + chunk->session_output_directory; + chunk->session_output_directory = NULL; } - if (chunk->chunk_directory.is_set) { - element->chunk.chunk_directory.value = - lttng_directory_handle_move( - &chunk->chunk_directory.value); + if (chunk->chunk_directory) { + /* Transferred ownership. */ + element->chunk.chunk_directory = chunk->chunk_directory; + chunk->chunk_directory = NULL; } /* * The original chunk becomes invalid; the name attribute is transferred @@ -1134,7 +1389,7 @@ lttng_trace_chunk_registry_publish_chunk( * * Re-attempt to publish. */ - ERR("Attemp to publish a trace chunk to the chunk registry raced with a trace chunk deletion"); + ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion"); continue; } } @@ -1224,6 +1479,41 @@ lttng_trace_chunk_registry_find_chunk( session_id, &chunk_id); } +LTTNG_HIDDEN +int lttng_trace_chunk_registry_chunk_exists( + const struct lttng_trace_chunk_registry *registry, + uint64_t session_id, uint64_t chunk_id, bool *chunk_exists) +{ + int ret = 0; + const struct lttng_trace_chunk_registry_element target_element = { + .chunk.id.is_set = true, + .chunk.id.value = chunk_id, + .session_id = session_id, + }; + const unsigned long element_hash = + lttng_trace_chunk_registry_element_hash( + &target_element); + struct cds_lfht_node *published_node; + struct cds_lfht_iter iter; + + rcu_read_lock(); + cds_lfht_lookup(registry->ht, + element_hash, + lttng_trace_chunk_registry_element_match, + &target_element, + &iter); + published_node = cds_lfht_iter_get_node(&iter); + if (!published_node) { + *chunk_exists = false; + goto end; + } + + *chunk_exists = !cds_lfht_is_node_deleted(published_node); +end: + rcu_read_unlock(); + return ret; +} + LTTNG_HIDDEN struct lttng_trace_chunk * lttng_trace_chunk_registry_find_anonymous_chunk( @@ -1233,3 +1523,49 @@ lttng_trace_chunk_registry_find_anonymous_chunk( return _lttng_trace_chunk_registry_find_chunk(registry, session_id, NULL); } + +unsigned int lttng_trace_chunk_registry_put_each_chunk( + struct lttng_trace_chunk_registry *registry) +{ + struct cds_lfht_iter iter; + struct lttng_trace_chunk_registry_element *chunk_element; + unsigned int trace_chunks_left = 0; + + DBG("Releasing trace chunk registry to all trace chunks"); + rcu_read_lock(); + cds_lfht_for_each_entry(registry->ht, + &iter, chunk_element, trace_chunk_registry_ht_node) { + const char *chunk_id_str = "none"; + char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)]; + + pthread_mutex_lock(&chunk_element->chunk.lock); + if (chunk_element->chunk.id.is_set) { + int fmt_ret; + + fmt_ret = snprintf(chunk_id_buf, sizeof(chunk_id_buf), + "%" PRIu64, + chunk_element->chunk.id.value); + if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) { + chunk_id_str = "formatting error"; + } else { + chunk_id_str = chunk_id_buf; + } + } + + DBG("Releasing reference to trace chunk: session_id = %" PRIu64 + "chunk_id = %s, name = \"%s\", status = %s", + chunk_element->session_id, + chunk_id_str, + chunk_element->chunk.name ? : "none", + chunk_element->chunk.close_command.is_set ? + "open" : "closed"); + pthread_mutex_unlock(&chunk_element->chunk.lock); + lttng_trace_chunk_put(&chunk_element->chunk); + trace_chunks_left++; + } + rcu_read_unlock(); + DBG("Released reference to %u trace chunks in %s()", trace_chunks_left, + __FUNCTION__); + + return trace_chunks_left; +}