struct lttng_credentials user;
};
+/* NOTE: Make sure to update lttng_trace_chunk_copy if you modify this. */
struct lttng_trace_chunk {
pthread_mutex_t lock;
struct urcu_ref ref;
/*
* 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;
/* Is contained within an lttng_trace_chunk_registry_element? */
bool in_registry_element;
- bool name_overriden;
+ bool name_overridden;
char *name;
/* An unset id means the chunk is anonymous. */
LTTNG_OPTIONAL(uint64_t) id;
/* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
struct lttng_trace_chunk_registry_element {
- uint64_t session_id;
struct lttng_trace_chunk chunk;
+ uint64_t session_id;
/* Weak and only set when added. */
struct lttng_trace_chunk_registry *registry;
struct cds_lfht_node trace_chunk_registry_ht_node;
struct cds_lfht *ht;
};
-const char *close_command_names[] = {
+static const
+char *close_command_names[] = {
[LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
"move to completed chunk folder",
};
+static const
chunk_close_command close_command_funcs[] = {
[LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
lttng_trace_chunk_move_to_completed,
{
int ret = 0;
char *new_name= NULL;
- char start_datetime[sizeof("YYYYmmddTHHMMSS+HHMM")] = {};
- char end_datetime_suffix[sizeof("-YYYYmmddTHHMMSS+HHMM")] = {};
+ char start_datetime[ISO8601_STR_LEN] = {};
+ /* Add 1 for a '-' prefix. */
+ char end_datetime_suffix[ISO8601_STR_LEN + 1] = {};
ret = time_to_iso8601_str(
creation_timestamp,
ret = time_to_iso8601_str(
*close_timestamp,
end_datetime_suffix + 1,
- sizeof(end_datetime_suffix));
+ sizeof(end_datetime_suffix) - 1);
if (ret) {
ERR("Failed to format trace chunk end date time");
goto error;
{
urcu_ref_init(&chunk->ref);
pthread_mutex_init(&chunk->lock, NULL);
- lttng_dynamic_pointer_array_init(&chunk->top_level_directories);
+ lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free);
}
static
}
free(chunk->name);
chunk->name = NULL;
- lttng_dynamic_pointer_array_reset(&chunk->top_level_directories, free);
+ lttng_dynamic_pointer_array_reset(&chunk->top_level_directories);
pthread_mutex_destroy(&chunk->lock);
}
return NULL;
}
+LTTNG_HIDDEN
+struct lttng_trace_chunk *lttng_trace_chunk_copy(
+ struct lttng_trace_chunk *source_chunk)
+{
+ struct lttng_trace_chunk *new_chunk = lttng_trace_chunk_allocate();
+
+ if (!new_chunk) {
+ goto end;
+ }
+
+ pthread_mutex_lock(&source_chunk->lock);
+ /*
+ * A new chunk is always a user; it shall create no new trace
+ * subdirectories.
+ */
+ new_chunk->mode = (typeof(new_chunk->mode)) {
+ .is_set = true,
+ .value = TRACE_CHUNK_MODE_USER,
+ };
+ /*
+ * top_level_directories is not copied as it is never used
+ * by _user_ mode chunks.
+ */
+ /* The new chunk is not part of a registry (yet, at least). */
+ new_chunk->in_registry_element = false;
+ new_chunk->name_overridden = source_chunk->name_overridden;
+ if (source_chunk->name) {
+ new_chunk->name = strdup(source_chunk->name);
+ if (!new_chunk->name) {
+ ERR("Failed to copy source trace chunk name in %s()",
+ __FUNCTION__);
+ goto error_unlock;
+ }
+ }
+ new_chunk->id = source_chunk->id;
+ new_chunk->timestamp_creation = source_chunk->timestamp_creation;
+ new_chunk->timestamp_close = source_chunk->timestamp_close;
+ new_chunk->credentials = source_chunk->credentials;
+ if (source_chunk->session_output_directory.is_set) {
+ if (lttng_directory_handle_copy(
+ &source_chunk->session_output_directory.value,
+ &new_chunk->session_output_directory.value)) {
+ goto error_unlock;
+ } else {
+ new_chunk->session_output_directory.is_set = true;
+ }
+ }
+ if (source_chunk->chunk_directory.is_set) {
+ if (lttng_directory_handle_copy(
+ &source_chunk->chunk_directory.value,
+ &new_chunk->chunk_directory.value)) {
+ goto error_unlock;
+ } else {
+ new_chunk->chunk_directory.is_set = true;
+ }
+ }
+ new_chunk->close_command = source_chunk->close_command;
+ pthread_mutex_unlock(&source_chunk->lock);
+end:
+ return new_chunk;
+error_unlock:
+ pthread_mutex_unlock(&source_chunk->lock);
+ lttng_trace_chunk_put(new_chunk);
+ return NULL;
+}
+
LTTNG_HIDDEN
enum lttng_trace_chunk_status lttng_trace_chunk_get_id(
struct lttng_trace_chunk *chunk, uint64_t *id)
goto end;
}
LTTNG_OPTIONAL_SET(&chunk->timestamp_close, close_ts);
- free(chunk->name);
- chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id),
- LTTNG_OPTIONAL_GET(chunk->timestamp_creation),
- &close_ts);
- if (!chunk->name) {
- status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+ if (!chunk->name_overridden) {
+ free(chunk->name);
+ chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id),
+ LTTNG_OPTIONAL_GET(chunk->timestamp_creation),
+ &close_ts);
+ if (!chunk->name) {
+ status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+ }
}
end:
pthread_mutex_unlock(&chunk->lock);
LTTNG_HIDDEN
enum lttng_trace_chunk_status lttng_trace_chunk_get_name(
struct lttng_trace_chunk *chunk, const char **name,
- bool *name_overriden)
+ bool *name_overridden)
{
enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
pthread_mutex_lock(&chunk->lock);
- if (name_overriden) {
- *name_overriden = chunk->name_overriden;
+ if (name_overridden) {
+ *name_overridden = chunk->name_overridden;
}
if (!chunk->name) {
status = LTTNG_TRACE_CHUNK_STATUS_NONE;
return status;
}
+static
+bool is_valid_chunk_name(const char *name)
+{
+ size_t len;
+
+ if (!name) {
+ return false;
+ }
+
+ len = lttng_strnlen(name, LTTNG_NAME_MAX);
+ if (len == 0 || len == LTTNG_NAME_MAX) {
+ return false;
+ }
+
+ if (strchr(name, '/') || strchr(name, '.')) {
+ return false;
+ }
+
+ return true;
+}
+
LTTNG_HIDDEN
enum lttng_trace_chunk_status lttng_trace_chunk_override_name(
struct lttng_trace_chunk *chunk, const char *name)
char *new_name;
enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
- if (!name || !*name || strnlen(name, LTTNG_NAME_MAX) == LTTNG_NAME_MAX) {
+ if (!is_valid_chunk_name(name)) {
ERR("Attempted to set an invalid name on a trace chunk: name = %s",
name ? : "NULL");
status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
}
free(chunk->name);
chunk->name = new_name;
- chunk->name_overriden = true;
+ chunk->name_overridden = true;
end_unlock:
pthread_mutex_unlock(&chunk->lock);
end:
}
if (!found) {
- char *copy = strndup(new_path, new_path_top_level_len);
+ char *copy = lttng_strndup(new_path, new_path_top_level_len);
DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
new_path, chunk->name ? : "(unnamed)");
chunk->credentials.value.use_current_user ?
NULL : &chunk->credentials.value.user);
if (ret < 0) {
- ERR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
+ 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;
goto end;
int ret;
char *directory_to_rename = NULL;
bool free_directory_to_rename = false;
- const int session_dirfd =
- trace_chunk->session_output_directory.value.dirfd;
char *archived_chunk_name = NULL;
const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id);
const time_t creation_timestamp =
LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation);
const time_t close_timestamp =
LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close);
- LTTNG_OPTIONAL(struct lttng_directory_handle) archived_chunks_directory;
+ LTTNG_OPTIONAL(struct lttng_directory_handle) archived_chunks_directory = {};
+
+ if (!trace_chunk->mode.is_set ||
+ trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER ||
+ !trace_chunk->session_output_directory.is_set) {
+ /*
+ * This command doesn't need to run if the output is remote
+ * or if the trace chunk is not owned by this process.
+ */
+ goto end;
+ }
- assert(trace_chunk->mode.is_set);
assert(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
- assert(!trace_chunk->name_overriden);
+ assert(!trace_chunk->name_overridden);
/*
* The fist trace chunk of a session is directly output to the
}
for (i = 0; i < count; i++) {
- const int temp_dirfd = temporary_rename_directory.dirfd;
const char *top_level_name =
lttng_dynamic_pointer_array_get_pointer(
&trace_chunk->top_level_directories, i);
- /*
- * FIXME replace renamat() use by directory handle
- * wrapper for non-POSIX 2008 systems.
- */
- ret = renameat(session_dirfd, top_level_name,
- temp_dirfd, top_level_name);
+ ret = lttng_directory_handle_rename_as_user(
+ &trace_chunk->session_output_directory.value,
+ top_level_name,
+ &temporary_rename_directory,
+ top_level_name,
+ LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
+ NULL :
+ &trace_chunk->credentials.value.user);
if (ret) {
PERROR("Failed to move \"%s\" to temporary trace chunk rename directory",
top_level_name);
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;
}
}
archived_chunks_directory.is_set = true;
- /*
- * FIXME replace renamat() use by directory handle
- * wrapper for non-POSIX 2008 systems.
- */
- ret = renameat(session_dirfd, directory_to_rename,
- archived_chunks_directory.value.dirfd,
- archived_chunk_name);
+ ret = lttng_directory_handle_rename_as_user(
+ &trace_chunk->session_output_directory.value,
+ directory_to_rename,
+ &archived_chunks_directory.value,
+ archived_chunk_name,
+ LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
+ NULL :
+ &trace_chunk->credentials.value.user);
if (ret) {
PERROR("Failed to rename folder \"%s\" to \"%s\"",
directory_to_rename, archived_chunk_name);
}
}
+LTTNG_HIDDEN
+enum lttng_trace_chunk_status lttng_trace_chunk_get_close_command(
+ struct lttng_trace_chunk *chunk,
+ enum lttng_trace_chunk_command_type *command_type)
+{
+ enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+ pthread_mutex_lock(&chunk->lock);
+ if (chunk->close_command.is_set) {
+ *command_type = chunk->close_command.value;
+ status = LTTNG_TRACE_CHUNK_STATUS_OK;
+ } else {
+ status = LTTNG_TRACE_CHUNK_STATUS_NONE;
+ }
+ pthread_mutex_unlock(&chunk->lock);
+ return status;
+}
+
LTTNG_HIDDEN
enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
struct lttng_trace_chunk *chunk,
if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
- goto end_unlock;
+ goto end;
}
pthread_mutex_lock(&chunk->lock);
}
LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
pthread_mutex_unlock(&chunk->lock);
-end_unlock:
+end:
return status;
}
+LTTNG_HIDDEN
+const char *lttng_trace_chunk_command_type_get_name(
+ enum lttng_trace_chunk_command_type command)
+{
+ switch (command) {
+ case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
+ return "move to completed trace chunk folder";
+ default:
+ abort();
+ }
+}
+
LTTNG_HIDDEN
bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
{
return registry;
error:
lttng_trace_chunk_registry_destroy(registry);
- goto end;
+ return NULL;
}
LTTNG_HIDDEN
*
* Re-attempt to publish.
*/
- ERR("Attemp to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
+ ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
continue;
}
}
session_id, &chunk_id);
}
+LTTNG_HIDDEN
+int lttng_trace_chunk_registry_chunk_exists(
+ const struct lttng_trace_chunk_registry *registry,
+ uint64_t session_id, uint64_t chunk_id, bool *chunk_exists)
+{
+ int ret = 0;
+ const struct lttng_trace_chunk_registry_element target_element = {
+ .chunk.id.is_set = true,
+ .chunk.id.value = chunk_id,
+ .session_id = session_id,
+ };
+ const unsigned long element_hash =
+ lttng_trace_chunk_registry_element_hash(
+ &target_element);
+ struct cds_lfht_node *published_node;
+ struct cds_lfht_iter iter;
+
+ rcu_read_lock();
+ cds_lfht_lookup(registry->ht,
+ element_hash,
+ lttng_trace_chunk_registry_element_match,
+ &target_element,
+ &iter);
+ published_node = cds_lfht_iter_get_node(&iter);
+ if (!published_node) {
+ *chunk_exists = false;
+ goto end;
+ }
+
+ *chunk_exists = !cds_lfht_is_node_deleted(published_node);
+end:
+ rcu_read_unlock();
+ return ret;
+}
+
LTTNG_HIDDEN
struct lttng_trace_chunk *
lttng_trace_chunk_registry_find_anonymous_chunk(
return _lttng_trace_chunk_registry_find_chunk(registry,
session_id, NULL);
}
+
+unsigned int lttng_trace_chunk_registry_put_each_chunk(
+ struct lttng_trace_chunk_registry *registry)
+{
+ struct cds_lfht_iter iter;
+ struct lttng_trace_chunk_registry_element *chunk_element;
+ unsigned int trace_chunks_left = 0;
+
+ DBG("Releasing trace chunk registry to all trace chunks");
+ rcu_read_lock();
+ cds_lfht_for_each_entry(registry->ht,
+ &iter, chunk_element, trace_chunk_registry_ht_node) {
+ const char *chunk_id_str = "none";
+ char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)];
+
+ pthread_mutex_lock(&chunk_element->chunk.lock);
+ if (chunk_element->chunk.id.is_set) {
+ int fmt_ret;
+
+ fmt_ret = snprintf(chunk_id_buf, sizeof(chunk_id_buf),
+ "%" PRIu64,
+ chunk_element->chunk.id.value);
+ if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) {
+ chunk_id_str = "formatting error";
+ } else {
+ chunk_id_str = chunk_id_buf;
+ }
+ }
+
+ DBG("Releasing reference to trace chunk: session_id = %" PRIu64
+ "chunk_id = %s, name = \"%s\", status = %s",
+ chunk_element->session_id,
+ chunk_id_str,
+ chunk_element->chunk.name ? : "none",
+ chunk_element->chunk.close_command.is_set ?
+ "open" : "closed");
+ pthread_mutex_unlock(&chunk_element->chunk.lock);
+ lttng_trace_chunk_put(&chunk_element->chunk);
+ trace_chunks_left++;
+ }
+ rcu_read_unlock();
+ DBG("Released reference to %u trace chunks in %s()", trace_chunks_left,
+ __FUNCTION__);
+
+ return trace_chunks_left;
+}