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-internal.hpp>
16 #include <common/fs-handle.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>
26 #include <lttng/constant.h>
32 #include <urcu/rculfhash.h>
36 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
37 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
39 #define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
40 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
42 enum trace_chunk_mode
{
43 TRACE_CHUNK_MODE_USER
,
44 TRACE_CHUNK_MODE_OWNER
,
48 * Callback to invoke on release of a trace chunk. Note that there is no
49 * need to 'lock' the trace chunk during the execution of these callbacks
50 * since only one thread may access a chunk during its destruction (the last
51 * to release its reference to the chunk).
53 typedef int (*chunk_command
)(struct lttng_trace_chunk
*trace_chunk
);
55 /* Move a completed trace chunk to the 'completed' trace archive folder. */
56 static int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk
*trace_chunk
);
58 static int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
);
59 /* Unlink old chunk files. */
60 static int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk
*trace_chunk
);
61 static enum lttng_trace_chunk_status
62 lttng_trace_chunk_rename_path_no_lock(struct lttng_trace_chunk
*chunk
, const char *path
);
65 struct chunk_credentials
{
66 bool use_current_user
;
67 struct lttng_credentials user
;
72 * NOTE: Make sure to update:
73 * - lttng_trace_chunk_copy(),
74 * - lttng_trace_chunk_registry_element_create_from_chunk()
75 * if you modify this structure.
77 struct lttng_trace_chunk
{
80 LTTNG_OPTIONAL(enum trace_chunk_mode
) mode
;
82 * First-level directories created within the trace chunk.
83 * Elements are of type 'char *'.
85 * Only used by _owner_ mode chunks.
87 struct lttng_dynamic_pointer_array top_level_directories
;
89 * All files contained within the trace chunk.
90 * Array of paths (char *).
92 struct lttng_dynamic_pointer_array files
;
93 /* Is contained within an lttng_trace_chunk_registry_element? */
94 bool in_registry_element
;
98 /* An unset id means the chunk is anonymous. */
99 LTTNG_OPTIONAL(uint64_t) id
;
102 * The creation and close timestamps are NOT monotonic.
103 * They must not be used in context were monotonicity is required.
105 LTTNG_OPTIONAL(time_t) timestamp_creation
;
106 LTTNG_OPTIONAL(time_t) timestamp_close
;
108 LTTNG_OPTIONAL(struct chunk_credentials
) credentials
;
109 struct lttng_directory_handle
*session_output_directory
;
110 struct lttng_directory_handle
*chunk_directory
;
111 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type
) close_command
;
113 * fd_tracker instance through which file descriptors should be
116 * An fd_tracker always outlives any trace chunk; there is no
117 * need to perform any reference counting of that object.
119 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
;
135 struct lttng_trace_chunk_registry
{
140 struct fs_handle_untracked
{
141 struct fs_handle parent
;
144 struct lttng_directory_handle
*directory_handle
;
150 static int fs_handle_untracked_get_fd(struct fs_handle
*handle
);
151 static void fs_handle_untracked_put_fd(struct fs_handle
*handle
);
152 static int fs_handle_untracked_unlink(struct fs_handle
*handle
);
153 static int fs_handle_untracked_close(struct fs_handle
*handle
);
155 static const char *lttng_trace_chunk_command_type_str(lttng_trace_chunk_command_type type
)
158 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
159 return "move to completed chunk folder";
160 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
161 return "no operation";
162 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
164 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
:
171 static chunk_command
close_command_get_post_release_func(lttng_trace_chunk_command_type type
)
174 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
175 return lttng_trace_chunk_move_to_completed_post_release
;
176 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
177 return lttng_trace_chunk_no_operation
;
178 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
179 return lttng_trace_chunk_delete_post_release
;
180 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
:
187 static struct fs_handle
*fs_handle_untracked_create(struct lttng_directory_handle
*directory_handle
,
191 struct fs_handle_untracked
*handle
= NULL
;
192 bool reference_acquired
;
193 char *path_copy
= strdup(path
);
195 LTTNG_ASSERT(fd
>= 0);
197 PERROR("Failed to copy file path while creating untracked filesystem handle");
201 handle
= zmalloc
<fs_handle_untracked
>();
203 PERROR("Failed to allocate untracked filesystem handle");
207 handle
->parent
= (typeof(handle
->parent
)){
208 .get_fd
= fs_handle_untracked_get_fd
,
209 .put_fd
= fs_handle_untracked_put_fd
,
210 .unlink
= fs_handle_untracked_unlink
,
211 .close
= fs_handle_untracked_close
,
215 reference_acquired
= lttng_directory_handle_get(directory_handle
);
216 LTTNG_ASSERT(reference_acquired
);
217 handle
->location
.directory_handle
= directory_handle
;
218 /* Ownership is transferred. */
219 handle
->location
.path
= path_copy
;
223 return handle
? &handle
->parent
: NULL
;
226 static int fs_handle_untracked_get_fd(struct fs_handle
*_handle
)
228 struct fs_handle_untracked
*handle
=
229 lttng::utils::container_of(_handle
, &fs_handle_untracked::parent
);
234 static void fs_handle_untracked_put_fd(struct fs_handle
*_handle
__attribute__((unused
)))
239 static int fs_handle_untracked_unlink(struct fs_handle
*_handle
)
241 struct fs_handle_untracked
*handle
=
242 lttng::utils::container_of(_handle
, &fs_handle_untracked::parent
);
244 return lttng_directory_handle_unlink_file(handle
->location
.directory_handle
,
245 handle
->location
.path
);
248 static void fs_handle_untracked_destroy(struct fs_handle_untracked
*handle
)
250 lttng_directory_handle_put(handle
->location
.directory_handle
);
251 free(handle
->location
.path
);
255 static int fs_handle_untracked_close(struct fs_handle
*_handle
)
257 struct fs_handle_untracked
*handle
=
258 lttng::utils::container_of(_handle
, &fs_handle_untracked::parent
);
259 int ret
= close(handle
->fd
);
261 fs_handle_untracked_destroy(handle
);
266 lttng_trace_chunk_registry_element_equals(const struct lttng_trace_chunk_registry_element
*a
,
267 const struct lttng_trace_chunk_registry_element
*b
)
269 if (a
->session_id
!= b
->session_id
) {
272 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
275 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
283 static int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
, const void *key
)
285 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
287 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
288 element_b
= caa_container_of(node
, typeof(*element_b
), trace_chunk_registry_ht_node
);
289 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
293 lttng_trace_chunk_registry_element_hash(const struct lttng_trace_chunk_registry_element
*element
)
295 unsigned long hash
= hash_key_u64(&element
->session_id
, lttng_ht_seed
);
297 if (element
->chunk
.id
.is_set
) {
298 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
305 generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
, const time_t *close_timestamp
)
308 char *new_name
= NULL
;
309 char start_datetime
[ISO8601_STR_LEN
] = {};
310 /* Add 1 for a '-' prefix. */
311 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
313 ret
= time_to_iso8601_str(creation_timestamp
, start_datetime
, sizeof(start_datetime
));
315 ERR("Failed to format trace chunk start date time");
318 if (close_timestamp
) {
319 *end_datetime_suffix
= '-';
320 ret
= time_to_iso8601_str(
321 *close_timestamp
, end_datetime_suffix
+ 1, sizeof(end_datetime_suffix
) - 1);
323 ERR("Failed to format trace chunk end date time");
327 new_name
= calloc
<char>(GENERATED_CHUNK_NAME_LEN
);
329 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
332 ret
= snprintf(new_name
,
333 GENERATED_CHUNK_NAME_LEN
,
338 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
339 ERR("Failed to format trace chunk name");
349 static void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
351 urcu_ref_init(&chunk
->ref
);
352 pthread_mutex_init(&chunk
->lock
, NULL
);
353 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
354 lttng_dynamic_pointer_array_init(&chunk
->files
, free
);
357 static void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
359 if (chunk
->session_output_directory
) {
360 lttng_directory_handle_put(chunk
->session_output_directory
);
361 chunk
->session_output_directory
= NULL
;
363 if (chunk
->chunk_directory
) {
364 lttng_directory_handle_put(chunk
->chunk_directory
);
365 chunk
->chunk_directory
= NULL
;
371 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
372 lttng_dynamic_pointer_array_reset(&chunk
->files
);
373 pthread_mutex_destroy(&chunk
->lock
);
376 static struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
378 struct lttng_trace_chunk
*chunk
= NULL
;
380 chunk
= zmalloc
<lttng_trace_chunk
>();
382 ERR("Failed to allocate trace chunk");
385 lttng_trace_chunk_init(chunk
);
390 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
392 DBG("Creating anonymous trace chunk");
393 return lttng_trace_chunk_allocate();
396 struct lttng_trace_chunk
*
397 lttng_trace_chunk_create(uint64_t chunk_id
, time_t chunk_creation_time
, const char *path
)
399 struct lttng_trace_chunk
*chunk
;
400 char chunk_creation_datetime_buf
[16] = {};
401 const char *chunk_creation_datetime_str
= "(formatting error)";
402 struct tm timeinfo_buf
, *timeinfo
;
404 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
408 /* Don't fail because of this; it is only used for logging. */
409 strftime_ret
= strftime(chunk_creation_datetime_buf
,
410 sizeof(chunk_creation_datetime_buf
),
414 chunk_creation_datetime_str
= chunk_creation_datetime_buf
;
418 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
420 chunk_creation_datetime_str
);
421 chunk
= lttng_trace_chunk_allocate();
426 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
427 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
429 chunk
->name
= generate_chunk_name(chunk_id
, chunk_creation_time
, NULL
);
431 ERR("Failed to allocate trace chunk name storage");
436 chunk
->path
= strdup(path
);
442 chunk
->path
= strdup(chunk
->name
);
449 DBG("Chunk name set to \"%s\"", chunk
->name
?: "(none)");
453 lttng_trace_chunk_put(chunk
);
457 void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk
*chunk
,
458 struct fd_tracker
*fd_tracker
)
460 LTTNG_ASSERT(!chunk
->session_output_directory
);
461 LTTNG_ASSERT(!chunk
->chunk_directory
);
462 LTTNG_ASSERT(lttng_dynamic_pointer_array_get_count(&chunk
->files
) == 0);
463 chunk
->fd_tracker
= fd_tracker
;
466 struct lttng_trace_chunk
*lttng_trace_chunk_copy(struct lttng_trace_chunk
*source_chunk
)
468 struct lttng_trace_chunk
*new_chunk
= lttng_trace_chunk_allocate();
474 pthread_mutex_lock(&source_chunk
->lock
);
476 * A new chunk is always a user; it shall create no new trace
479 new_chunk
->mode
= (typeof(new_chunk
->mode
)){
481 .value
= TRACE_CHUNK_MODE_USER
,
484 * top_level_directories is not copied as it is never used
485 * by _user_ mode chunks.
487 /* The new chunk is not part of a registry (yet, at least). */
488 new_chunk
->in_registry_element
= false;
489 new_chunk
->name_overridden
= source_chunk
->name_overridden
;
490 if (source_chunk
->name
) {
491 new_chunk
->name
= strdup(source_chunk
->name
);
492 if (!new_chunk
->name
) {
493 ERR("Failed to copy source trace chunk name in %s()", __FUNCTION__
);
497 if (source_chunk
->path
) {
498 new_chunk
->path
= strdup(source_chunk
->path
);
499 if (!new_chunk
->path
) {
500 ERR("Failed to copy source trace chunk path in %s()", __FUNCTION__
);
503 new_chunk
->id
= source_chunk
->id
;
504 new_chunk
->timestamp_creation
= source_chunk
->timestamp_creation
;
505 new_chunk
->timestamp_close
= source_chunk
->timestamp_close
;
506 new_chunk
->credentials
= source_chunk
->credentials
;
507 if (source_chunk
->session_output_directory
) {
508 const bool reference_acquired
=
509 lttng_directory_handle_get(source_chunk
->session_output_directory
);
511 LTTNG_ASSERT(reference_acquired
);
512 new_chunk
->session_output_directory
= source_chunk
->session_output_directory
;
514 if (source_chunk
->chunk_directory
) {
515 const bool reference_acquired
=
516 lttng_directory_handle_get(source_chunk
->chunk_directory
);
518 LTTNG_ASSERT(reference_acquired
);
519 new_chunk
->chunk_directory
= source_chunk
->chunk_directory
;
521 new_chunk
->close_command
= source_chunk
->close_command
;
522 new_chunk
->fd_tracker
= source_chunk
->fd_tracker
;
523 pthread_mutex_unlock(&source_chunk
->lock
);
527 pthread_mutex_unlock(&source_chunk
->lock
);
528 lttng_trace_chunk_put(new_chunk
);
532 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(struct lttng_trace_chunk
*chunk
,
535 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
537 pthread_mutex_lock(&chunk
->lock
);
538 if (chunk
->id
.is_set
) {
539 *id
= chunk
->id
.value
;
541 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
543 pthread_mutex_unlock(&chunk
->lock
);
547 enum lttng_trace_chunk_status
548 lttng_trace_chunk_get_creation_timestamp(struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
551 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
553 pthread_mutex_lock(&chunk
->lock
);
554 if (chunk
->timestamp_creation
.is_set
) {
555 *creation_ts
= chunk
->timestamp_creation
.value
;
557 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
559 pthread_mutex_unlock(&chunk
->lock
);
563 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_timestamp(struct lttng_trace_chunk
*chunk
,
566 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
568 pthread_mutex_lock(&chunk
->lock
);
569 if (chunk
->timestamp_close
.is_set
) {
570 *close_ts
= chunk
->timestamp_close
.value
;
572 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
574 pthread_mutex_unlock(&chunk
->lock
);
578 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(struct lttng_trace_chunk
*chunk
,
581 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
583 pthread_mutex_lock(&chunk
->lock
);
584 if (!chunk
->timestamp_creation
.is_set
) {
585 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
586 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
591 * Note: we do not enforce that the closing timestamp be greater or
592 * equal to the begin timestamp. These timestamps are used for
593 * generating the chunk name and should only be used in context where
594 * the monotonicity of time is not important. The source of those
595 * timestamps is NOT monotonic and represent the system calendar time,
596 * also know as the wall time.
598 if (chunk
->timestamp_creation
.value
> close_ts
) {
599 WARN("Set trace chunk close timestamp: close timestamp is before creation timestamp, begin : %ld, close : %ld",
600 chunk
->timestamp_creation
.value
,
604 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
605 if (!chunk
->name_overridden
) {
607 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
608 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
611 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
615 pthread_mutex_unlock(&chunk
->lock
);
619 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(struct lttng_trace_chunk
*chunk
,
621 bool *name_overridden
)
623 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
625 pthread_mutex_lock(&chunk
->lock
);
626 if (name_overridden
) {
627 *name_overridden
= chunk
->name_overridden
;
630 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
635 pthread_mutex_unlock(&chunk
->lock
);
639 bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk
*chunk
)
641 bool name_overridden
;
643 pthread_mutex_lock(&chunk
->lock
);
644 name_overridden
= chunk
->name_overridden
;
645 pthread_mutex_unlock(&chunk
->lock
);
646 return name_overridden
;
649 static bool is_valid_chunk_name(const char *name
)
657 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
658 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
662 if (strchr(name
, '/') || strchr(name
, '.')) {
669 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(struct lttng_trace_chunk
*chunk
,
673 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
674 char *new_name
, *new_path
;
676 DBG("Override trace chunk name from %s to %s", chunk
->name
, name
);
677 if (!is_valid_chunk_name(name
)) {
678 ERR("Attempted to set an invalid name on a trace chunk: name = %s", name
?: "NULL");
679 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
683 pthread_mutex_lock(&chunk
->lock
);
684 if (!chunk
->id
.is_set
) {
685 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
687 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
691 new_name
= strdup(name
);
693 ERR("Failed to allocate new trace chunk name");
694 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
698 chunk
->name
= new_name
;
700 new_path
= strdup(name
);
702 ERR("Failed to allocate new trace chunk path");
703 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
707 chunk
->path
= new_path
;
709 chunk
->name_overridden
= true;
711 pthread_mutex_unlock(&chunk
->lock
);
716 static enum lttng_trace_chunk_status
717 lttng_trace_chunk_rename_path_no_lock(struct lttng_trace_chunk
*chunk
, const char *path
)
720 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
721 struct lttng_directory_handle
*rename_directory
= NULL
;
722 char *new_path
, *old_path
;
725 if (chunk
->name_overridden
) {
726 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
730 old_path
= chunk
->path
;
731 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path
, path
);
733 if ((!old_path
&& !path
) || (old_path
&& path
&& !strcmp(old_path
, path
))) {
737 * Use chunk name as path if NULL path is specified.
743 /* Renaming from "" to "" is not accepted. */
744 if (path
[0] == '\0' && old_path
[0] == '\0') {
745 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
750 * If a rename is performed on a chunk for which the chunk_directory
751 * is not set (yet), or the session_output_directory is not set
752 * (interacting with a relay daemon), there is no rename to perform.
754 if (!chunk
->chunk_directory
|| !chunk
->session_output_directory
) {
758 if (old_path
&& old_path
[0] != '\0' && path
[0] != '\0') {
759 /* Rename chunk directory. */
760 ret
= lttng_directory_handle_rename_as_user(
761 chunk
->session_output_directory
,
763 chunk
->session_output_directory
,
765 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
767 &chunk
->credentials
.value
.user
);
769 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
772 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
775 rename_directory
= chunk
->fd_tracker
?
776 fd_tracker_create_directory_handle_from_handle(
777 chunk
->fd_tracker
, chunk
->session_output_directory
, path
) :
778 lttng_directory_handle_create_from_handle(path
,
779 chunk
->session_output_directory
);
780 if (!rename_directory
) {
781 ERR("Failed to get handle to trace chunk rename directory");
782 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
786 /* Release old handle. */
787 lttng_directory_handle_put(chunk
->chunk_directory
);
789 * Transfer new handle reference to chunk as the current chunk
792 chunk
->chunk_directory
= rename_directory
;
793 rename_directory
= NULL
;
794 } else if (old_path
&& old_path
[0] == '\0') {
796 count
= lttng_dynamic_pointer_array_get_count(
797 &chunk
->top_level_directories
);
799 ret
= lttng_directory_handle_create_subdirectory_as_user(
800 chunk
->session_output_directory
,
803 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
805 &chunk
->credentials
.value
.user
);
807 PERROR("Failed to create trace chunk rename directory \"%s\"", path
);
808 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
812 rename_directory
= lttng_directory_handle_create_from_handle(
813 path
, chunk
->session_output_directory
);
814 if (!rename_directory
) {
815 ERR("Failed to get handle to trace chunk rename directory");
816 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
820 /* Move toplevel directories. */
821 for (i
= 0; i
< count
; i
++) {
822 const char *top_level_name
=
823 (const char *) lttng_dynamic_pointer_array_get_pointer(
824 &chunk
->top_level_directories
, i
);
826 ret
= lttng_directory_handle_rename_as_user(
827 chunk
->chunk_directory
,
831 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
833 &chunk
->credentials
.value
.user
);
835 PERROR("Failed to move \"%s\" to trace chunk rename directory",
837 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
841 /* Release old handle. */
842 lttng_directory_handle_put(chunk
->chunk_directory
);
844 * Transfer new handle reference to chunk as the current chunk
847 chunk
->chunk_directory
= rename_directory
;
848 rename_directory
= NULL
;
849 } else if (old_path
) {
851 count
= lttng_dynamic_pointer_array_get_count(
852 &chunk
->top_level_directories
);
853 const bool reference_acquired
=
854 lttng_directory_handle_get(chunk
->session_output_directory
);
856 LTTNG_ASSERT(reference_acquired
);
857 rename_directory
= chunk
->session_output_directory
;
859 /* Move toplevel directories. */
860 for (i
= 0; i
< count
; i
++) {
861 const char *top_level_name
=
862 (const char *) lttng_dynamic_pointer_array_get_pointer(
863 &chunk
->top_level_directories
, i
);
865 ret
= lttng_directory_handle_rename_as_user(
866 chunk
->chunk_directory
,
870 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
872 &chunk
->credentials
.value
.user
);
874 PERROR("Failed to move \"%s\" to trace chunk rename directory",
876 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
880 /* Release old handle. */
881 lttng_directory_handle_put(chunk
->chunk_directory
);
883 * Transfer new handle reference to chunk as the current chunk
886 chunk
->chunk_directory
= rename_directory
;
887 rename_directory
= NULL
;
889 /* Remove old directory. */
890 status
= (lttng_trace_chunk_status
) lttng_directory_handle_remove_subdirectory(
891 chunk
->session_output_directory
, old_path
);
892 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
893 ERR("Error removing subdirectory '%s' file when deleting chunk", old_path
);
897 /* Unexpected !old_path && !path. */
898 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
903 new_path
= strdup(path
);
905 ERR("Failed to allocate new trace chunk path");
906 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
910 chunk
->path
= new_path
;
912 lttng_directory_handle_put(rename_directory
);
916 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path(struct lttng_trace_chunk
*chunk
,
920 enum lttng_trace_chunk_status status
;
922 pthread_mutex_lock(&chunk
->lock
);
923 status
= lttng_trace_chunk_rename_path_no_lock(chunk
, path
);
924 pthread_mutex_unlock(&chunk
->lock
);
929 enum lttng_trace_chunk_status
930 lttng_trace_chunk_get_credentials(struct lttng_trace_chunk
*chunk
,
931 struct lttng_credentials
*credentials
)
933 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
935 pthread_mutex_lock(&chunk
->lock
);
936 if (chunk
->credentials
.is_set
) {
937 if (chunk
->credentials
.value
.use_current_user
) {
938 LTTNG_OPTIONAL_SET(&credentials
->uid
, geteuid());
939 LTTNG_OPTIONAL_SET(&credentials
->gid
, getegid());
941 *credentials
= chunk
->credentials
.value
.user
;
944 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
946 pthread_mutex_unlock(&chunk
->lock
);
950 enum lttng_trace_chunk_status
951 lttng_trace_chunk_set_credentials(struct lttng_trace_chunk
*chunk
,
952 const struct lttng_credentials
*user_credentials
)
954 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
955 const struct chunk_credentials credentials
= {
956 .use_current_user
= false,
957 .user
= *user_credentials
,
960 pthread_mutex_lock(&chunk
->lock
);
961 if (chunk
->credentials
.is_set
) {
962 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
965 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
967 pthread_mutex_unlock(&chunk
->lock
);
971 enum lttng_trace_chunk_status
972 lttng_trace_chunk_set_credentials_current_user(struct lttng_trace_chunk
*chunk
)
974 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
975 const struct chunk_credentials credentials
= {
976 .use_current_user
= true,
980 pthread_mutex_lock(&chunk
->lock
);
981 if (chunk
->credentials
.is_set
) {
982 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
985 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
987 pthread_mutex_unlock(&chunk
->lock
);
991 enum lttng_trace_chunk_status
992 lttng_trace_chunk_set_as_owner(struct lttng_trace_chunk
*chunk
,
993 struct lttng_directory_handle
*session_output_directory
)
996 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
997 struct lttng_directory_handle
*chunk_directory_handle
= NULL
;
998 bool reference_acquired
;
1000 pthread_mutex_lock(&chunk
->lock
);
1001 if (chunk
->mode
.is_set
) {
1002 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1005 if (!chunk
->credentials
.is_set
) {
1007 * Fatal error, credentials must be set before a
1008 * directory is created.
1010 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
1011 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1014 if (chunk
->path
&& chunk
->path
[0] != '\0') {
1015 ret
= lttng_directory_handle_create_subdirectory_as_user(
1016 session_output_directory
,
1019 !chunk
->credentials
.value
.use_current_user
?
1020 &chunk
->credentials
.value
.user
:
1023 PERROR("Failed to create chunk output directory \"%s\"", chunk
->path
);
1024 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1027 chunk_directory_handle
= chunk
->fd_tracker
?
1028 fd_tracker_create_directory_handle_from_handle(
1029 chunk
->fd_tracker
, session_output_directory
, chunk
->path
) :
1030 lttng_directory_handle_create_from_handle(chunk
->path
,
1031 session_output_directory
);
1032 if (!chunk_directory_handle
) {
1033 /* The function already logs on all error paths. */
1034 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1039 * A nameless chunk does not need its own output directory.
1040 * The session's output directory will be used.
1042 reference_acquired
= lttng_directory_handle_get(session_output_directory
);
1044 LTTNG_ASSERT(reference_acquired
);
1045 chunk_directory_handle
= session_output_directory
;
1047 chunk
->chunk_directory
= chunk_directory_handle
;
1048 chunk_directory_handle
= NULL
;
1049 reference_acquired
= lttng_directory_handle_get(session_output_directory
);
1050 LTTNG_ASSERT(reference_acquired
);
1051 chunk
->session_output_directory
= session_output_directory
;
1052 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
1054 pthread_mutex_unlock(&chunk
->lock
);
1058 enum lttng_trace_chunk_status
1059 lttng_trace_chunk_set_as_user(struct lttng_trace_chunk
*chunk
,
1060 struct lttng_directory_handle
*chunk_directory
)
1062 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1063 bool reference_acquired
;
1065 pthread_mutex_lock(&chunk
->lock
);
1066 if (chunk
->mode
.is_set
) {
1067 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1070 if (!chunk
->credentials
.is_set
) {
1071 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
1072 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1075 reference_acquired
= lttng_directory_handle_get(chunk_directory
);
1076 LTTNG_ASSERT(reference_acquired
);
1077 chunk
->chunk_directory
= chunk_directory
;
1078 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
1080 pthread_mutex_unlock(&chunk
->lock
);
1084 enum lttng_trace_chunk_status
1085 lttng_trace_chunk_get_session_output_directory_handle(struct lttng_trace_chunk
*chunk
,
1086 struct lttng_directory_handle
**handle
)
1088 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1090 pthread_mutex_lock(&chunk
->lock
);
1091 if (!chunk
->session_output_directory
) {
1092 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1096 const bool reference_acquired
=
1097 lttng_directory_handle_get(chunk
->session_output_directory
);
1099 LTTNG_ASSERT(reference_acquired
);
1100 *handle
= chunk
->session_output_directory
;
1103 pthread_mutex_unlock(&chunk
->lock
);
1107 enum lttng_trace_chunk_status
1108 lttng_trace_chunk_borrow_chunk_directory_handle(struct lttng_trace_chunk
*chunk
,
1109 const struct lttng_directory_handle
**handle
)
1111 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1113 pthread_mutex_lock(&chunk
->lock
);
1114 if (!chunk
->chunk_directory
) {
1115 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1119 *handle
= chunk
->chunk_directory
;
1121 pthread_mutex_unlock(&chunk
->lock
);
1125 /* Add a top-level directory to the trace chunk if it was previously unknown. */
1126 static int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
, const char *new_path
)
1130 size_t i
, count
= lttng_dynamic_pointer_array_get_count(&chunk
->top_level_directories
);
1131 const char *new_path_separator_pos
= strchr(new_path
, '/');
1132 const ptrdiff_t new_path_top_level_len
=
1133 new_path_separator_pos
? new_path_separator_pos
- new_path
: strlen(new_path
);
1135 for (i
= 0; i
< count
; i
++) {
1136 const char *path
= (const char *) lttng_dynamic_pointer_array_get_pointer(
1137 &chunk
->top_level_directories
, i
);
1138 const ptrdiff_t path_top_level_len
= strlen(path
);
1140 if (path_top_level_len
!= new_path_top_level_len
) {
1143 if (!strncmp(path
, new_path
, path_top_level_len
)) {
1150 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
1152 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1154 chunk
->name
?: "(unnamed)");
1156 PERROR("Failed to copy path");
1160 ret
= lttng_dynamic_pointer_array_add_pointer(&chunk
->top_level_directories
, copy
);
1162 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1171 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(struct lttng_trace_chunk
*chunk
,
1175 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1177 DBG("Creating trace chunk subdirectory \"%s\"", path
);
1178 pthread_mutex_lock(&chunk
->lock
);
1179 if (!chunk
->credentials
.is_set
) {
1181 * Fatal error, credentials must be set before a
1182 * directory is created.
1184 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1186 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1189 if (!chunk
->mode
.is_set
|| chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
1190 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1192 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1195 if (!chunk
->chunk_directory
) {
1196 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1198 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1202 ERR("Refusing to create absolute trace chunk directory \"%s\"", path
);
1203 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1206 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
1207 chunk
->chunk_directory
,
1210 chunk
->credentials
.value
.use_current_user
? NULL
: &chunk
->credentials
.value
.user
);
1212 PERROR("Failed to create trace chunk subdirectory \"%s\"", path
);
1213 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1216 ret
= add_top_level_directory_unique(chunk
, path
);
1218 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1222 pthread_mutex_unlock(&chunk
->lock
);
1227 * TODO: Implement O(1) lookup.
1230 lttng_trace_chunk_find_file(struct lttng_trace_chunk
*chunk
, const char *path
, size_t *index
)
1234 count
= lttng_dynamic_pointer_array_get_count(&chunk
->files
);
1235 for (i
= 0; i
< count
; i
++) {
1236 const char *iter_path
=
1237 (const char *) lttng_dynamic_pointer_array_get_pointer(&chunk
->files
, i
);
1238 if (!strcmp(iter_path
, path
)) {
1248 static enum lttng_trace_chunk_status
lttng_trace_chunk_add_file(struct lttng_trace_chunk
*chunk
,
1253 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1255 if (lttng_trace_chunk_find_file(chunk
, path
, NULL
)) {
1256 return LTTNG_TRACE_CHUNK_STATUS_OK
;
1258 DBG("Adding new file \"%s\" to trace chunk \"%s\"", path
, chunk
->name
?: "(unnamed)");
1259 copy
= strdup(path
);
1261 PERROR("Failed to copy path");
1262 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1265 ret
= lttng_dynamic_pointer_array_add_pointer(&chunk
->files
, copy
);
1267 ERR("Allocation failure while adding file to a trace chunk");
1269 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1276 static void lttng_trace_chunk_remove_file(struct lttng_trace_chunk
*chunk
, const char *path
)
1282 found
= lttng_trace_chunk_find_file(chunk
, path
, &index
);
1286 ret
= lttng_dynamic_pointer_array_remove_pointer(&chunk
->files
, index
);
1290 static enum lttng_trace_chunk_status
1291 _lttng_trace_chunk_open_fs_handle_locked(struct lttng_trace_chunk
*chunk
,
1292 const char *file_path
,
1295 struct fs_handle
**out_handle
,
1296 bool expect_no_file
)
1299 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1301 DBG("Opening trace chunk file \"%s\"", file_path
);
1302 if (!chunk
->credentials
.is_set
) {
1304 * Fatal error, credentials must be set before a
1307 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1309 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1312 if (!chunk
->chunk_directory
) {
1313 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1315 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1318 status
= lttng_trace_chunk_add_file(chunk
, file_path
);
1319 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1322 if (chunk
->fd_tracker
) {
1323 LTTNG_ASSERT(chunk
->credentials
.value
.use_current_user
);
1324 *out_handle
= fd_tracker_open_fs_handle(
1325 chunk
->fd_tracker
, chunk
->chunk_directory
, file_path
, flags
, &mode
);
1326 ret
= *out_handle
? 0 : -1;
1328 ret
= lttng_directory_handle_open_file_as_user(
1329 chunk
->chunk_directory
,
1333 chunk
->credentials
.value
.use_current_user
? NULL
:
1334 &chunk
->credentials
.value
.user
);
1337 fs_handle_untracked_create(chunk
->chunk_directory
, file_path
, ret
);
1339 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1345 if (errno
== ENOENT
&& expect_no_file
) {
1346 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
1348 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1352 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1354 lttng_trace_chunk_remove_file(chunk
, file_path
);
1361 enum lttng_trace_chunk_status
lttng_trace_chunk_open_fs_handle(struct lttng_trace_chunk
*chunk
,
1362 const char *file_path
,
1365 struct fs_handle
**out_handle
,
1366 bool expect_no_file
)
1368 enum lttng_trace_chunk_status status
;
1370 pthread_mutex_lock(&chunk
->lock
);
1371 status
= _lttng_trace_chunk_open_fs_handle_locked(
1372 chunk
, file_path
, flags
, mode
, out_handle
, expect_no_file
);
1373 pthread_mutex_unlock(&chunk
->lock
);
1377 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(struct lttng_trace_chunk
*chunk
,
1378 const char *file_path
,
1382 bool expect_no_file
)
1384 enum lttng_trace_chunk_status status
;
1385 struct fs_handle
*fs_handle
;
1387 pthread_mutex_lock(&chunk
->lock
);
1389 * Using this method is never valid when an fd_tracker is being
1390 * used since the resulting file descriptor would not be tracked.
1392 LTTNG_ASSERT(!chunk
->fd_tracker
);
1393 status
= _lttng_trace_chunk_open_fs_handle_locked(
1394 chunk
, file_path
, flags
, mode
, &fs_handle
, expect_no_file
);
1395 pthread_mutex_unlock(&chunk
->lock
);
1397 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
1398 *out_fd
= fs_handle_get_fd(fs_handle
);
1400 * Does not close the fd; we just "unbox" it from the fs_handle.
1402 fs_handle_untracked_destroy(
1403 lttng::utils::container_of(fs_handle
, &fs_handle_untracked::parent
));
1409 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
, const char *file_path
)
1412 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1414 DBG("Unlinking trace chunk file \"%s\"", file_path
);
1415 pthread_mutex_lock(&chunk
->lock
);
1416 if (!chunk
->credentials
.is_set
) {
1418 * Fatal error, credentials must be set before a
1421 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1423 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1426 if (!chunk
->chunk_directory
) {
1427 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1429 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1432 ret
= lttng_directory_handle_unlink_file_as_user(
1433 chunk
->chunk_directory
,
1435 chunk
->credentials
.value
.use_current_user
? NULL
: &chunk
->credentials
.value
.user
);
1437 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1440 lttng_trace_chunk_remove_file(chunk
, file_path
);
1442 pthread_mutex_unlock(&chunk
->lock
);
1446 static int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk
*chunk
,
1450 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1452 DBG("Recursively removing trace chunk directory \"%s\"", path
);
1453 pthread_mutex_lock(&chunk
->lock
);
1454 if (!chunk
->credentials
.is_set
) {
1456 * Fatal error, credentials must be set before a
1457 * directory is removed.
1459 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1461 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1464 if (!chunk
->chunk_directory
) {
1465 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1467 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1470 ret
= lttng_directory_handle_remove_subdirectory_recursive_as_user(
1471 chunk
->chunk_directory
,
1473 chunk
->credentials
.value
.use_current_user
? NULL
: &chunk
->credentials
.value
.user
,
1474 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
);
1476 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1480 pthread_mutex_unlock(&chunk
->lock
);
1484 static int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk
*trace_chunk
)
1487 char *archived_chunk_name
= NULL
;
1488 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
1489 const time_t creation_timestamp
= LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
1490 const time_t close_timestamp
= LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
1491 struct lttng_directory_handle
*archived_chunks_directory
= NULL
;
1492 enum lttng_trace_chunk_status status
;
1494 if (!trace_chunk
->mode
.is_set
|| trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
1495 !trace_chunk
->session_output_directory
) {
1497 * This command doesn't need to run if the output is remote
1498 * or if the trace chunk is not owned by this process.
1503 LTTNG_ASSERT(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
1504 LTTNG_ASSERT(!trace_chunk
->name_overridden
);
1505 LTTNG_ASSERT(trace_chunk
->path
);
1507 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
, &close_timestamp
);
1508 if (!archived_chunk_name
) {
1509 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1514 ret
= lttng_directory_handle_create_subdirectory_as_user(
1515 trace_chunk
->session_output_directory
,
1516 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1518 !trace_chunk
->credentials
.value
.use_current_user
?
1519 &trace_chunk
->credentials
.value
.user
:
1522 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1523 "\" directory for archived trace chunks");
1527 archived_chunks_directory
= trace_chunk
->fd_tracker
?
1528 fd_tracker_create_directory_handle_from_handle(
1529 trace_chunk
->fd_tracker
,
1530 trace_chunk
->session_output_directory
,
1531 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
) :
1532 lttng_directory_handle_create_from_handle(DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1533 trace_chunk
->session_output_directory
);
1534 if (!archived_chunks_directory
) {
1535 PERROR("Failed to get handle to archived trace chunks directory");
1541 * Make sure chunk is renamed to old directory if not already done by
1542 * the creation of the next chunk. This happens if a rotation is
1543 * performed while tracing is stopped.
1545 if (!trace_chunk
->path
|| strcmp(trace_chunk
->path
, DEFAULT_CHUNK_TMP_OLD_DIRECTORY
)) {
1546 status
= lttng_trace_chunk_rename_path_no_lock(trace_chunk
,
1547 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1548 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1549 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1555 ret
= lttng_directory_handle_rename_as_user(
1556 trace_chunk
->session_output_directory
,
1558 archived_chunks_directory
,
1559 archived_chunk_name
,
1560 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1562 &trace_chunk
->credentials
.value
.user
);
1564 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1566 archived_chunk_name
);
1570 lttng_directory_handle_put(archived_chunks_directory
);
1571 free(archived_chunk_name
);
1575 static int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
1576 __attribute__((unused
)))
1581 static int lttng_trace_chunk_delete_post_release_user(struct lttng_trace_chunk
*trace_chunk
)
1585 DBG("Trace chunk \"delete\" close command post-release (User)");
1587 /* Unlink all files. */
1588 while (lttng_dynamic_pointer_array_get_count(&trace_chunk
->files
) != 0) {
1589 enum lttng_trace_chunk_status status
;
1593 path
= (const char *) lttng_dynamic_pointer_array_get_pointer(&trace_chunk
->files
,
1595 DBG("Unlink file: %s", path
);
1597 (lttng_trace_chunk_status
) lttng_trace_chunk_unlink_file(trace_chunk
, path
);
1598 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1599 ERR("Error unlinking file '%s' when deleting chunk", path
);
1608 static int lttng_trace_chunk_delete_post_release_owner(struct lttng_trace_chunk
*trace_chunk
)
1610 enum lttng_trace_chunk_status status
;
1614 ret
= lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1619 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1621 LTTNG_ASSERT(trace_chunk
->session_output_directory
);
1622 LTTNG_ASSERT(trace_chunk
->chunk_directory
);
1624 /* Remove empty directories. */
1625 count
= lttng_dynamic_pointer_array_get_count(&trace_chunk
->top_level_directories
);
1627 for (i
= 0; i
< count
; i
++) {
1628 const char *top_level_name
= (const char *) lttng_dynamic_pointer_array_get_pointer(
1629 &trace_chunk
->top_level_directories
, i
);
1631 status
= (lttng_trace_chunk_status
) lttng_trace_chunk_remove_subdirectory_recursive(
1632 trace_chunk
, top_level_name
);
1633 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1634 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1641 lttng_directory_handle_put(trace_chunk
->chunk_directory
);
1642 trace_chunk
->chunk_directory
= NULL
;
1644 if (trace_chunk
->path
&& trace_chunk
->path
[0] != '\0') {
1645 status
= (lttng_trace_chunk_status
)
1646 lttng_directory_handle_remove_subdirectory(
1647 trace_chunk
->session_output_directory
, trace_chunk
->path
);
1648 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1649 ERR("Error removing subdirectory '%s' file when deleting chunk",
1655 free(trace_chunk
->path
);
1656 trace_chunk
->path
= NULL
;
1662 * For local files, session and consumer daemons all run the delete hook. The
1663 * consumer daemons have the list of files to unlink, and technically the
1664 * session daemon is the owner of the chunk. Unlink all files owned by each
1667 static int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk
*trace_chunk
)
1669 if (!trace_chunk
->chunk_directory
) {
1673 if (trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
) {
1674 return lttng_trace_chunk_delete_post_release_owner(trace_chunk
);
1676 return lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1680 enum lttng_trace_chunk_status
1681 lttng_trace_chunk_get_close_command(struct lttng_trace_chunk
*chunk
,
1682 enum lttng_trace_chunk_command_type
*command_type
)
1684 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1686 pthread_mutex_lock(&chunk
->lock
);
1687 if (chunk
->close_command
.is_set
) {
1688 *command_type
= chunk
->close_command
.value
;
1689 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1691 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1693 pthread_mutex_unlock(&chunk
->lock
);
1697 enum lttng_trace_chunk_status
1698 lttng_trace_chunk_set_close_command(struct lttng_trace_chunk
*chunk
,
1699 enum lttng_trace_chunk_command_type close_command
)
1701 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1703 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1704 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1705 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1709 pthread_mutex_lock(&chunk
->lock
);
1710 if (chunk
->close_command
.is_set
) {
1711 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1712 lttng_trace_chunk_command_type_str(chunk
->close_command
.value
),
1713 lttng_trace_chunk_command_type_str(close_command
));
1715 DBG("Setting trace chunk close command to \"%s\"",
1716 lttng_trace_chunk_command_type_str(close_command
));
1719 * Unset close command for no-op for backward compatibility with relayd
1722 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1723 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1725 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1727 pthread_mutex_unlock(&chunk
->lock
);
1732 const char *lttng_trace_chunk_command_type_get_name(enum lttng_trace_chunk_command_type command
)
1735 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1736 return "move to completed trace chunk folder";
1737 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1738 return "no operation";
1739 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1746 bool lttng_trace_chunk_ids_equal(const struct lttng_trace_chunk
*chunk_a
,
1747 const struct lttng_trace_chunk
*chunk_b
)
1751 if (chunk_a
== chunk_b
) {
1756 if (!!chunk_a
^ !!chunk_b
) {
1760 if (chunk_a
->id
.is_set
^ chunk_a
->id
.is_set
) {
1761 /* One id is set and not the other, thus they are not equal. */
1765 if (!chunk_a
->id
.is_set
) {
1766 /* Both ids are unset. */
1769 equal
= chunk_a
->id
.value
== chunk_b
->id
.value
;
1776 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1778 return urcu_ref_get_unless_zero(&chunk
->ref
);
1781 static void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1783 struct lttng_trace_chunk_registry_element
*element
=
1784 lttng::utils::container_of(node
, <tng_trace_chunk_registry_element::rcu_node
);
1789 static void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1791 struct lttng_trace_chunk
*chunk
= lttng::utils::container_of(ref
, <tng_trace_chunk::ref
);
1793 if (chunk
->close_command
.is_set
) {
1794 chunk_command func
=
1795 close_command_get_post_release_func(chunk
->close_command
.value
);
1798 ERR("Trace chunk post-release command %s has failed.",
1799 lttng_trace_chunk_command_type_str(chunk
->close_command
.value
));
1803 if (chunk
->in_registry_element
) {
1804 struct lttng_trace_chunk_registry_element
*element
;
1807 * Release internal chunk attributes immediately and
1808 * only use the deferred `call_rcu` work to reclaim the
1811 * This ensures that file handles are released as soon as
1812 * possible which works around a problem we encounter with PRAM fs
1813 * mounts (and possibly other non-POSIX compliant file systems):
1814 * directories that contain files which are open can't be
1817 * This means that the recording of a snapshot could be
1818 * completed, but that it would be impossible for the user to
1819 * delete it until the deferred clean-up released the file
1820 * handles to its contents.
1822 lttng_trace_chunk_fini(chunk
);
1824 element
= lttng::utils::container_of(chunk
,
1825 <tng_trace_chunk_registry_element::chunk
);
1826 if (element
->registry
) {
1828 cds_lfht_del(element
->registry
->ht
, &element
->trace_chunk_registry_ht_node
);
1830 call_rcu(&element
->rcu_node
, free_lttng_trace_chunk_registry_element
);
1832 /* Never published, can be free'd immediately. */
1833 free_lttng_trace_chunk_registry_element(&element
->rcu_node
);
1836 /* Not RCU-protected, free immediately. */
1837 lttng_trace_chunk_fini(chunk
);
1842 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1847 LTTNG_ASSERT(chunk
->ref
.refcount
);
1848 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1851 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1853 struct lttng_trace_chunk_registry
*registry
;
1855 registry
= zmalloc
<lttng_trace_chunk_registry
>();
1860 registry
->ht
= cds_lfht_new(
1861 DEFAULT_HT_SIZE
, 1, 0, CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1862 if (!registry
->ht
) {
1868 lttng_trace_chunk_registry_destroy(registry
);
1872 void lttng_trace_chunk_registry_destroy(struct lttng_trace_chunk_registry
*registry
)
1878 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1884 static struct lttng_trace_chunk_registry_element
*
1885 lttng_trace_chunk_registry_element_create_from_chunk(struct lttng_trace_chunk
*chunk
,
1886 uint64_t session_id
)
1888 struct lttng_trace_chunk_registry_element
*element
=
1889 zmalloc
<lttng_trace_chunk_registry_element
>();
1894 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1895 element
->session_id
= session_id
;
1897 element
->chunk
= *chunk
;
1898 lttng_trace_chunk_init(&element
->chunk
);
1899 if (chunk
->session_output_directory
) {
1900 /* Transferred ownership. */
1901 element
->chunk
.session_output_directory
= chunk
->session_output_directory
;
1902 chunk
->session_output_directory
= NULL
;
1904 if (chunk
->chunk_directory
) {
1905 /* Transferred ownership. */
1906 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1907 chunk
->chunk_directory
= NULL
;
1910 * The original chunk becomes invalid; the name and path attributes are
1911 * transferred to the new chunk instance.
1915 element
->chunk
.fd_tracker
= chunk
->fd_tracker
;
1916 element
->chunk
.in_registry_element
= true;
1921 struct lttng_trace_chunk
*
1922 lttng_trace_chunk_registry_publish_chunk(struct lttng_trace_chunk_registry
*registry
,
1923 uint64_t session_id
,
1924 struct lttng_trace_chunk
*chunk
)
1928 return lttng_trace_chunk_registry_publish_chunk(registry
, session_id
, chunk
, &unused
);
1931 struct lttng_trace_chunk
*
1932 lttng_trace_chunk_registry_publish_chunk(struct lttng_trace_chunk_registry
*registry
,
1933 uint64_t session_id
,
1934 struct lttng_trace_chunk
*chunk
,
1935 bool *previously_published
)
1937 struct lttng_trace_chunk_registry_element
*element
;
1938 unsigned long element_hash
;
1940 pthread_mutex_lock(&chunk
->lock
);
1941 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
, session_id
);
1942 pthread_mutex_unlock(&chunk
->lock
);
1947 * chunk is now invalid, the only valid operation is a 'put' from the
1951 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1955 struct cds_lfht_node
*published_node
;
1956 struct lttng_trace_chunk
*published_chunk
;
1957 struct lttng_trace_chunk_registry_element
*published_element
;
1959 published_node
= cds_lfht_add_unique(registry
->ht
,
1961 lttng_trace_chunk_registry_element_match
,
1963 &element
->trace_chunk_registry_ht_node
);
1964 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
1965 /* Successfully published the new element. */
1966 element
->registry
= registry
;
1967 /* Acquire a reference for the caller. */
1968 if (lttng_trace_chunk_get(&element
->chunk
)) {
1969 *previously_published
= false;
1973 * Another thread concurrently unpublished the
1974 * trace chunk. This is currently unexpected.
1976 * Re-attempt to publish.
1978 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1984 * An equivalent trace chunk was published before this trace
1985 * chunk. Attempt to acquire a reference to the one that was
1986 * already published and release the reference to the copy we
1987 * created if successful.
1989 published_element
= lttng::utils::container_of(
1991 <tng_trace_chunk_registry_element::trace_chunk_registry_ht_node
);
1992 published_chunk
= &published_element
->chunk
;
1993 if (lttng_trace_chunk_get(published_chunk
)) {
1994 lttng_trace_chunk_put(&element
->chunk
);
1995 element
= published_element
;
1996 *previously_published
= true;
2000 * A reference to the previously published trace chunk could not
2001 * be acquired. Hence, retry to publish our copy of the trace
2007 return element
? &element
->chunk
: NULL
;
2011 * Note that the caller must be registered as an RCU thread.
2012 * However, it does not need to hold the RCU read lock. The RCU read lock is
2013 * acquired to perform the look-up in the registry's hash table and held until
2014 * after a reference to the "found" trace chunk is acquired.
2016 * IOW, holding a reference guarantees the existence of the object for the
2019 static struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
2020 const struct lttng_trace_chunk_registry
*registry
, uint64_t session_id
, uint64_t *chunk_id
)
2022 lttng_trace_chunk_registry_element target_element
{};
2024 target_element
.chunk
.id
.is_set
= !!chunk_id
;
2025 target_element
.chunk
.id
.value
= chunk_id
? *chunk_id
: 0;
2026 target_element
.session_id
= session_id
;
2028 const unsigned long element_hash
= lttng_trace_chunk_registry_element_hash(&target_element
);
2029 struct cds_lfht_node
*published_node
;
2030 struct lttng_trace_chunk_registry_element
*published_element
;
2031 struct lttng_trace_chunk
*published_chunk
= NULL
;
2032 struct cds_lfht_iter iter
;
2035 cds_lfht_lookup(registry
->ht
,
2037 lttng_trace_chunk_registry_element_match
,
2040 published_node
= cds_lfht_iter_get_node(&iter
);
2041 if (!published_node
) {
2045 published_element
= lttng::utils::container_of(
2046 published_node
, <tng_trace_chunk_registry_element::trace_chunk_registry_ht_node
);
2047 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
2048 published_chunk
= &published_element
->chunk
;
2052 return published_chunk
;
2055 struct lttng_trace_chunk
*lttng_trace_chunk_registry_find_chunk(
2056 const struct lttng_trace_chunk_registry
*registry
, uint64_t session_id
, uint64_t chunk_id
)
2058 return _lttng_trace_chunk_registry_find_chunk(registry
, session_id
, &chunk_id
);
2061 int lttng_trace_chunk_registry_chunk_exists(const struct lttng_trace_chunk_registry
*registry
,
2062 uint64_t session_id
,
2067 lttng_trace_chunk_registry_element target_element
;
2069 target_element
.chunk
.id
.is_set
= true;
2070 target_element
.chunk
.id
.value
= chunk_id
;
2071 target_element
.session_id
= session_id
;
2073 const unsigned long element_hash
= lttng_trace_chunk_registry_element_hash(&target_element
);
2074 struct cds_lfht_node
*published_node
;
2075 struct cds_lfht_iter iter
;
2078 cds_lfht_lookup(registry
->ht
,
2080 lttng_trace_chunk_registry_element_match
,
2083 published_node
= cds_lfht_iter_get_node(&iter
);
2084 if (!published_node
) {
2085 *chunk_exists
= false;
2089 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
2095 struct lttng_trace_chunk
*
2096 lttng_trace_chunk_registry_find_anonymous_chunk(const struct lttng_trace_chunk_registry
*registry
,
2097 uint64_t session_id
)
2099 return _lttng_trace_chunk_registry_find_chunk(registry
, session_id
, NULL
);
2103 lttng_trace_chunk_registry_put_each_chunk(const struct lttng_trace_chunk_registry
*registry
)
2105 struct cds_lfht_iter iter
;
2106 struct lttng_trace_chunk_registry_element
*chunk_element
;
2107 unsigned int trace_chunks_left
= 0;
2109 DBG("Releasing trace chunk registry to all trace chunks");
2111 cds_lfht_for_each_entry (registry
->ht
, &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
2112 const char *chunk_id_str
= "none";
2113 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
2115 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
2116 if (chunk_element
->chunk
.id
.is_set
) {
2119 fmt_ret
= snprintf(chunk_id_buf
,
2120 sizeof(chunk_id_buf
),
2122 chunk_element
->chunk
.id
.value
);
2123 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
2124 chunk_id_str
= "formatting error";
2126 chunk_id_str
= chunk_id_buf
;
2130 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
2131 "chunk_id = %s, name = \"%s\", status = %s",
2132 chunk_element
->session_id
,
2134 chunk_element
->chunk
.name
?: "none",
2135 chunk_element
->chunk
.close_command
.is_set
? "open" : "closed");
2136 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
2137 lttng_trace_chunk_put(&chunk_element
->chunk
);
2138 trace_chunks_left
++;
2141 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
, __FUNCTION__
);
2143 return trace_chunks_left
;