X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fcommon%2Ftrace-chunk.c;fp=src%2Fcommon%2Ftrace-chunk.c;h=0000000000000000000000000000000000000000;hp=d04d51637e603fe763a067a3271917d1da5e80a7;hb=a6bc4ca9d659caf016ef932fcd944029737ac57c;hpb=97535efaa975ca52bf02c2d5e76351bfd2e3defa diff --git a/src/common/trace-chunk.c b/src/common/trace-chunk.c deleted file mode 100644 index d04d51637..000000000 --- a/src/common/trace-chunk.c +++ /dev/null @@ -1,2191 +0,0 @@ -/* - * Copyright (C) 2019 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#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 - * index, i.e. --. - */ -#define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t)) -#define DIR_CREATION_MODE (S_IRWXU | S_IRWXG) - -enum trace_chunk_mode { - TRACE_CHUNK_MODE_USER, - TRACE_CHUNK_MODE_OWNER, -}; - -/* - * Callback to invoke on release of a trace chunk. Note that there is no - * need to 'lock' the trace chunk during the execution of these callbacks - * since only one thread may access a chunk during its destruction (the last - * to release its reference to the chunk). - */ -typedef int (*chunk_command)(struct lttng_trace_chunk *trace_chunk); - -/* Move a completed trace chunk to the 'completed' trace archive folder. */ -static -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; - LTTNG_OPTIONAL(enum trace_chunk_mode) mode; - /* - * 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; - - /* - * The creation and close timestamps are NOT monotonic. - * They must not be used in context were monotonicity is required. - */ - LTTNG_OPTIONAL(time_t) timestamp_creation; - LTTNG_OPTIONAL(time_t) timestamp_close; - - LTTNG_OPTIONAL(struct chunk_credentials) credentials; - 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 { - 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; - /* call_rcu delayed reclaim. */ - struct rcu_head rcu_node; -}; - -struct lttng_trace_chunk_registry { - struct cds_lfht *ht; -}; - -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", -}; - -static const -chunk_command close_command_post_release_funcs[] = { - [LTTNG_TRACE_CHUNK_COMMAND_TYPE_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); - - LTTNG_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); - LTTNG_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, - const struct lttng_trace_chunk_registry_element *b) -{ - if (a->session_id != b->session_id) { - goto not_equal; - } - if (a->chunk.id.is_set != b->chunk.id.is_set) { - goto not_equal; - } - if (a->chunk.id.is_set && a->chunk.id.value != b->chunk.id.value) { - goto not_equal; - } - return true; -not_equal: - return false; -} - -static -int lttng_trace_chunk_registry_element_match(struct cds_lfht_node *node, - const void *key) -{ - const struct lttng_trace_chunk_registry_element *element_a, *element_b; - - element_a = (const struct lttng_trace_chunk_registry_element *) key; - element_b = caa_container_of(node, typeof(*element_b), - trace_chunk_registry_ht_node); - return lttng_trace_chunk_registry_element_equals(element_a, element_b); -} - -static -unsigned long lttng_trace_chunk_registry_element_hash( - const struct lttng_trace_chunk_registry_element *element) -{ - unsigned long hash = hash_key_u64(&element->session_id, - lttng_ht_seed); - - if (element->chunk.id.is_set) { - hash ^= hash_key_u64(&element->chunk.id.value, lttng_ht_seed); - } - - return hash; -} - -static -char *generate_chunk_name(uint64_t chunk_id, time_t creation_timestamp, - const time_t *close_timestamp) -{ - int ret = 0; - char *new_name= NULL; - 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, - start_datetime, sizeof(start_datetime)); - if (ret) { - ERR("Failed to format trace chunk start date time"); - goto error; - } - if (close_timestamp) { - *end_datetime_suffix = '-'; - ret = time_to_iso8601_str( - *close_timestamp, - end_datetime_suffix + 1, - sizeof(end_datetime_suffix) - 1); - if (ret) { - ERR("Failed to format trace chunk end date time"); - goto error; - } - } - new_name = zmalloc(GENERATED_CHUNK_NAME_LEN); - if (!new_name) { - ERR("Failed to allocate buffer for automatically-generated trace chunk name"); - goto error; - } - ret = snprintf(new_name, GENERATED_CHUNK_NAME_LEN, "%s%s-%" PRIu64, - start_datetime, end_datetime_suffix, chunk_id); - if (ret >= GENERATED_CHUNK_NAME_LEN || ret == -1) { - ERR("Failed to format trace chunk name"); - goto error; - } - - return new_name; -error: - free(new_name); - return NULL; -} - -static -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) { - lttng_directory_handle_put( - chunk->session_output_directory); - chunk->session_output_directory = NULL; - } - 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); -} - -static -struct lttng_trace_chunk *lttng_trace_chunk_allocate(void) -{ - struct lttng_trace_chunk *chunk = NULL; - - chunk = zmalloc(sizeof(*chunk)); - if (!chunk) { - ERR("Failed to allocate trace chunk"); - goto end; - } - lttng_trace_chunk_init(chunk); -end: - return chunk; -} - -struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous(void) -{ - DBG("Creating anonymous trace chunk"); - return lttng_trace_chunk_allocate(); -} - -struct lttng_trace_chunk *lttng_trace_chunk_create( - uint64_t chunk_id, time_t chunk_creation_time, const char *path) -{ - struct lttng_trace_chunk *chunk; - char chunk_creation_datetime_buf[16] = {}; - const char *chunk_creation_datetime_str = "(formatting error)"; - struct tm timeinfo_buf, *timeinfo; - - timeinfo = localtime_r(&chunk_creation_time, &timeinfo_buf); - if (timeinfo) { - size_t strftime_ret; - - /* Don't fail because of this; it is only used for logging. */ - strftime_ret = strftime(chunk_creation_datetime_buf, - sizeof(chunk_creation_datetime_buf), - "%Y%m%d-%H%M%S", timeinfo); - if (strftime_ret) { - chunk_creation_datetime_str = - chunk_creation_datetime_buf; - } - } - - DBG("Creating trace chunk: chunk_id = %" PRIu64 ", creation time = %s", - chunk_id, chunk_creation_datetime_str); - chunk = lttng_trace_chunk_allocate(); - if (!chunk) { - goto end; - } - - LTTNG_OPTIONAL_SET(&chunk->id, chunk_id); - LTTNG_OPTIONAL_SET(&chunk->timestamp_creation, chunk_creation_time); - if (chunk_id != 0) { - chunk->name = generate_chunk_name(chunk_id, - chunk_creation_time, NULL); - if (!chunk->name) { - ERR("Failed to allocate trace chunk name storage"); - 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: - return chunk; -error: - lttng_trace_chunk_put(chunk); - return NULL; -} - -void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk *chunk, - struct fd_tracker *fd_tracker) -{ - LTTNG_ASSERT(!chunk->session_output_directory); - LTTNG_ASSERT(!chunk->chunk_directory); - LTTNG_ASSERT(lttng_dynamic_pointer_array_get_count(&chunk->files) == 0); - chunk->fd_tracker = fd_tracker; -} - -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); - - LTTNG_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); - - LTTNG_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; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_get_id( - struct lttng_trace_chunk *chunk, uint64_t *id) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - pthread_mutex_lock(&chunk->lock); - if (chunk->id.is_set) { - *id = chunk->id.value; - } else { - status = LTTNG_TRACE_CHUNK_STATUS_NONE; - } - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_get_creation_timestamp( - struct lttng_trace_chunk *chunk, time_t *creation_ts) - -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - pthread_mutex_lock(&chunk->lock); - if (chunk->timestamp_creation.is_set) { - *creation_ts = chunk->timestamp_creation.value; - } else { - status = LTTNG_TRACE_CHUNK_STATUS_NONE; - } - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_get_close_timestamp( - struct lttng_trace_chunk *chunk, time_t *close_ts) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - pthread_mutex_lock(&chunk->lock); - if (chunk->timestamp_close.is_set) { - *close_ts = chunk->timestamp_close.value; - } else { - status = LTTNG_TRACE_CHUNK_STATUS_NONE; - } - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_set_close_timestamp( - struct lttng_trace_chunk *chunk, time_t close_ts) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - pthread_mutex_lock(&chunk->lock); - if (!chunk->timestamp_creation.is_set) { - ERR("Failed to set trace chunk close timestamp: creation timestamp is unset"); - status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; - goto end; - } - - /* - * Note: we do not enforce that the closing timestamp be greater or - * equal to the begin timestamp. These timestamps are used for - * generating the chunk name and should only be used in context where - * the monotonicity of time is not important. The source of those - * timestamps is NOT monotonic and represent the system calendar time, - * also know as the wall time. - */ - if (chunk->timestamp_creation.value > close_ts) { - WARN("Set trace chunk close timestamp: close timestamp is before creation timestamp, begin : %ld, close : %ld", - chunk->timestamp_creation.value, close_ts); - } - - LTTNG_OPTIONAL_SET(&chunk->timestamp_close, close_ts); - 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); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_get_name( - struct lttng_trace_chunk *chunk, const char **name, - bool *name_overridden) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - pthread_mutex_lock(&chunk->lock); - if (name_overridden) { - *name_overridden = chunk->name_overridden; - } - if (!chunk->name) { - status = LTTNG_TRACE_CHUNK_STATUS_NONE; - goto end; - } - *name = chunk->name; -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - -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; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_override_name( - struct lttng_trace_chunk *chunk, const char *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"); - status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT; - goto end; - } - - pthread_mutex_lock(&chunk->lock); - if (!chunk->id.is_set) { - ERR("Attempted to set an override name on an anonymous trace chunk: name = %s", - 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"); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end_unlock; - } - 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: - 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); - - LTTNG_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; -} - -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; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials( - struct lttng_trace_chunk *chunk, - struct lttng_credentials *credentials) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - pthread_mutex_lock(&chunk->lock); - if (chunk->credentials.is_set) { - if (chunk->credentials.value.use_current_user) { - LTTNG_OPTIONAL_SET(&credentials->uid, geteuid()); - LTTNG_OPTIONAL_SET(&credentials->gid, getegid()); - } else { - *credentials = chunk->credentials.value.user; - } - } else { - status = LTTNG_TRACE_CHUNK_STATUS_NONE; - } - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials( - struct lttng_trace_chunk *chunk, - const struct lttng_credentials *user_credentials) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - const struct chunk_credentials credentials = { - .user = *user_credentials, - .use_current_user = false, - }; - - pthread_mutex_lock(&chunk->lock); - if (chunk->credentials.is_set) { - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - LTTNG_OPTIONAL_SET(&chunk->credentials, credentials); -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials_current_user( - struct lttng_trace_chunk *chunk) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - const struct chunk_credentials credentials = { - .use_current_user = true, - }; - - pthread_mutex_lock(&chunk->lock); - if (chunk->credentials.is_set) { - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - LTTNG_OPTIONAL_SET(&chunk->credentials, credentials); -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - - -enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner( - struct lttng_trace_chunk *chunk, - struct lttng_directory_handle *session_output_directory) -{ - int ret; - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - struct lttng_directory_handle *chunk_directory_handle = NULL; - bool reference_acquired; - - pthread_mutex_lock(&chunk->lock); - if (chunk->mode.is_set) { - status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; - goto end; - } - if (!chunk->credentials.is_set) { - /* - * Fatal error, credentials must be set before a - * directory is created. - */ - ERR("Credentials of trace chunk are unset: refusing to set session output directory"); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - if (chunk->path && chunk->path[0] != '\0') { - ret = lttng_directory_handle_create_subdirectory_as_user( - session_output_directory, - 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->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. - */ - reference_acquired = lttng_directory_handle_get( - session_output_directory); - - LTTNG_ASSERT(reference_acquired); - chunk_directory_handle = session_output_directory; - } - chunk->chunk_directory = chunk_directory_handle; - chunk_directory_handle = NULL; - reference_acquired = lttng_directory_handle_get( - session_output_directory); - LTTNG_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); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user( - struct lttng_trace_chunk *chunk, - 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) { - status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; - goto end; - } - if (!chunk->credentials.is_set) { - ERR("Credentials of trace chunk are unset: refusing to set chunk output directory"); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - reference_acquired = lttng_directory_handle_get(chunk_directory); - LTTNG_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; -} - -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); - - LTTNG_ASSERT(reference_acquired); - *handle = chunk->session_output_directory; - } -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - -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) { - status = LTTNG_TRACE_CHUNK_STATUS_NONE; - goto end; - } - - *handle = chunk->chunk_directory; -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - -/* Add a top-level directory to the trace chunk if it was previously unknown. */ -static -int add_top_level_directory_unique(struct lttng_trace_chunk *chunk, - const char *new_path) -{ - int ret = 0; - bool found = false; - size_t i, count = lttng_dynamic_pointer_array_get_count( - &chunk->top_level_directories); - const char *new_path_separator_pos = strchr(new_path, '/'); - const ptrdiff_t new_path_top_level_len = new_path_separator_pos ? - new_path_separator_pos - new_path : strlen(new_path); - - for (i = 0; i < count; i++) { - const char *path = lttng_dynamic_pointer_array_get_pointer( - &chunk->top_level_directories, i); - const ptrdiff_t path_top_level_len = strlen(path); - - if (path_top_level_len != new_path_top_level_len) { - continue; - } - if (!strncmp(path, new_path, path_top_level_len)) { - found = true; - break; - } - } - - if (!found) { - 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)"); - if (!copy) { - PERROR("Failed to copy path"); - ret = -1; - goto end; - } - ret = lttng_dynamic_pointer_array_add_pointer( - &chunk->top_level_directories, copy); - if (ret) { - ERR("Allocation failure while adding top-level directory entry to a trace chunk"); - free(copy); - goto end; - } - } -end: - return ret; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory( - struct lttng_trace_chunk *chunk, - const char *path) -{ - int ret; - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - DBG("Creating trace chunk subdirectory \"%s\"", path); - pthread_mutex_lock(&chunk->lock); - if (!chunk->credentials.is_set) { - /* - * Fatal error, credentials must be set before a - * directory is created. - */ - ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"", - path); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - if (!chunk->mode.is_set || - chunk->mode.value != TRACE_CHUNK_MODE_OWNER) { - ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk", - path); - status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; - goto end; - } - 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; - goto end; - } - if (*path == '/') { - ERR("Refusing to create absolute trace chunk directory \"%s\"", - path); - status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT; - goto end; - } - ret = lttng_directory_handle_create_subdirectory_recursive_as_user( - chunk->chunk_directory, path, - DIR_CREATION_MODE, - chunk->credentials.value.use_current_user ? - NULL : &chunk->credentials.value.user); - if (ret) { - PERROR("Failed to create trace chunk subdirectory \"%s\"", - path); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - ret = add_top_level_directory_unique(chunk, path); - if (ret) { - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } -end: - pthread_mutex_unlock(&chunk->lock); - 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); - LTTNG_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); - if (!chunk->credentials.is_set) { - /* - * Fatal error, credentials must be set before a - * file is created. - */ - ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"", - file_path); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - 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; - } - if (chunk->fd_tracker) { - LTTNG_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) { - 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; - } - lttng_trace_chunk_remove_file(chunk, file_path); - goto end; - } -end: - return status; -} - -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; -} - -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. - */ - LTTNG_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; -} - -int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk, - const char *file_path) -{ - int ret; - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - DBG("Unlinking trace chunk file \"%s\"", file_path); - pthread_mutex_lock(&chunk->lock); - if (!chunk->credentials.is_set) { - /* - * Fatal error, credentials must be set before a - * 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) { - 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, 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 -int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk *chunk, - const char *path) -{ - int ret; - 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); - 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) { - /* - * 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; - } - - LTTNG_ASSERT(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER); - LTTNG_ASSERT(!trace_chunk->name_overridden); - LTTNG_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, - DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY, - DIR_CREATION_MODE, - !trace_chunk->credentials.value.use_current_user ? - &trace_chunk->credentials.value.user : - NULL); - if (ret) { - PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY - "\" directory for archived trace chunks"); - goto end; - } - - 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; - } - - /* - * 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, - 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\"", - trace_chunk->path, - archived_chunk_name); - } - -end: - lttng_directory_handle_put(archived_chunks_directory); - free(archived_chunk_name); - 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)"); - - LTTNG_ASSERT(trace_chunk->session_output_directory); - LTTNG_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); - } -} - -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; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command( - struct lttng_trace_chunk *chunk, - enum lttng_trace_chunk_command_type close_command) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - 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; - } - - pthread_mutex_lock(&chunk->lock); - if (chunk->close_command.is_set) { - DBG("Overriding trace chunk close command from \"%s\" to \"%s\"", - close_command_names[chunk->close_command.value], - close_command_names[close_command]); - } else { - DBG("Setting trace chunk close command to \"%s\"", - close_command_names[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; -} - -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(); - } -} - -bool lttng_trace_chunk_ids_equal(const struct lttng_trace_chunk *chunk_a, - const struct lttng_trace_chunk *chunk_b) -{ - bool equal = false; - - if (chunk_a == chunk_b) { - equal = true; - goto end; - } - - if (!!chunk_a ^ !!chunk_b) { - goto end; - } - - if (chunk_a->id.is_set ^ chunk_a->id.is_set) { - /* One id is set and not the other, thus they are not equal. */ - goto end; - } - - if (!chunk_a->id.is_set) { - /* Both ids are unset. */ - equal = true; - } else { - equal = chunk_a->id.value == chunk_b->id.value; - } - -end: - return equal; -} - -bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk) -{ - return urcu_ref_get_unless_zero(&chunk->ref); -} - -static -void free_lttng_trace_chunk_registry_element(struct rcu_head *node) -{ - struct lttng_trace_chunk_registry_element *element = - container_of(node, typeof(*element), rcu_node); - - lttng_trace_chunk_fini(&element->chunk); - free(element); -} - -static -void lttng_trace_chunk_release(struct urcu_ref *ref) -{ - struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk), - ref); - - if (chunk->close_command.is_set) { - 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) { - struct lttng_trace_chunk_registry_element *element; - - element = container_of(chunk, typeof(*element), chunk); - if (element->registry) { - rcu_read_lock(); - cds_lfht_del(element->registry->ht, - &element->trace_chunk_registry_ht_node); - rcu_read_unlock(); - call_rcu(&element->rcu_node, - free_lttng_trace_chunk_registry_element); - } else { - /* Never published, can be free'd immediately. */ - free_lttng_trace_chunk_registry_element( - &element->rcu_node); - } - } else { - /* Not RCU-protected, free immediately. */ - lttng_trace_chunk_fini(chunk); - free(chunk); - } -} - -void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk) -{ - if (!chunk) { - return; - } - LTTNG_ASSERT(chunk->ref.refcount); - urcu_ref_put(&chunk->ref, lttng_trace_chunk_release); -} - -struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void) -{ - struct lttng_trace_chunk_registry *registry; - - registry = zmalloc(sizeof(*registry)); - if (!registry) { - goto end; - } - - registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0, - CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); - if (!registry->ht) { - goto error; - } -end: - return registry; -error: - lttng_trace_chunk_registry_destroy(registry); - return NULL; -} - -void lttng_trace_chunk_registry_destroy( - struct lttng_trace_chunk_registry *registry) -{ - if (!registry) { - return; - } - if (registry->ht) { - int ret = cds_lfht_destroy(registry->ht, NULL); - LTTNG_ASSERT(!ret); - } - free(registry); -} - -static -struct lttng_trace_chunk_registry_element * -lttng_trace_chunk_registry_element_create_from_chunk( - struct lttng_trace_chunk *chunk, uint64_t session_id) -{ - struct lttng_trace_chunk_registry_element *element = - zmalloc(sizeof(*element)); - - if (!element) { - goto end; - } - cds_lfht_node_init(&element->trace_chunk_registry_ht_node); - element->session_id = session_id; - - element->chunk = *chunk; - lttng_trace_chunk_init(&element->chunk); - 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) { - /* Transferred ownership. */ - element->chunk.chunk_directory = chunk->chunk_directory; - chunk->chunk_directory = NULL; - } - /* - * 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; -} - -struct lttng_trace_chunk * -lttng_trace_chunk_registry_publish_chunk( - struct lttng_trace_chunk_registry *registry, - uint64_t session_id, struct lttng_trace_chunk *chunk) -{ - struct lttng_trace_chunk_registry_element *element; - unsigned long element_hash; - - pthread_mutex_lock(&chunk->lock); - element = lttng_trace_chunk_registry_element_create_from_chunk(chunk, - session_id); - pthread_mutex_unlock(&chunk->lock); - if (!element) { - goto end; - } - /* - * chunk is now invalid, the only valid operation is a 'put' from the - * caller. - */ - chunk = NULL; - element_hash = lttng_trace_chunk_registry_element_hash(element); - - rcu_read_lock(); - while (1) { - struct cds_lfht_node *published_node; - struct lttng_trace_chunk *published_chunk; - struct lttng_trace_chunk_registry_element *published_element; - - published_node = cds_lfht_add_unique(registry->ht, - element_hash, - lttng_trace_chunk_registry_element_match, - element, - &element->trace_chunk_registry_ht_node); - if (published_node == &element->trace_chunk_registry_ht_node) { - /* Successfully published the new element. */ - element->registry = registry; - /* Acquire a reference for the caller. */ - if (lttng_trace_chunk_get(&element->chunk)) { - break; - } else { - /* - * Another thread concurrently unpublished the - * trace chunk. This is currently unexpected. - * - * Re-attempt to publish. - */ - ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion"); - continue; - } - } - - /* - * An equivalent trace chunk was published before this trace - * chunk. Attempt to acquire a reference to the one that was - * already published and release the reference to the copy we - * created if successful. - */ - published_element = container_of(published_node, - typeof(*published_element), - trace_chunk_registry_ht_node); - published_chunk = &published_element->chunk; - if (lttng_trace_chunk_get(published_chunk)) { - lttng_trace_chunk_put(&element->chunk); - element = published_element; - break; - } - /* - * A reference to the previously published trace chunk could not - * be acquired. Hence, retry to publish our copy of the trace - * chunk. - */ - } - rcu_read_unlock(); -end: - return element ? &element->chunk : NULL; -} - -/* - * Note that the caller must be registered as an RCU thread. - * However, it does not need to hold the RCU read lock. The RCU read lock is - * acquired to perform the look-up in the registry's hash table and held until - * after a reference to the "found" trace chunk is acquired. - * - * IOW, holding a reference guarantees the existence of the object for the - * caller. - */ -static -struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk( - const struct lttng_trace_chunk_registry *registry, - uint64_t session_id, uint64_t *chunk_id) -{ - const struct lttng_trace_chunk_registry_element target_element = { - .chunk.id.is_set = !!chunk_id, - .chunk.id.value = chunk_id ? *chunk_id : 0, - .session_id = session_id, - }; - const unsigned long element_hash = - lttng_trace_chunk_registry_element_hash( - &target_element); - struct cds_lfht_node *published_node; - struct lttng_trace_chunk_registry_element *published_element; - struct lttng_trace_chunk *published_chunk = NULL; - 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) { - goto end; - } - - published_element = container_of(published_node, - typeof(*published_element), - trace_chunk_registry_ht_node); - if (lttng_trace_chunk_get(&published_element->chunk)) { - published_chunk = &published_element->chunk; - } -end: - rcu_read_unlock(); - return published_chunk; -} - -struct lttng_trace_chunk * -lttng_trace_chunk_registry_find_chunk( - const struct lttng_trace_chunk_registry *registry, - uint64_t session_id, uint64_t chunk_id) -{ - return _lttng_trace_chunk_registry_find_chunk(registry, - session_id, &chunk_id); -} - -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; -} - -struct lttng_trace_chunk * -lttng_trace_chunk_registry_find_anonymous_chunk( - const struct lttng_trace_chunk_registry *registry, - uint64_t session_id) -{ - return _lttng_trace_chunk_registry_find_chunk(registry, - session_id, NULL); -} - -unsigned int lttng_trace_chunk_registry_put_each_chunk( - const 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; -}