X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fcommon%2Ftrace-chunk.c;h=c549f46b33c855334814355ef8a6e37f6cacadf2;hp=29977cabb1ae48e7ab015e717dc38173fcfd93d8;hb=0e2d816a62eccd0d159ec254e87ff80a7e6cf1bb;hpb=6def6cd7c0d7fc16c679898ccf170d8a45d68ab4 diff --git a/src/common/trace-chunk.c b/src/common/trace-chunk.c index 29977cabb..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,8 +74,15 @@ 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_overridden; @@ -84,8 +92,8 @@ struct lttng_trace_chunk { 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; }; @@ -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, @@ -211,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); } @@ -300,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) @@ -402,6 +483,17 @@ 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) { @@ -532,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) { @@ -567,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); @@ -591,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) { @@ -602,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); @@ -611,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; @@ -705,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; @@ -718,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); @@ -738,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; @@ -758,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; @@ -799,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; @@ -830,11 +1011,11 @@ void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) 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.is_set) { + !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. @@ -852,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 ? @@ -867,10 +1049,10 @@ 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; } @@ -881,9 +1063,9 @@ void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) &trace_chunk->top_level_directories, i); ret = lttng_directory_handle_rename_as_user( - &trace_chunk->session_output_directory.value, + trace_chunk->session_output_directory, top_level_name, - &temporary_rename_directory, + temporary_rename_directory, top_level_name, LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ? NULL : @@ -891,12 +1073,12 @@ void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) 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 { @@ -904,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; } @@ -916,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 ? @@ -928,20 +1111,18 @@ 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; ret = lttng_directory_handle_rename_as_user( - &trace_chunk->session_output_directory.value, + trace_chunk->session_output_directory, directory_to_rename, - &archived_chunks_directory.value, + archived_chunks_directory, archived_chunk_name, LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ? NULL : @@ -952,9 +1133,7 @@ void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) } 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); @@ -1001,7 +1180,15 @@ 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: return status; @@ -1014,6 +1201,10 @@ const char *lttng_trace_chunk_command_type_get_name( 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(); } @@ -1130,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 @@ -1197,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; } } @@ -1287,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(