2 * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * SPDX-License-Identifier: LGPL-2.1-only
8 #include <common/compat/directory-handle.hpp>
9 #include <common/credentials.hpp>
10 #include <common/defaults.hpp>
11 #include <common/dynamic-array.hpp>
12 #include <common/error.hpp>
13 #include <common/fd-tracker/fd-tracker.hpp>
14 #include <common/fd-tracker/utils.hpp>
15 #include <common/fs-handle.hpp>
16 #include <common/fs-handle-internal.hpp>
17 #include <common/hashtable/hashtable.hpp>
18 #include <common/hashtable/utils.hpp>
19 #include <common/optional.hpp>
20 #include <common/string-utils/format.hpp>
21 #include <common/time.hpp>
22 #include <common/trace-chunk-registry.hpp>
23 #include <common/trace-chunk.hpp>
24 #include <common/utils.hpp>
25 #include <lttng/constant.h>
31 #include <urcu/rculfhash.h>
35 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
36 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
38 #define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
39 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
41 enum trace_chunk_mode
{
42 TRACE_CHUNK_MODE_USER
,
43 TRACE_CHUNK_MODE_OWNER
,
47 * Callback to invoke on release of a trace chunk. Note that there is no
48 * need to 'lock' the trace chunk during the execution of these callbacks
49 * since only one thread may access a chunk during its destruction (the last
50 * to release its reference to the chunk).
52 typedef int (*chunk_command
)(struct lttng_trace_chunk
*trace_chunk
);
54 /* Move a completed trace chunk to the 'completed' trace archive folder. */
56 int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk
*trace_chunk
);
59 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
);
60 /* Unlink old chunk files. */
62 int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk
*trace_chunk
);
64 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
65 struct lttng_trace_chunk
*chunk
, const char *path
);
67 struct chunk_credentials
{
68 bool use_current_user
;
69 struct lttng_credentials user
;
73 * NOTE: Make sure to update:
74 * - lttng_trace_chunk_copy(),
75 * - lttng_trace_chunk_registry_element_create_from_chunk()
76 * if you modify this structure.
78 struct lttng_trace_chunk
{
81 LTTNG_OPTIONAL(enum trace_chunk_mode
) mode
;
83 * First-level directories created within the trace chunk.
84 * Elements are of type 'char *'.
86 * Only used by _owner_ mode chunks.
88 struct lttng_dynamic_pointer_array top_level_directories
;
90 * All files contained within the trace chunk.
91 * Array of paths (char *).
93 struct lttng_dynamic_pointer_array files
;
94 /* Is contained within an lttng_trace_chunk_registry_element? */
95 bool in_registry_element
;
99 /* An unset id means the chunk is anonymous. */
100 LTTNG_OPTIONAL(uint64_t) id
;
103 * The creation and close timestamps are NOT monotonic.
104 * They must not be used in context were monotonicity is required.
106 LTTNG_OPTIONAL(time_t) timestamp_creation
;
107 LTTNG_OPTIONAL(time_t) timestamp_close
;
109 LTTNG_OPTIONAL(struct chunk_credentials
) credentials
;
110 struct lttng_directory_handle
*session_output_directory
;
111 struct lttng_directory_handle
*chunk_directory
;
112 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type
) close_command
;
114 * fd_tracker instance through which file descriptors should be
117 * An fd_tracker always outlives any trace chunk; there is no
118 * need to perform any reference counting of that object.
120 struct fd_tracker
*fd_tracker
;
123 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
124 struct lttng_trace_chunk_registry_element
{
125 struct lttng_trace_chunk chunk
;
127 /* Weak and only set when added. */
128 struct lttng_trace_chunk_registry
*registry
;
129 struct cds_lfht_node trace_chunk_registry_ht_node
;
130 /* call_rcu delayed reclaim. */
131 struct rcu_head rcu_node
;
134 struct lttng_trace_chunk_registry
{
138 struct fs_handle_untracked
{
139 struct fs_handle parent
;
142 struct lttng_directory_handle
*directory_handle
;
148 int fs_handle_untracked_get_fd(struct fs_handle
*handle
);
150 void fs_handle_untracked_put_fd(struct fs_handle
*handle
);
152 int fs_handle_untracked_unlink(struct fs_handle
*handle
);
154 int fs_handle_untracked_close(struct fs_handle
*handle
);
157 const char *lttng_trace_chunk_command_type_str(
158 lttng_trace_chunk_command_type type
) {
160 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
161 return "move to completed chunk folder";
162 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
163 return "no operation";
164 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
166 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
:
174 chunk_command
close_command_get_post_release_func(
175 lttng_trace_chunk_command_type type
) {
177 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
178 return lttng_trace_chunk_move_to_completed_post_release
;
179 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
180 return lttng_trace_chunk_no_operation
;
181 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
182 return lttng_trace_chunk_delete_post_release
;
183 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
:
191 struct fs_handle
*fs_handle_untracked_create(
192 struct lttng_directory_handle
*directory_handle
,
196 struct fs_handle_untracked
*handle
= NULL
;
197 bool reference_acquired
;
198 char *path_copy
= strdup(path
);
200 LTTNG_ASSERT(fd
>= 0);
202 PERROR("Failed to copy file path while creating untracked filesystem handle");
206 handle
= zmalloc
<fs_handle_untracked
>();
208 PERROR("Failed to allocate untracked filesystem handle");
212 handle
->parent
= (typeof(handle
->parent
)) {
213 .get_fd
= fs_handle_untracked_get_fd
,
214 .put_fd
= fs_handle_untracked_put_fd
,
215 .unlink
= fs_handle_untracked_unlink
,
216 .close
= fs_handle_untracked_close
,
220 reference_acquired
= lttng_directory_handle_get(directory_handle
);
221 LTTNG_ASSERT(reference_acquired
);
222 handle
->location
.directory_handle
= directory_handle
;
223 /* Ownership is transferred. */
224 handle
->location
.path
= path_copy
;
228 return handle
? &handle
->parent
: NULL
;
232 int fs_handle_untracked_get_fd(struct fs_handle
*_handle
)
234 struct fs_handle_untracked
*handle
= container_of(
235 _handle
, struct fs_handle_untracked
, parent
);
241 void fs_handle_untracked_put_fd(struct fs_handle
*_handle
__attribute__((unused
)))
247 int fs_handle_untracked_unlink(struct fs_handle
*_handle
)
249 struct fs_handle_untracked
*handle
= container_of(
250 _handle
, struct fs_handle_untracked
, parent
);
252 return lttng_directory_handle_unlink_file(
253 handle
->location
.directory_handle
,
254 handle
->location
.path
);
258 void fs_handle_untracked_destroy(struct fs_handle_untracked
*handle
)
260 lttng_directory_handle_put(handle
->location
.directory_handle
);
261 free(handle
->location
.path
);
266 int fs_handle_untracked_close(struct fs_handle
*_handle
)
268 struct fs_handle_untracked
*handle
= container_of(
269 _handle
, struct fs_handle_untracked
, parent
);
270 int ret
= close(handle
->fd
);
272 fs_handle_untracked_destroy(handle
);
277 bool lttng_trace_chunk_registry_element_equals(
278 const struct lttng_trace_chunk_registry_element
*a
,
279 const struct lttng_trace_chunk_registry_element
*b
)
281 if (a
->session_id
!= b
->session_id
) {
284 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
287 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
296 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
,
299 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
301 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
302 element_b
= caa_container_of(node
, typeof(*element_b
),
303 trace_chunk_registry_ht_node
);
304 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
308 unsigned long lttng_trace_chunk_registry_element_hash(
309 const struct lttng_trace_chunk_registry_element
*element
)
311 unsigned long hash
= hash_key_u64(&element
->session_id
,
314 if (element
->chunk
.id
.is_set
) {
315 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
322 char *generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
,
323 const time_t *close_timestamp
)
326 char *new_name
= NULL
;
327 char start_datetime
[ISO8601_STR_LEN
] = {};
328 /* Add 1 for a '-' prefix. */
329 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
331 ret
= time_to_iso8601_str(
333 start_datetime
, sizeof(start_datetime
));
335 ERR("Failed to format trace chunk start date time");
338 if (close_timestamp
) {
339 *end_datetime_suffix
= '-';
340 ret
= time_to_iso8601_str(
342 end_datetime_suffix
+ 1,
343 sizeof(end_datetime_suffix
) - 1);
345 ERR("Failed to format trace chunk end date time");
349 new_name
= calloc
<char>(GENERATED_CHUNK_NAME_LEN
);
351 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
354 ret
= snprintf(new_name
, GENERATED_CHUNK_NAME_LEN
, "%s%s-%" PRIu64
,
355 start_datetime
, end_datetime_suffix
, chunk_id
);
356 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
357 ERR("Failed to format trace chunk name");
368 void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
370 urcu_ref_init(&chunk
->ref
);
371 pthread_mutex_init(&chunk
->lock
, NULL
);
372 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
373 lttng_dynamic_pointer_array_init(&chunk
->files
, free
);
377 void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
379 if (chunk
->session_output_directory
) {
380 lttng_directory_handle_put(
381 chunk
->session_output_directory
);
382 chunk
->session_output_directory
= NULL
;
384 if (chunk
->chunk_directory
) {
385 lttng_directory_handle_put(chunk
->chunk_directory
);
386 chunk
->chunk_directory
= NULL
;
392 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
393 lttng_dynamic_pointer_array_reset(&chunk
->files
);
394 pthread_mutex_destroy(&chunk
->lock
);
398 struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
400 struct lttng_trace_chunk
*chunk
= NULL
;
402 chunk
= zmalloc
<lttng_trace_chunk
>();
404 ERR("Failed to allocate trace chunk");
407 lttng_trace_chunk_init(chunk
);
412 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
414 DBG("Creating anonymous trace chunk");
415 return lttng_trace_chunk_allocate();
418 struct lttng_trace_chunk
*lttng_trace_chunk_create(
419 uint64_t chunk_id
, time_t chunk_creation_time
, const char *path
)
421 struct lttng_trace_chunk
*chunk
;
422 char chunk_creation_datetime_buf
[16] = {};
423 const char *chunk_creation_datetime_str
= "(formatting error)";
424 struct tm timeinfo_buf
, *timeinfo
;
426 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
430 /* Don't fail because of this; it is only used for logging. */
431 strftime_ret
= strftime(chunk_creation_datetime_buf
,
432 sizeof(chunk_creation_datetime_buf
),
433 "%Y%m%d-%H%M%S", timeinfo
);
435 chunk_creation_datetime_str
=
436 chunk_creation_datetime_buf
;
440 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
441 chunk_id
, chunk_creation_datetime_str
);
442 chunk
= lttng_trace_chunk_allocate();
447 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
448 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
450 chunk
->name
= generate_chunk_name(chunk_id
,
451 chunk_creation_time
, NULL
);
453 ERR("Failed to allocate trace chunk name storage");
458 chunk
->path
= strdup(path
);
464 chunk
->path
= strdup(chunk
->name
);
471 DBG("Chunk name set to \"%s\"", chunk
->name
? : "(none)");
475 lttng_trace_chunk_put(chunk
);
479 void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk
*chunk
,
480 struct fd_tracker
*fd_tracker
)
482 LTTNG_ASSERT(!chunk
->session_output_directory
);
483 LTTNG_ASSERT(!chunk
->chunk_directory
);
484 LTTNG_ASSERT(lttng_dynamic_pointer_array_get_count(&chunk
->files
) == 0);
485 chunk
->fd_tracker
= fd_tracker
;
488 struct lttng_trace_chunk
*lttng_trace_chunk_copy(
489 struct lttng_trace_chunk
*source_chunk
)
491 struct lttng_trace_chunk
*new_chunk
= lttng_trace_chunk_allocate();
497 pthread_mutex_lock(&source_chunk
->lock
);
499 * A new chunk is always a user; it shall create no new trace
502 new_chunk
->mode
= (typeof(new_chunk
->mode
)) {
504 .value
= TRACE_CHUNK_MODE_USER
,
507 * top_level_directories is not copied as it is never used
508 * by _user_ mode chunks.
510 /* The new chunk is not part of a registry (yet, at least). */
511 new_chunk
->in_registry_element
= false;
512 new_chunk
->name_overridden
= source_chunk
->name_overridden
;
513 if (source_chunk
->name
) {
514 new_chunk
->name
= strdup(source_chunk
->name
);
515 if (!new_chunk
->name
) {
516 ERR("Failed to copy source trace chunk name in %s()",
521 if (source_chunk
->path
) {
522 new_chunk
->path
= strdup(source_chunk
->path
);
523 if (!new_chunk
->path
) {
524 ERR("Failed to copy source trace chunk path in %s()",
528 new_chunk
->id
= source_chunk
->id
;
529 new_chunk
->timestamp_creation
= source_chunk
->timestamp_creation
;
530 new_chunk
->timestamp_close
= source_chunk
->timestamp_close
;
531 new_chunk
->credentials
= source_chunk
->credentials
;
532 if (source_chunk
->session_output_directory
) {
533 const bool reference_acquired
= lttng_directory_handle_get(
534 source_chunk
->session_output_directory
);
536 LTTNG_ASSERT(reference_acquired
);
537 new_chunk
->session_output_directory
=
538 source_chunk
->session_output_directory
;
540 if (source_chunk
->chunk_directory
) {
541 const bool reference_acquired
= lttng_directory_handle_get(
542 source_chunk
->chunk_directory
);
544 LTTNG_ASSERT(reference_acquired
);
545 new_chunk
->chunk_directory
= source_chunk
->chunk_directory
;
547 new_chunk
->close_command
= source_chunk
->close_command
;
548 new_chunk
->fd_tracker
= source_chunk
->fd_tracker
;
549 pthread_mutex_unlock(&source_chunk
->lock
);
553 pthread_mutex_unlock(&source_chunk
->lock
);
554 lttng_trace_chunk_put(new_chunk
);
558 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(
559 struct lttng_trace_chunk
*chunk
, uint64_t *id
)
561 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
563 pthread_mutex_lock(&chunk
->lock
);
564 if (chunk
->id
.is_set
) {
565 *id
= chunk
->id
.value
;
567 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
569 pthread_mutex_unlock(&chunk
->lock
);
573 enum lttng_trace_chunk_status
lttng_trace_chunk_get_creation_timestamp(
574 struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
577 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
579 pthread_mutex_lock(&chunk
->lock
);
580 if (chunk
->timestamp_creation
.is_set
) {
581 *creation_ts
= chunk
->timestamp_creation
.value
;
583 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
585 pthread_mutex_unlock(&chunk
->lock
);
589 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_timestamp(
590 struct lttng_trace_chunk
*chunk
, time_t *close_ts
)
592 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
594 pthread_mutex_lock(&chunk
->lock
);
595 if (chunk
->timestamp_close
.is_set
) {
596 *close_ts
= chunk
->timestamp_close
.value
;
598 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
600 pthread_mutex_unlock(&chunk
->lock
);
604 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(
605 struct lttng_trace_chunk
*chunk
, time_t close_ts
)
607 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
609 pthread_mutex_lock(&chunk
->lock
);
610 if (!chunk
->timestamp_creation
.is_set
) {
611 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
612 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
617 * Note: we do not enforce that the closing timestamp be greater or
618 * equal to the begin timestamp. These timestamps are used for
619 * generating the chunk name and should only be used in context where
620 * the monotonicity of time is not important. The source of those
621 * timestamps is NOT monotonic and represent the system calendar time,
622 * also know as the wall time.
624 if (chunk
->timestamp_creation
.value
> close_ts
) {
625 WARN("Set trace chunk close timestamp: close timestamp is before creation timestamp, begin : %ld, close : %ld",
626 chunk
->timestamp_creation
.value
, close_ts
);
629 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
630 if (!chunk
->name_overridden
) {
632 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
633 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
636 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
640 pthread_mutex_unlock(&chunk
->lock
);
644 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(
645 struct lttng_trace_chunk
*chunk
, const char **name
,
646 bool *name_overridden
)
648 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
650 pthread_mutex_lock(&chunk
->lock
);
651 if (name_overridden
) {
652 *name_overridden
= chunk
->name_overridden
;
655 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
660 pthread_mutex_unlock(&chunk
->lock
);
664 bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk
*chunk
)
666 bool name_overridden
;
668 pthread_mutex_lock(&chunk
->lock
);
669 name_overridden
= chunk
->name_overridden
;
670 pthread_mutex_unlock(&chunk
->lock
);
671 return name_overridden
;
675 bool is_valid_chunk_name(const char *name
)
683 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
684 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
688 if (strchr(name
, '/') || strchr(name
, '.')) {
695 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
696 struct lttng_trace_chunk
*chunk
, const char *name
)
699 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
700 char *new_name
, *new_path
;
702 DBG("Override trace chunk name from %s to %s", chunk
->name
, name
);
703 if (!is_valid_chunk_name(name
)) {
704 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
706 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
710 pthread_mutex_lock(&chunk
->lock
);
711 if (!chunk
->id
.is_set
) {
712 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
714 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
718 new_name
= strdup(name
);
720 ERR("Failed to allocate new trace chunk name");
721 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
725 chunk
->name
= new_name
;
727 new_path
= strdup(name
);
729 ERR("Failed to allocate new trace chunk path");
730 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
734 chunk
->path
= new_path
;
736 chunk
->name_overridden
= true;
738 pthread_mutex_unlock(&chunk
->lock
);
744 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
745 struct lttng_trace_chunk
*chunk
, const char *path
)
748 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
749 struct lttng_directory_handle
*rename_directory
= NULL
;
750 char *new_path
, *old_path
;
753 if (chunk
->name_overridden
) {
754 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
758 old_path
= chunk
->path
;
759 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path
, path
);
761 if ((!old_path
&& !path
) ||
762 (old_path
&& path
&& !strcmp(old_path
, path
))) {
766 * Use chunk name as path if NULL path is specified.
772 /* Renaming from "" to "" is not accepted. */
773 if (path
[0] == '\0' && old_path
[0] == '\0') {
774 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
779 * If a rename is performed on a chunk for which the chunk_directory
780 * is not set (yet), or the session_output_directory is not set
781 * (interacting with a relay daemon), there is no rename to perform.
783 if (!chunk
->chunk_directory
||
784 !chunk
->session_output_directory
) {
788 if (old_path
&& old_path
[0] != '\0' && path
[0] != '\0') {
789 /* Rename chunk directory. */
790 ret
= lttng_directory_handle_rename_as_user(
791 chunk
->session_output_directory
,
793 chunk
->session_output_directory
,
795 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
797 &chunk
->credentials
.value
.user
);
799 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
801 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
804 rename_directory
= chunk
->fd_tracker
?
805 fd_tracker_create_directory_handle_from_handle(
807 chunk
->session_output_directory
,
809 lttng_directory_handle_create_from_handle(
811 chunk
->session_output_directory
);
812 if (!rename_directory
) {
813 ERR("Failed to get handle to trace chunk rename directory");
814 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
818 /* Release old handle. */
819 lttng_directory_handle_put(chunk
->chunk_directory
);
821 * Transfer new handle reference to chunk as the current chunk
824 chunk
->chunk_directory
= rename_directory
;
825 rename_directory
= NULL
;
826 } else if (old_path
&& old_path
[0] == '\0') {
827 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
828 &chunk
->top_level_directories
);
830 ret
= lttng_directory_handle_create_subdirectory_as_user(
831 chunk
->session_output_directory
,
834 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
836 &chunk
->credentials
.value
.user
);
838 PERROR("Failed to create trace chunk rename directory \"%s\"",
840 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
844 rename_directory
= lttng_directory_handle_create_from_handle(
845 path
, chunk
->session_output_directory
);
846 if (!rename_directory
) {
847 ERR("Failed to get handle to trace chunk rename directory");
848 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
852 /* Move toplevel directories. */
853 for (i
= 0; i
< count
; i
++) {
854 const char *top_level_name
=
855 (const char *) lttng_dynamic_pointer_array_get_pointer(
856 &chunk
->top_level_directories
, i
);
858 ret
= lttng_directory_handle_rename_as_user(
859 chunk
->chunk_directory
,
863 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
865 &chunk
->credentials
.value
.user
);
867 PERROR("Failed to move \"%s\" to trace chunk rename directory",
869 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
873 /* Release old handle. */
874 lttng_directory_handle_put(chunk
->chunk_directory
);
876 * Transfer new handle reference to chunk as the current chunk
879 chunk
->chunk_directory
= rename_directory
;
880 rename_directory
= NULL
;
881 } else if (old_path
) {
882 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
883 &chunk
->top_level_directories
);
884 const bool reference_acquired
= lttng_directory_handle_get(
885 chunk
->session_output_directory
);
887 LTTNG_ASSERT(reference_acquired
);
888 rename_directory
= chunk
->session_output_directory
;
890 /* Move toplevel directories. */
891 for (i
= 0; i
< count
; i
++) {
892 const char *top_level_name
=
893 (const char *) lttng_dynamic_pointer_array_get_pointer(
894 &chunk
->top_level_directories
, i
);
896 ret
= lttng_directory_handle_rename_as_user(
897 chunk
->chunk_directory
,
901 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
903 &chunk
->credentials
.value
.user
);
905 PERROR("Failed to move \"%s\" to trace chunk rename directory",
907 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
911 /* Release old handle. */
912 lttng_directory_handle_put(chunk
->chunk_directory
);
914 * Transfer new handle reference to chunk as the current chunk
917 chunk
->chunk_directory
= rename_directory
;
918 rename_directory
= NULL
;
920 /* Remove old directory. */
921 status
= (lttng_trace_chunk_status
) lttng_directory_handle_remove_subdirectory(
922 chunk
->session_output_directory
,
924 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
925 ERR("Error removing subdirectory '%s' file when deleting chunk",
930 /* Unexpected !old_path && !path. */
931 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
936 new_path
= strdup(path
);
938 ERR("Failed to allocate new trace chunk path");
939 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
943 chunk
->path
= new_path
;
945 lttng_directory_handle_put(rename_directory
);
949 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path(
950 struct lttng_trace_chunk
*chunk
, const char *path
)
953 enum lttng_trace_chunk_status status
;
955 pthread_mutex_lock(&chunk
->lock
);
956 status
= lttng_trace_chunk_rename_path_no_lock(chunk
, path
);
957 pthread_mutex_unlock(&chunk
->lock
);
962 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
963 struct lttng_trace_chunk
*chunk
,
964 struct lttng_credentials
*credentials
)
966 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
968 pthread_mutex_lock(&chunk
->lock
);
969 if (chunk
->credentials
.is_set
) {
970 if (chunk
->credentials
.value
.use_current_user
) {
971 LTTNG_OPTIONAL_SET(&credentials
->uid
, geteuid());
972 LTTNG_OPTIONAL_SET(&credentials
->gid
, getegid());
974 *credentials
= chunk
->credentials
.value
.user
;
977 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
979 pthread_mutex_unlock(&chunk
->lock
);
983 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
984 struct lttng_trace_chunk
*chunk
,
985 const struct lttng_credentials
*user_credentials
)
987 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
988 const struct chunk_credentials credentials
= {
989 .use_current_user
= false,
990 .user
= *user_credentials
,
993 pthread_mutex_lock(&chunk
->lock
);
994 if (chunk
->credentials
.is_set
) {
995 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
998 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
1000 pthread_mutex_unlock(&chunk
->lock
);
1004 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
1005 struct lttng_trace_chunk
*chunk
)
1007 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1008 const struct chunk_credentials credentials
= {
1009 .use_current_user
= true,
1013 pthread_mutex_lock(&chunk
->lock
);
1014 if (chunk
->credentials
.is_set
) {
1015 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1018 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
1020 pthread_mutex_unlock(&chunk
->lock
);
1025 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
1026 struct lttng_trace_chunk
*chunk
,
1027 struct lttng_directory_handle
*session_output_directory
)
1030 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1031 struct lttng_directory_handle
*chunk_directory_handle
= NULL
;
1032 bool reference_acquired
;
1034 pthread_mutex_lock(&chunk
->lock
);
1035 if (chunk
->mode
.is_set
) {
1036 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1039 if (!chunk
->credentials
.is_set
) {
1041 * Fatal error, credentials must be set before a
1042 * directory is created.
1044 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
1045 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1048 if (chunk
->path
&& chunk
->path
[0] != '\0') {
1049 ret
= lttng_directory_handle_create_subdirectory_as_user(
1050 session_output_directory
,
1053 !chunk
->credentials
.value
.use_current_user
?
1054 &chunk
->credentials
.value
.user
: NULL
);
1056 PERROR("Failed to create chunk output directory \"%s\"",
1058 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1061 chunk_directory_handle
=
1063 fd_tracker_create_directory_handle_from_handle(
1065 session_output_directory
,
1067 lttng_directory_handle_create_from_handle(
1069 session_output_directory
);
1070 if (!chunk_directory_handle
) {
1071 /* The function already logs on all error paths. */
1072 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1077 * A nameless chunk does not need its own output directory.
1078 * The session's output directory will be used.
1080 reference_acquired
= lttng_directory_handle_get(
1081 session_output_directory
);
1083 LTTNG_ASSERT(reference_acquired
);
1084 chunk_directory_handle
= session_output_directory
;
1086 chunk
->chunk_directory
= chunk_directory_handle
;
1087 chunk_directory_handle
= NULL
;
1088 reference_acquired
= lttng_directory_handle_get(
1089 session_output_directory
);
1090 LTTNG_ASSERT(reference_acquired
);
1091 chunk
->session_output_directory
= session_output_directory
;
1092 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
1094 pthread_mutex_unlock(&chunk
->lock
);
1098 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
1099 struct lttng_trace_chunk
*chunk
,
1100 struct lttng_directory_handle
*chunk_directory
)
1102 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1103 bool reference_acquired
;
1105 pthread_mutex_lock(&chunk
->lock
);
1106 if (chunk
->mode
.is_set
) {
1107 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1110 if (!chunk
->credentials
.is_set
) {
1111 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
1112 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1115 reference_acquired
= lttng_directory_handle_get(chunk_directory
);
1116 LTTNG_ASSERT(reference_acquired
);
1117 chunk
->chunk_directory
= chunk_directory
;
1118 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
1120 pthread_mutex_unlock(&chunk
->lock
);
1124 enum lttng_trace_chunk_status
1125 lttng_trace_chunk_get_session_output_directory_handle(
1126 struct lttng_trace_chunk
*chunk
,
1127 struct lttng_directory_handle
**handle
)
1129 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1131 pthread_mutex_lock(&chunk
->lock
);
1132 if (!chunk
->session_output_directory
) {
1133 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1137 const bool reference_acquired
= lttng_directory_handle_get(
1138 chunk
->session_output_directory
);
1140 LTTNG_ASSERT(reference_acquired
);
1141 *handle
= chunk
->session_output_directory
;
1144 pthread_mutex_unlock(&chunk
->lock
);
1148 enum lttng_trace_chunk_status
lttng_trace_chunk_borrow_chunk_directory_handle(
1149 struct lttng_trace_chunk
*chunk
,
1150 const struct lttng_directory_handle
**handle
)
1152 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1154 pthread_mutex_lock(&chunk
->lock
);
1155 if (!chunk
->chunk_directory
) {
1156 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1160 *handle
= chunk
->chunk_directory
;
1162 pthread_mutex_unlock(&chunk
->lock
);
1166 /* Add a top-level directory to the trace chunk if it was previously unknown. */
1168 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
1169 const char *new_path
)
1173 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
1174 &chunk
->top_level_directories
);
1175 const char *new_path_separator_pos
= strchr(new_path
, '/');
1176 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
1177 new_path_separator_pos
- new_path
: strlen(new_path
);
1179 for (i
= 0; i
< count
; i
++) {
1180 const char *path
= (const char *) lttng_dynamic_pointer_array_get_pointer(
1181 &chunk
->top_level_directories
, i
);
1182 const ptrdiff_t path_top_level_len
= strlen(path
);
1184 if (path_top_level_len
!= new_path_top_level_len
) {
1187 if (!strncmp(path
, new_path
, path_top_level_len
)) {
1194 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
1196 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1197 new_path
, chunk
->name
? : "(unnamed)");
1199 PERROR("Failed to copy path");
1203 ret
= lttng_dynamic_pointer_array_add_pointer(
1204 &chunk
->top_level_directories
, copy
);
1206 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1215 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
1216 struct lttng_trace_chunk
*chunk
,
1220 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1222 DBG("Creating trace chunk subdirectory \"%s\"", path
);
1223 pthread_mutex_lock(&chunk
->lock
);
1224 if (!chunk
->credentials
.is_set
) {
1226 * Fatal error, credentials must be set before a
1227 * directory is created.
1229 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1231 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1234 if (!chunk
->mode
.is_set
||
1235 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
1236 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1238 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1241 if (!chunk
->chunk_directory
) {
1242 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1244 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1248 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1250 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1253 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
1254 chunk
->chunk_directory
, path
,
1256 chunk
->credentials
.value
.use_current_user
?
1257 NULL
: &chunk
->credentials
.value
.user
);
1259 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1261 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1264 ret
= add_top_level_directory_unique(chunk
, path
);
1266 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1270 pthread_mutex_unlock(&chunk
->lock
);
1275 * TODO: Implement O(1) lookup.
1278 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk
*chunk
,
1279 const char *path
, size_t *index
)
1283 count
= lttng_dynamic_pointer_array_get_count(&chunk
->files
);
1284 for (i
= 0; i
< count
; i
++) {
1285 const char *iter_path
=
1286 (const char *) lttng_dynamic_pointer_array_get_pointer(
1288 if (!strcmp(iter_path
, path
)) {
1299 enum lttng_trace_chunk_status
lttng_trace_chunk_add_file(
1300 struct lttng_trace_chunk
*chunk
,
1305 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1307 if (lttng_trace_chunk_find_file(chunk
, path
, NULL
)) {
1308 return LTTNG_TRACE_CHUNK_STATUS_OK
;
1310 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1311 path
, chunk
->name
? : "(unnamed)");
1312 copy
= strdup(path
);
1314 PERROR("Failed to copy path");
1315 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1318 ret
= lttng_dynamic_pointer_array_add_pointer(
1319 &chunk
->files
, copy
);
1321 ERR("Allocation failure while adding file to a trace chunk");
1323 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1331 void lttng_trace_chunk_remove_file(
1332 struct lttng_trace_chunk
*chunk
,
1339 found
= lttng_trace_chunk_find_file(chunk
, path
, &index
);
1343 ret
= lttng_dynamic_pointer_array_remove_pointer(
1344 &chunk
->files
, index
);
1349 enum lttng_trace_chunk_status
_lttng_trace_chunk_open_fs_handle_locked(
1350 struct lttng_trace_chunk
*chunk
,
1351 const char *file_path
,
1354 struct fs_handle
**out_handle
,
1355 bool expect_no_file
)
1358 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1360 DBG("Opening trace chunk file \"%s\"", file_path
);
1361 if (!chunk
->credentials
.is_set
) {
1363 * Fatal error, credentials must be set before a
1366 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1368 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1371 if (!chunk
->chunk_directory
) {
1372 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1374 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1377 status
= lttng_trace_chunk_add_file(chunk
, file_path
);
1378 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1381 if (chunk
->fd_tracker
) {
1382 LTTNG_ASSERT(chunk
->credentials
.value
.use_current_user
);
1383 *out_handle
= fd_tracker_open_fs_handle(chunk
->fd_tracker
,
1384 chunk
->chunk_directory
, file_path
, flags
, &mode
);
1385 ret
= *out_handle
? 0 : -1;
1387 ret
= lttng_directory_handle_open_file_as_user(
1388 chunk
->chunk_directory
, file_path
, flags
, mode
,
1389 chunk
->credentials
.value
.use_current_user
?
1391 &chunk
->credentials
.value
.user
);
1393 *out_handle
= fs_handle_untracked_create(
1394 chunk
->chunk_directory
, file_path
, ret
);
1396 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1402 if (errno
== ENOENT
&& expect_no_file
) {
1403 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
1405 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1406 file_path
, flags
, (int) mode
);
1407 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1409 lttng_trace_chunk_remove_file(chunk
, file_path
);
1416 enum lttng_trace_chunk_status
lttng_trace_chunk_open_fs_handle(
1417 struct lttng_trace_chunk
*chunk
,
1418 const char *file_path
,
1421 struct fs_handle
**out_handle
,
1422 bool expect_no_file
)
1424 enum lttng_trace_chunk_status status
;
1426 pthread_mutex_lock(&chunk
->lock
);
1427 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1428 flags
, mode
, out_handle
, expect_no_file
);
1429 pthread_mutex_unlock(&chunk
->lock
);
1433 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
1434 struct lttng_trace_chunk
*chunk
,
1435 const char *file_path
,
1439 bool expect_no_file
)
1441 enum lttng_trace_chunk_status status
;
1442 struct fs_handle
*fs_handle
;
1444 pthread_mutex_lock(&chunk
->lock
);
1446 * Using this method is never valid when an fd_tracker is being
1447 * used since the resulting file descriptor would not be tracked.
1449 LTTNG_ASSERT(!chunk
->fd_tracker
);
1450 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1451 flags
, mode
, &fs_handle
, expect_no_file
);
1452 pthread_mutex_unlock(&chunk
->lock
);
1454 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
1455 *out_fd
= fs_handle_get_fd(fs_handle
);
1457 * Does not close the fd; we just "unbox" it from the fs_handle.
1459 fs_handle_untracked_destroy(container_of(
1460 fs_handle
, struct fs_handle_untracked
, parent
));
1466 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
1467 const char *file_path
)
1470 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1472 DBG("Unlinking trace chunk file \"%s\"", file_path
);
1473 pthread_mutex_lock(&chunk
->lock
);
1474 if (!chunk
->credentials
.is_set
) {
1476 * Fatal error, credentials must be set before a
1479 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1481 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1484 if (!chunk
->chunk_directory
) {
1485 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1487 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1490 ret
= lttng_directory_handle_unlink_file_as_user(
1491 chunk
->chunk_directory
, file_path
,
1492 chunk
->credentials
.value
.use_current_user
?
1493 NULL
: &chunk
->credentials
.value
.user
);
1495 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1498 lttng_trace_chunk_remove_file(chunk
, file_path
);
1500 pthread_mutex_unlock(&chunk
->lock
);
1505 int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk
*chunk
,
1509 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1511 DBG("Recursively removing trace chunk directory \"%s\"", path
);
1512 pthread_mutex_lock(&chunk
->lock
);
1513 if (!chunk
->credentials
.is_set
) {
1515 * Fatal error, credentials must be set before a
1516 * directory is removed.
1518 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1520 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1523 if (!chunk
->chunk_directory
) {
1524 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1526 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1529 ret
= lttng_directory_handle_remove_subdirectory_recursive_as_user(
1530 chunk
->chunk_directory
, path
,
1531 chunk
->credentials
.value
.use_current_user
?
1532 NULL
: &chunk
->credentials
.value
.user
,
1533 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
);
1535 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1539 pthread_mutex_unlock(&chunk
->lock
);
1544 int lttng_trace_chunk_move_to_completed_post_release(
1545 struct lttng_trace_chunk
*trace_chunk
)
1548 char *archived_chunk_name
= NULL
;
1549 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
1550 const time_t creation_timestamp
=
1551 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
1552 const time_t close_timestamp
=
1553 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
1554 struct lttng_directory_handle
*archived_chunks_directory
= NULL
;
1555 enum lttng_trace_chunk_status status
;
1557 if (!trace_chunk
->mode
.is_set
||
1558 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
1559 !trace_chunk
->session_output_directory
) {
1561 * This command doesn't need to run if the output is remote
1562 * or if the trace chunk is not owned by this process.
1567 LTTNG_ASSERT(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
1568 LTTNG_ASSERT(!trace_chunk
->name_overridden
);
1569 LTTNG_ASSERT(trace_chunk
->path
);
1571 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
1573 if (!archived_chunk_name
) {
1574 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1579 ret
= lttng_directory_handle_create_subdirectory_as_user(
1580 trace_chunk
->session_output_directory
,
1581 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1583 !trace_chunk
->credentials
.value
.use_current_user
?
1584 &trace_chunk
->credentials
.value
.user
:
1587 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1588 "\" directory for archived trace chunks");
1592 archived_chunks_directory
= trace_chunk
->fd_tracker
?
1593 fd_tracker_create_directory_handle_from_handle(
1594 trace_chunk
->fd_tracker
,
1595 trace_chunk
->session_output_directory
,
1596 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
) :
1597 lttng_directory_handle_create_from_handle(
1598 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1599 trace_chunk
->session_output_directory
);
1600 if (!archived_chunks_directory
) {
1601 PERROR("Failed to get handle to archived trace chunks directory");
1607 * Make sure chunk is renamed to old directory if not already done by
1608 * the creation of the next chunk. This happens if a rotation is
1609 * performed while tracing is stopped.
1611 if (!trace_chunk
->path
|| strcmp(trace_chunk
->path
,
1612 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
)) {
1613 status
= lttng_trace_chunk_rename_path_no_lock(trace_chunk
,
1614 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1615 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1616 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1622 ret
= lttng_directory_handle_rename_as_user(
1623 trace_chunk
->session_output_directory
,
1625 archived_chunks_directory
,
1626 archived_chunk_name
,
1627 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1629 &trace_chunk
->credentials
.value
.user
);
1631 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1633 archived_chunk_name
);
1637 lttng_directory_handle_put(archived_chunks_directory
);
1638 free(archived_chunk_name
);
1643 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
__attribute__((unused
)))
1649 int lttng_trace_chunk_delete_post_release_user(
1650 struct lttng_trace_chunk
*trace_chunk
)
1654 DBG("Trace chunk \"delete\" close command post-release (User)");
1656 /* Unlink all files. */
1657 while (lttng_dynamic_pointer_array_get_count(&trace_chunk
->files
) != 0) {
1658 enum lttng_trace_chunk_status status
;
1662 path
= (const char *) lttng_dynamic_pointer_array_get_pointer(
1663 &trace_chunk
->files
, 0);
1664 DBG("Unlink file: %s", path
);
1665 status
= (lttng_trace_chunk_status
) lttng_trace_chunk_unlink_file(trace_chunk
, path
);
1666 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1667 ERR("Error unlinking file '%s' when deleting chunk", path
);
1677 int lttng_trace_chunk_delete_post_release_owner(
1678 struct lttng_trace_chunk
*trace_chunk
)
1680 enum lttng_trace_chunk_status status
;
1684 ret
= lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1689 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1691 LTTNG_ASSERT(trace_chunk
->session_output_directory
);
1692 LTTNG_ASSERT(trace_chunk
->chunk_directory
);
1694 /* Remove empty directories. */
1695 count
= lttng_dynamic_pointer_array_get_count(
1696 &trace_chunk
->top_level_directories
);
1698 for (i
= 0; i
< count
; i
++) {
1699 const char *top_level_name
=
1700 (const char *) lttng_dynamic_pointer_array_get_pointer(
1701 &trace_chunk
->top_level_directories
, i
);
1703 status
= (lttng_trace_chunk_status
) lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk
, top_level_name
);
1704 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1705 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1712 lttng_directory_handle_put(trace_chunk
->chunk_directory
);
1713 trace_chunk
->chunk_directory
= NULL
;
1715 if (trace_chunk
->path
&& trace_chunk
->path
[0] != '\0') {
1716 status
= (lttng_trace_chunk_status
) lttng_directory_handle_remove_subdirectory(
1717 trace_chunk
->session_output_directory
,
1719 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1720 ERR("Error removing subdirectory '%s' file when deleting chunk",
1726 free(trace_chunk
->path
);
1727 trace_chunk
->path
= NULL
;
1733 * For local files, session and consumer daemons all run the delete hook. The
1734 * consumer daemons have the list of files to unlink, and technically the
1735 * session daemon is the owner of the chunk. Unlink all files owned by each
1739 int lttng_trace_chunk_delete_post_release(
1740 struct lttng_trace_chunk
*trace_chunk
)
1742 if (!trace_chunk
->chunk_directory
) {
1746 if (trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
) {
1747 return lttng_trace_chunk_delete_post_release_owner(trace_chunk
);
1749 return lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1753 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
1754 struct lttng_trace_chunk
*chunk
,
1755 enum lttng_trace_chunk_command_type
*command_type
)
1757 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1759 pthread_mutex_lock(&chunk
->lock
);
1760 if (chunk
->close_command
.is_set
) {
1761 *command_type
= chunk
->close_command
.value
;
1762 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1764 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1766 pthread_mutex_unlock(&chunk
->lock
);
1770 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
1771 struct lttng_trace_chunk
*chunk
,
1772 enum lttng_trace_chunk_command_type close_command
)
1774 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1776 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1777 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1778 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1782 pthread_mutex_lock(&chunk
->lock
);
1783 if (chunk
->close_command
.is_set
) {
1784 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1785 lttng_trace_chunk_command_type_str(chunk
->close_command
.value
),
1786 lttng_trace_chunk_command_type_str(close_command
));
1788 DBG("Setting trace chunk close command to \"%s\"",
1789 lttng_trace_chunk_command_type_str(close_command
));
1792 * Unset close command for no-op for backward compatibility with relayd
1795 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1796 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1798 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1800 pthread_mutex_unlock(&chunk
->lock
);
1805 const char *lttng_trace_chunk_command_type_get_name(
1806 enum lttng_trace_chunk_command_type command
)
1809 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1810 return "move to completed trace chunk folder";
1811 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1812 return "no operation";
1813 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1820 bool lttng_trace_chunk_ids_equal(const struct lttng_trace_chunk
*chunk_a
,
1821 const struct lttng_trace_chunk
*chunk_b
)
1825 if (chunk_a
== chunk_b
) {
1830 if (!!chunk_a
^ !!chunk_b
) {
1834 if (chunk_a
->id
.is_set
^ chunk_a
->id
.is_set
) {
1835 /* One id is set and not the other, thus they are not equal. */
1839 if (!chunk_a
->id
.is_set
) {
1840 /* Both ids are unset. */
1843 equal
= chunk_a
->id
.value
== chunk_b
->id
.value
;
1850 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1852 return urcu_ref_get_unless_zero(&chunk
->ref
);
1856 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1858 struct lttng_trace_chunk_registry_element
*element
=
1859 container_of(node
, typeof(*element
), rcu_node
);
1865 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1867 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1870 if (chunk
->close_command
.is_set
) {
1871 chunk_command func
= close_command_get_post_release_func(chunk
->close_command
.value
);
1874 ERR("Trace chunk post-release command %s has failed.",
1875 lttng_trace_chunk_command_type_str(chunk
->close_command
.value
));
1879 if (chunk
->in_registry_element
) {
1880 struct lttng_trace_chunk_registry_element
*element
;
1883 * Release internal chunk attributes immediately and
1884 * only use the deferred `call_rcu` work to reclaim the
1887 * This ensures that file handles are released as soon as
1888 * possible which works around a problem we encounter with PRAM fs
1889 * mounts (and possibly other non-POSIX compliant file systems):
1890 * directories that contain files which are open can't be
1893 * This means that the recording of a snapshot could be
1894 * completed, but that it would be impossible for the user to
1895 * delete it until the deferred clean-up released the file
1896 * handles to its contents.
1898 lttng_trace_chunk_fini(chunk
);
1900 element
= container_of(chunk
, typeof(*element
), chunk
);
1901 if (element
->registry
) {
1903 cds_lfht_del(element
->registry
->ht
,
1904 &element
->trace_chunk_registry_ht_node
);
1906 call_rcu(&element
->rcu_node
,
1907 free_lttng_trace_chunk_registry_element
);
1909 /* Never published, can be free'd immediately. */
1910 free_lttng_trace_chunk_registry_element(
1911 &element
->rcu_node
);
1914 /* Not RCU-protected, free immediately. */
1915 lttng_trace_chunk_fini(chunk
);
1920 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1925 LTTNG_ASSERT(chunk
->ref
.refcount
);
1926 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1929 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1931 struct lttng_trace_chunk_registry
*registry
;
1933 registry
= zmalloc
<lttng_trace_chunk_registry
>();
1938 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1939 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1940 if (!registry
->ht
) {
1946 lttng_trace_chunk_registry_destroy(registry
);
1950 void lttng_trace_chunk_registry_destroy(
1951 struct lttng_trace_chunk_registry
*registry
)
1957 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1964 struct lttng_trace_chunk_registry_element
*
1965 lttng_trace_chunk_registry_element_create_from_chunk(
1966 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1968 struct lttng_trace_chunk_registry_element
*element
=
1969 zmalloc
<lttng_trace_chunk_registry_element
>();
1974 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1975 element
->session_id
= session_id
;
1977 element
->chunk
= *chunk
;
1978 lttng_trace_chunk_init(&element
->chunk
);
1979 if (chunk
->session_output_directory
) {
1980 /* Transferred ownership. */
1981 element
->chunk
.session_output_directory
=
1982 chunk
->session_output_directory
;
1983 chunk
->session_output_directory
= NULL
;
1985 if (chunk
->chunk_directory
) {
1986 /* Transferred ownership. */
1987 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1988 chunk
->chunk_directory
= NULL
;
1991 * The original chunk becomes invalid; the name and path attributes are
1992 * transferred to the new chunk instance.
1996 element
->chunk
.fd_tracker
= chunk
->fd_tracker
;
1997 element
->chunk
.in_registry_element
= true;
2002 struct lttng_trace_chunk
*
2003 lttng_trace_chunk_registry_publish_chunk(
2004 struct lttng_trace_chunk_registry
*registry
,
2005 uint64_t session_id
,
2006 struct lttng_trace_chunk
*chunk
)
2010 return lttng_trace_chunk_registry_publish_chunk(
2011 registry
, session_id
, chunk
, &unused
);
2014 struct lttng_trace_chunk
*
2015 lttng_trace_chunk_registry_publish_chunk(
2016 struct lttng_trace_chunk_registry
*registry
,
2017 uint64_t session_id
, struct lttng_trace_chunk
*chunk
,
2018 bool *previously_published
)
2020 struct lttng_trace_chunk_registry_element
*element
;
2021 unsigned long element_hash
;
2023 pthread_mutex_lock(&chunk
->lock
);
2024 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
2026 pthread_mutex_unlock(&chunk
->lock
);
2031 * chunk is now invalid, the only valid operation is a 'put' from the
2035 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
2039 struct cds_lfht_node
*published_node
;
2040 struct lttng_trace_chunk
*published_chunk
;
2041 struct lttng_trace_chunk_registry_element
*published_element
;
2043 published_node
= cds_lfht_add_unique(registry
->ht
,
2045 lttng_trace_chunk_registry_element_match
,
2047 &element
->trace_chunk_registry_ht_node
);
2048 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
2049 /* Successfully published the new element. */
2050 element
->registry
= registry
;
2051 /* Acquire a reference for the caller. */
2052 if (lttng_trace_chunk_get(&element
->chunk
)) {
2053 *previously_published
= false;
2057 * Another thread concurrently unpublished the
2058 * trace chunk. This is currently unexpected.
2060 * Re-attempt to publish.
2062 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
2068 * An equivalent trace chunk was published before this trace
2069 * chunk. Attempt to acquire a reference to the one that was
2070 * already published and release the reference to the copy we
2071 * created if successful.
2073 published_element
= container_of(published_node
,
2074 typeof(*published_element
),
2075 trace_chunk_registry_ht_node
);
2076 published_chunk
= &published_element
->chunk
;
2077 if (lttng_trace_chunk_get(published_chunk
)) {
2078 lttng_trace_chunk_put(&element
->chunk
);
2079 element
= published_element
;
2080 *previously_published
= true;
2084 * A reference to the previously published trace chunk could not
2085 * be acquired. Hence, retry to publish our copy of the trace
2091 return element
? &element
->chunk
: NULL
;
2095 * Note that the caller must be registered as an RCU thread.
2096 * However, it does not need to hold the RCU read lock. The RCU read lock is
2097 * acquired to perform the look-up in the registry's hash table and held until
2098 * after a reference to the "found" trace chunk is acquired.
2100 * IOW, holding a reference guarantees the existence of the object for the
2104 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
2105 const struct lttng_trace_chunk_registry
*registry
,
2106 uint64_t session_id
, uint64_t *chunk_id
)
2108 lttng_trace_chunk_registry_element target_element
{};
2110 target_element
.chunk
.id
.is_set
= !!chunk_id
;
2111 target_element
.chunk
.id
.value
= chunk_id
? *chunk_id
: 0;
2112 target_element
.session_id
= session_id
;
2114 const unsigned long element_hash
=
2115 lttng_trace_chunk_registry_element_hash(
2117 struct cds_lfht_node
*published_node
;
2118 struct lttng_trace_chunk_registry_element
*published_element
;
2119 struct lttng_trace_chunk
*published_chunk
= NULL
;
2120 struct cds_lfht_iter iter
;
2123 cds_lfht_lookup(registry
->ht
,
2125 lttng_trace_chunk_registry_element_match
,
2128 published_node
= cds_lfht_iter_get_node(&iter
);
2129 if (!published_node
) {
2133 published_element
= container_of(published_node
,
2134 typeof(*published_element
),
2135 trace_chunk_registry_ht_node
);
2136 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
2137 published_chunk
= &published_element
->chunk
;
2141 return published_chunk
;
2144 struct lttng_trace_chunk
*
2145 lttng_trace_chunk_registry_find_chunk(
2146 const struct lttng_trace_chunk_registry
*registry
,
2147 uint64_t session_id
, uint64_t chunk_id
)
2149 return _lttng_trace_chunk_registry_find_chunk(registry
,
2150 session_id
, &chunk_id
);
2153 int lttng_trace_chunk_registry_chunk_exists(
2154 const struct lttng_trace_chunk_registry
*registry
,
2155 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
2158 lttng_trace_chunk_registry_element target_element
;
2160 target_element
.chunk
.id
.is_set
= true;
2161 target_element
.chunk
.id
.value
= chunk_id
;
2162 target_element
.session_id
= session_id
;
2164 const unsigned long element_hash
=
2165 lttng_trace_chunk_registry_element_hash(
2167 struct cds_lfht_node
*published_node
;
2168 struct cds_lfht_iter iter
;
2171 cds_lfht_lookup(registry
->ht
,
2173 lttng_trace_chunk_registry_element_match
,
2176 published_node
= cds_lfht_iter_get_node(&iter
);
2177 if (!published_node
) {
2178 *chunk_exists
= false;
2182 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
2188 struct lttng_trace_chunk
*
2189 lttng_trace_chunk_registry_find_anonymous_chunk(
2190 const struct lttng_trace_chunk_registry
*registry
,
2191 uint64_t session_id
)
2193 return _lttng_trace_chunk_registry_find_chunk(registry
,
2197 unsigned int lttng_trace_chunk_registry_put_each_chunk(
2198 const struct lttng_trace_chunk_registry
*registry
)
2200 struct cds_lfht_iter iter
;
2201 struct lttng_trace_chunk_registry_element
*chunk_element
;
2202 unsigned int trace_chunks_left
= 0;
2204 DBG("Releasing trace chunk registry to all trace chunks");
2206 cds_lfht_for_each_entry(registry
->ht
,
2207 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
2208 const char *chunk_id_str
= "none";
2209 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
2211 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
2212 if (chunk_element
->chunk
.id
.is_set
) {
2215 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
2217 chunk_element
->chunk
.id
.value
);
2218 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
2219 chunk_id_str
= "formatting error";
2221 chunk_id_str
= chunk_id_buf
;
2225 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
2226 "chunk_id = %s, name = \"%s\", status = %s",
2227 chunk_element
->session_id
,
2229 chunk_element
->chunk
.name
? : "none",
2230 chunk_element
->chunk
.close_command
.is_set
?
2232 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
2233 lttng_trace_chunk_put(&chunk_element
->chunk
);
2234 trace_chunks_left
++;
2237 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
2240 return trace_chunks_left
;