X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fcommon%2Ftrace-chunk.c;h=32792926928d6abb6ebed749460e3a970307c2f3;hp=dbab99d192876a73ce49be5975b674f55ec4e60e;hb=d7a20fcf33143103aa58cf1b48f4507af2d09e70;hpb=e10aec8f225c0822811c65401710a039d7e2d7d7 diff --git a/src/common/trace-chunk.c b/src/common/trace-chunk.c index dbab99d19..327929269 100644 --- a/src/common/trace-chunk.c +++ b/src/common/trace-chunk.c @@ -1,41 +1,34 @@ /* - * Copyright (C) 2019 - Jérémie Galarneau + * Copyright (C) 2019 Jérémie Galarneau * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License, version 2.1 only, - * as published by the Free Software Foundation. + * SPDX-License-Identifier: LGPL-2.1-only * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include -#include -#include #include #include #include +#include +#include +#include /* * Two ISO 8601-compatible timestamps, separated by a hypen, followed an @@ -55,17 +48,32 @@ 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); +/* Empty callback. */ +static +int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk); +/* Unlink old chunk files. */ +static +int lttng_trace_chunk_delete_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(), + * - 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; @@ -73,26 +81,42 @@ 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; char *name; + char *path; /* 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; + /* + * fd_tracker instance through which file descriptors should be + * created/closed. + * + * An fd_tracker always outlives any trace chunk; there is no + * need to perform any reference counting of that object. + */ + struct fd_tracker *fd_tracker; }; /* 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,16 +128,130 @@ struct lttng_trace_chunk_registry { struct cds_lfht *ht; }; -const char *close_command_names[] = { +struct fs_handle_untracked { + struct fs_handle parent; + int fd; + struct { + struct lttng_directory_handle *directory_handle; + char *path; + } location; +}; + +static +int fs_handle_untracked_get_fd(struct fs_handle *handle); +static +void fs_handle_untracked_put_fd(struct fs_handle *handle); +static +int fs_handle_untracked_unlink(struct fs_handle *handle); +static +int fs_handle_untracked_close(struct fs_handle *handle); + +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", }; -chunk_close_command close_command_funcs[] = { +static const +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, + [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION] = + lttng_trace_chunk_no_operation, + [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE] = + lttng_trace_chunk_delete_post_release, }; +static +struct fs_handle *fs_handle_untracked_create( + struct lttng_directory_handle *directory_handle, + const char *path, + int fd) +{ + struct fs_handle_untracked *handle = NULL; + bool reference_acquired; + char *path_copy = strdup(path); + + assert(fd >= 0); + if (!path_copy) { + PERROR("Failed to copy file path while creating untracked filesystem handle"); + goto end; + } + + handle = zmalloc(sizeof(typeof(*handle))); + if (!handle) { + PERROR("Failed to allocate untracked filesystem handle"); + goto end; + } + + handle->parent = (typeof(handle->parent)) { + .get_fd = fs_handle_untracked_get_fd, + .put_fd = fs_handle_untracked_put_fd, + .unlink = fs_handle_untracked_unlink, + .close = fs_handle_untracked_close, + }; + + handle->fd = fd; + reference_acquired = lttng_directory_handle_get(directory_handle); + assert(reference_acquired); + handle->location.directory_handle = directory_handle; + /* Ownership is transferred. */ + handle->location.path = path_copy; + path_copy = NULL; +end: + free(path_copy); + return handle ? &handle->parent : NULL; +} + +static +int fs_handle_untracked_get_fd(struct fs_handle *_handle) +{ + struct fs_handle_untracked *handle = container_of( + _handle, struct fs_handle_untracked, parent); + + return handle->fd; +} + +static +void fs_handle_untracked_put_fd(struct fs_handle *_handle) +{ + /* no-op. */ +} + +static +int fs_handle_untracked_unlink(struct fs_handle *_handle) +{ + struct fs_handle_untracked *handle = container_of( + _handle, struct fs_handle_untracked, parent); + + return lttng_directory_handle_unlink_file( + handle->location.directory_handle, + handle->location.path); +} + +static +void fs_handle_untracked_destroy(struct fs_handle_untracked *handle) +{ + lttng_directory_handle_put(handle->location.directory_handle); + free(handle->location.path); + free(handle); +} + +static +int fs_handle_untracked_close(struct fs_handle *_handle) +{ + struct fs_handle_untracked *handle = container_of( + _handle, struct fs_handle_untracked, parent); + int ret = close(handle->fd); + + fs_handle_untracked_destroy(handle); + return ret; +} + static bool lttng_trace_chunk_registry_element_equals( const struct lttng_trace_chunk_registry_element *a, @@ -165,8 +303,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 +319,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 +349,27 @@ 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; + 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); } @@ -252,7 +397,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] = {}; @@ -290,6 +435,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: @@ -299,6 +457,87 @@ error: return NULL; } +LTTNG_HIDDEN +void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk *chunk, + struct fd_tracker *fd_tracker) +{ + assert(!chunk->session_output_directory); + assert(!chunk->chunk_directory); + assert(lttng_dynamic_pointer_array_get_count(&chunk->files) == 0); + chunk->fd_tracker = fd_tracker; +} + +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; + } + } + 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; + 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; + new_chunk->fd_tracker = source_chunk->fd_tracker; + 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 +605,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); @@ -399,6 +640,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) { @@ -408,7 +660,7 @@ bool is_valid_chunk_name(const char *name) return false; } - len = strnlen(name, LTTNG_NAME_MAX); + len = lttng_strnlen(name, LTTNG_NAME_MAX); if (len == 0 || len == LTTNG_NAME_MAX) { return false; } @@ -425,9 +677,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"); @@ -442,6 +695,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"); @@ -450,13 +704,243 @@ 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 && 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 = chunk->fd_tracker ? + fd_tracker_create_directory_handle_from_handle( + chunk->fd_tracker, + chunk->session_output_directory, + path) : + 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 && 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 if (old_path) { + 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); + goto end; + } + } else { + /* Unexpected !old_path && !path. */ + status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT; + goto end; + } + +skip_move: + new_path = strdup(path); + if (!new_path) { + ERR("Failed to allocate new trace chunk path"); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + 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, @@ -529,7 +1013,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) { @@ -545,37 +1030,51 @@ 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 && 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 = + chunk->fd_tracker ? + fd_tracker_create_directory_handle_from_handle( + chunk->fd_tracker, + session_output_directory, + chunk->path) : + 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; } - ret = lttng_directory_handle_init_from_handle(&chunk_directory_handle, - chunk->name, + chunk->chunk_directory = chunk_directory_handle; + chunk_directory_handle = NULL; + reference_acquired = lttng_directory_handle_get( session_output_directory); - if (ret) { - /* 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)); + 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); @@ -588,6 +1087,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) { @@ -599,28 +1099,54 @@ 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)); - LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER); + 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); + return status; +} + +LTTNG_HIDDEN +enum lttng_trace_chunk_status +lttng_trace_chunk_get_session_output_directory_handle( + struct lttng_trace_chunk *chunk, + struct lttng_directory_handle **handle) +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + pthread_mutex_lock(&chunk->lock); + if (!chunk->session_output_directory) { + status = LTTNG_TRACE_CHUNK_STATUS_NONE; + *handle = NULL; + goto end; + } else { + const bool reference_acquired = lttng_directory_handle_get( + chunk->session_output_directory); + + assert(reference_acquired); + *handle = chunk->session_output_directory; + } end: pthread_mutex_unlock(&chunk->lock); return status; } 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; @@ -654,7 +1180,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)"); @@ -702,7 +1228,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; @@ -715,7 +1241,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); @@ -735,16 +1261,93 @@ end: return status; } -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) +/* + * 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); +} + +static +enum lttng_trace_chunk_status _lttng_trace_chunk_open_fs_handle_locked( + struct lttng_trace_chunk *chunk, + const char *file_path, + int flags, + mode_t mode, + struct fs_handle **out_handle, + bool expect_no_file) { int ret; enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; DBG("Opening trace chunk file \"%s\"", file_path); - pthread_mutex_lock(&chunk->lock); if (!chunk->credentials.is_set) { /* * Fatal error, credentials must be set before a @@ -755,25 +1358,100 @@ 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; } - ret = lttng_directory_handle_open_file_as_user( - &chunk->chunk_directory.value, file_path, flags, mode, - chunk->credentials.value.use_current_user ? - NULL : &chunk->credentials.value.user); + status = lttng_trace_chunk_add_file(chunk, file_path); + if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { + goto end; + } + if (chunk->fd_tracker) { + assert(chunk->credentials.value.use_current_user); + *out_handle = fd_tracker_open_fs_handle(chunk->fd_tracker, + chunk->chunk_directory, file_path, flags, &mode); + ret = *out_handle ? 0 : -1; + } else { + ret = lttng_directory_handle_open_file_as_user( + chunk->chunk_directory, file_path, flags, mode, + chunk->credentials.value.use_current_user ? + NULL : + &chunk->credentials.value.user); + if (ret >= 0) { + *out_handle = fs_handle_untracked_create( + chunk->chunk_directory, file_path, ret); + if (!*out_handle) { + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + } + } 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; end: + return status; +} + +LTTNG_HIDDEN +enum lttng_trace_chunk_status lttng_trace_chunk_open_fs_handle( + struct lttng_trace_chunk *chunk, + const char *file_path, + int flags, + mode_t mode, + struct fs_handle **out_handle, + bool expect_no_file) +{ + enum lttng_trace_chunk_status status; + + pthread_mutex_lock(&chunk->lock); + status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path, + flags, mode, out_handle, expect_no_file); + pthread_mutex_unlock(&chunk->lock); + return status; +} + +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, + bool expect_no_file) +{ + enum lttng_trace_chunk_status status; + struct fs_handle *fs_handle; + + pthread_mutex_lock(&chunk->lock); + /* + * Using this method is never valid when an fd_tracker is being + * used since the resulting file descriptor would not be tracked. + */ + assert(!chunk->fd_tracker); + status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path, + flags, mode, &fs_handle, expect_no_file); pthread_mutex_unlock(&chunk->lock); + + if (status == LTTNG_TRACE_CHUNK_STATUS_OK) { + *out_fd = fs_handle_get_fd(fs_handle); + /* + * Does not close the fd; we just "unbox" it from the fs_handle. + */ + fs_handle_untracked_destroy(container_of( + fs_handle, struct fs_handle_untracked, parent)); + } + return status; } @@ -789,49 +1467,89 @@ 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); 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; } static -void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk) +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 = 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; + enum lttng_trace_chunk_status status; 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. @@ -841,79 +1559,18 @@ 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; - 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.value, - 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); - } - - ret = lttng_directory_handle_init_from_handle(&temporary_rename_directory, - DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY, - &trace_chunk->session_output_directory.value); - if (ret) { - 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.value, - 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); - goto end; - } - } - lttng_directory_handle_fini(&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"); - } - 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; } 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 ? @@ -925,36 +1582,164 @@ 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, - DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY, - &trace_chunk->session_output_directory.value); - if (ret) { + archived_chunks_directory = trace_chunk->fd_tracker ? + fd_tracker_create_directory_handle_from_handle( + trace_chunk->fd_tracker, + trace_chunk->session_output_directory, + DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY) : + lttng_directory_handle_create_from_handle( + DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY, + trace_chunk->session_output_directory); + if (!archived_chunks_directory) { PERROR("Failed to get handle to archived trace chunks directory"); + ret = -1; goto end; } - archived_chunks_directory.is_set = true; + + /* + * 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.value, - directory_to_rename, - &archived_chunks_directory.value, + trace_chunk->session_output_directory, + trace_chunk->path, + 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); + trace_chunk->path, + 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); + return ret; +} + +static +int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk) +{ + return 0; +} + +static +int lttng_trace_chunk_delete_post_release_user( + struct lttng_trace_chunk *trace_chunk) +{ + int ret = 0; + + DBG("Trace chunk \"delete\" close command post-release (User)"); + + /* Unlink all files. */ + while (lttng_dynamic_pointer_array_get_count(&trace_chunk->files) != 0) { + enum lttng_trace_chunk_status status; + const char *path; + + /* Remove first. */ + path = lttng_dynamic_pointer_array_get_pointer( + &trace_chunk->files, 0); + DBG("Unlink file: %s", path); + status = lttng_trace_chunk_unlink_file(trace_chunk, path); + if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ERR("Error unlinking file '%s' when deleting chunk", path); + ret = -1; + goto end; + } + } +end: + return ret; +} + +static +int lttng_trace_chunk_delete_post_release_owner( + struct lttng_trace_chunk *trace_chunk) +{ + enum lttng_trace_chunk_status status; + size_t i, count; + int ret = 0; + + ret = lttng_trace_chunk_delete_post_release_user(trace_chunk); + if (ret) { + goto end; + } + + DBG("Trace chunk \"delete\" close command post-release (Owner)"); + + assert(trace_chunk->session_output_directory); + assert(trace_chunk->chunk_directory); + + /* Remove empty directories. */ + count = lttng_dynamic_pointer_array_get_count( + &trace_chunk->top_level_directories); + + for (i = 0; i < count; i++) { + const char *top_level_name = + lttng_dynamic_pointer_array_get_pointer( + &trace_chunk->top_level_directories, i); + + status = lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk, top_level_name); + if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ERR("Error recursively removing subdirectory '%s' file when deleting chunk", + top_level_name); + ret = -1; + break; + } + } + if (!ret) { + lttng_directory_handle_put(trace_chunk->chunk_directory); + trace_chunk->chunk_directory = NULL; + + if (trace_chunk->path && trace_chunk->path[0] != '\0') { + status = lttng_directory_handle_remove_subdirectory( + trace_chunk->session_output_directory, + trace_chunk->path); + if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ERR("Error removing subdirectory '%s' file when deleting chunk", + trace_chunk->path); + ret = -1; + } + } + } + free(trace_chunk->path); + trace_chunk->path = NULL; +end: + return ret; +} + +/* + * For local files, session and consumer daemons all run the delete hook. The + * consumer daemons have the list of files to unlink, and technically the + * session daemon is the owner of the chunk. Unlink all files owned by each + * consumer daemon. + */ +static +int lttng_trace_chunk_delete_post_release( + struct lttng_trace_chunk *trace_chunk) +{ + if (!trace_chunk->chunk_directory) { + return 0; + } + + if (trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER) { + return lttng_trace_chunk_delete_post_release_owner(trace_chunk); + } else { + return lttng_trace_chunk_delete_post_release_user(trace_chunk); } } @@ -986,7 +1771,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); @@ -998,9 +1783,17 @@ 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; } @@ -1011,6 +1804,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(); } @@ -1039,7 +1836,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) { @@ -1094,7 +1895,7 @@ end: return registry; error: lttng_trace_chunk_registry_destroy(registry); - goto end; + return NULL; } LTTNG_HIDDEN @@ -1127,21 +1928,24 @@ 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 - * 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.fd_tracker = chunk->fd_tracker; element->chunk.in_registry_element = true; end: return element; @@ -1194,7 +1998,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; } } @@ -1284,6 +2088,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( @@ -1294,8 +2133,9 @@ lttng_trace_chunk_registry_find_anonymous_chunk( session_id, NULL); } +LTTNG_HIDDEN unsigned int lttng_trace_chunk_registry_put_each_chunk( - struct lttng_trace_chunk_registry *registry) + const struct lttng_trace_chunk_registry *registry) { struct cds_lfht_iter iter; struct lttng_trace_chunk_registry_element *chunk_element;