/*
- * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
*
- * 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 <lttng/constant.h>
-#include <common/string-utils/format.h>
-#include <common/trace-chunk.h>
-#include <common/trace-chunk-registry.h>
-#include <common/hashtable/utils.h>
-#include <common/hashtable/hashtable.h>
-#include <common/error.h>
-#include <common/utils.h>
-#include <common/time.h>
-#include <common/optional.h>
#include <common/compat/directory-handle.h>
#include <common/credentials.h>
#include <common/defaults.h>
#include <common/dynamic-array.h>
+#include <common/error.h>
+#include <common/fd-tracker/fd-tracker.h>
+#include <common/fd-tracker/utils.h>
+#include <common/fs-handle-internal.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/optional.h>
+#include <common/string-utils/format.h>
+#include <common/time.h>
+#include <common/trace-chunk-registry.h>
+#include <common/trace-chunk.h>
+#include <common/utils.h>
+#include <lttng/constant.h>
-#include <urcu/ref.h>
-#include <urcu/rculfhash.h>
-#include <sys/stat.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdio.h>
+#include <sys/stat.h>
+#include <urcu/rculfhash.h>
+#include <urcu/ref.h>
/*
* Two ISO 8601-compatible timestamps, separated by a hypen, followed an
* 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 if you modify this. */
+/*
+ * NOTE: Make sure to update:
+ * - lttng_trace_chunk_copy(),
+ * - lttng_trace_chunk_registry_element_create_from_chunk()
+ * if you modify this structure.
+ */
struct lttng_trace_chunk {
pthread_mutex_t lock;
struct urcu_ref ref;
* 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 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_close_command close_command_funcs[] = {
+chunk_command close_command_post_release_funcs[] = {
[LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
- lttng_trace_chunk_move_to_completed,
+ lttng_trace_chunk_move_to_completed_post_release,
+ [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,
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
}
if (chunk->chunk_directory) {
lttng_directory_handle_put(chunk->chunk_directory);
- chunk->chunk_directory = NULL;
+ 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);
}
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] = {};
+ char chunk_creation_datetime_buf[16] = {};
const char *chunk_creation_datetime_str = "(formatting error)";
- struct tm timeinfo_buf, *timeinfo;
+ struct tm timeinfo_buf, *timeinfo;
timeinfo = localtime_r(&chunk_creation_time, &timeinfo_buf);
if (timeinfo) {
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)");
+ DBG("Chunk name set to \"%s\"", chunk->name ? : "(none)");
end:
return chunk;
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)
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->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;
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) {
- ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
- status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
- goto end;
+ 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);
enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
pthread_mutex_lock(&chunk->lock);
- if (name_overridden) {
+ if (name_overridden) {
*name_overridden = chunk->name_overridden;
- }
- if (!chunk->name) {
+ }
+ if (!chunk->name) {
status = LTTNG_TRACE_CHUNK_STATUS_NONE;
goto 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)
{
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");
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");
}
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,
pthread_mutex_lock(&chunk->lock);
if (chunk->credentials.is_set) {
if (chunk->credentials.value.use_current_user) {
- credentials->uid = geteuid();
- credentials->gid = getegid();
+ LTTNG_OPTIONAL_SET(&credentials->uid, geteuid());
+ LTTNG_OPTIONAL_SET(&credentials->gid, getegid());
} else {
*credentials = chunk->credentials.value.user;
}
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 = lttng_directory_handle_create_from_handle(
- chunk->name,
- session_output_directory);
- if (!chunk_directory_handle) {
- /* The function already logs on all error paths. */
- status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
- goto end;
+ chunk_directory_handle =
+ 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;
}
chunk->chunk_directory = chunk_directory_handle;
chunk_directory_handle = NULL;
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_borrow_chunk_directory_handle(
struct lttng_trace_chunk *chunk,
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
status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
goto end;
}
- 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);
+ 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) {
- PERROR("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;
+}
+
LTTNG_HIDDEN
int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk,
const char *file_path)
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;
}
+ 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 =
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 ||
assert(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
assert(!trace_chunk->name_overridden);
-
- /*
- * The fist trace chunk of a session is directly output to the
- * session's output folder. In this case, the top level directories
- * must be moved to a temporary folder before that temporary directory
- * is renamed to match the chunk's name.
- */
- if (chunk_id == 0) {
- struct lttng_directory_handle *temporary_rename_directory =
- NULL;
- size_t i, count = lttng_dynamic_pointer_array_get_count(
- &trace_chunk->top_level_directories);
-
- ret = lttng_directory_handle_create_subdirectory_as_user(
- trace_chunk->session_output_directory,
- DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY,
- DIR_CREATION_MODE,
- !trace_chunk->credentials.value.use_current_user ?
- &trace_chunk->credentials.value.user : NULL);
- if (ret) {
- PERROR("Failed to create temporary trace chunk rename directory \"%s\"",
- DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY);
- }
-
- temporary_rename_directory = lttng_directory_handle_create_from_handle(
- DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY,
- trace_chunk->session_output_directory);
- if (!temporary_rename_directory) {
- ERR("Failed to get handle to temporary trace chunk rename directory");
- goto end;
- }
-
- for (i = 0; i < count; i++) {
- const char *top_level_name =
- lttng_dynamic_pointer_array_get_pointer(
- &trace_chunk->top_level_directories, i);
-
- ret = lttng_directory_handle_rename_as_user(
- trace_chunk->session_output_directory,
- top_level_name,
- temporary_rename_directory,
- top_level_name,
- LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
- NULL :
- &trace_chunk->credentials.value.user);
- if (ret) {
- PERROR("Failed to move \"%s\" to temporary trace chunk rename directory",
- top_level_name);
- lttng_directory_handle_put(
- temporary_rename_directory);
- goto end;
- }
- }
- lttng_directory_handle_put(temporary_rename_directory);
- directory_to_rename = DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY;
- free_directory_to_rename = false;
- } else {
- directory_to_rename = generate_chunk_name(chunk_id,
- creation_timestamp, NULL);
- if (!directory_to_rename) {
- ERR("Failed to generate initial trace chunk name while renaming trace chunk");
- goto end;
- }
- free_directory_to_rename = true;
- }
+ assert(trace_chunk->path);
archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp,
&close_timestamp);
if (!archived_chunk_name) {
ERR("Failed to generate archived trace chunk name while renaming trace chunk");
+ ret = -1;
goto end;
}
ret = lttng_directory_handle_create_subdirectory_as_user(
trace_chunk->session_output_directory,
- DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
+ DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
DIR_CREATION_MODE,
!trace_chunk->credentials.value.use_current_user ?
&trace_chunk->credentials.value.user :
goto end;
}
- archived_chunks_directory = lttng_directory_handle_create_from_handle(
- DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
- trace_chunk->session_output_directory);
+ 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,
- directory_to_rename,
+ trace_chunk->path,
archived_chunks_directory,
archived_chunk_name,
LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
&trace_chunk->credentials.value.user);
if (ret) {
PERROR("Failed to rename folder \"%s\" to \"%s\"",
- directory_to_rename, archived_chunk_name);
+ trace_chunk->path,
+ archived_chunk_name);
}
end:
lttng_directory_handle_put(archived_chunks_directory);
free(archived_chunk_name);
- if (free_directory_to_rename) {
- free(directory_to_rename);
+ return ret;
+}
+
+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);
}
}
DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
close_command_names[chunk->close_command.value],
close_command_names[close_command]);
- } else {
+ } else {
DBG("Setting trace chunk close command to \"%s\"",
close_command_names[close_command]);
- }
- LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
+ }
+ /*
+ * Unset close command for no-op for backward compatibility with relayd
+ * 2.11.
+ */
+ if (close_command != LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) {
+ LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
+ } else {
+ LTTNG_OPTIONAL_UNSET(&chunk->close_command);
+ }
pthread_mutex_unlock(&chunk->lock);
end:
return status;
switch (command) {
case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
return "move to completed trace chunk folder";
+ case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
+ return "no operation";
+ case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
+ return "delete";
default:
abort();
}
}
+LTTNG_HIDDEN
+bool lttng_trace_chunk_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;
+}
+
LTTNG_HIDDEN
bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
{
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) {
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;
struct lttng_trace_chunk_registry_element *published_element;
published_node = cds_lfht_add_unique(registry->ht,
- element_hash,
+ element_hash,
lttng_trace_chunk_registry_element_match,
- element,
+ element,
&element->trace_chunk_registry_ht_node);
if (published_node == &element->trace_chunk_registry_ht_node) {
/* Successfully published the new element. */
- element->registry = registry;
+ element->registry = registry;
/* Acquire a reference for the caller. */
if (lttng_trace_chunk_get(&element->chunk)) {
break;
const struct lttng_trace_chunk_registry *registry,
uint64_t session_id, uint64_t chunk_id)
{
- return _lttng_trace_chunk_registry_find_chunk(registry,
+ return _lttng_trace_chunk_registry_find_chunk(registry,
session_id, &chunk_id);
}
const struct lttng_trace_chunk_registry *registry,
uint64_t session_id)
{
- return _lttng_trace_chunk_registry_find_chunk(registry,
+ return _lttng_trace_chunk_registry_find_chunk(registry,
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;