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/urcu.hpp>
25 #include <common/utils.hpp>
27 #include <lttng/constant.h>
33 #include <urcu/rculfhash.h>
37 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
38 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
40 #define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
41 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
43 enum trace_chunk_mode
{
44 TRACE_CHUNK_MODE_USER
,
45 TRACE_CHUNK_MODE_OWNER
,
49 * Callback to invoke on release of a trace chunk. Note that there is no
50 * need to 'lock' the trace chunk during the execution of these callbacks
51 * since only one thread may access a chunk during its destruction (the last
52 * to release its reference to the chunk).
54 using chunk_command
= int (*)(struct lttng_trace_chunk
*);
56 /* Move a completed trace chunk to the 'completed' trace archive folder. */
57 static int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk
*trace_chunk
);
59 static int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
);
60 /* Unlink old chunk files. */
61 static int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk
*trace_chunk
);
62 static enum lttng_trace_chunk_status
63 lttng_trace_chunk_rename_path_no_lock(struct lttng_trace_chunk
*chunk
, const char *path
);
66 struct chunk_credentials
{
67 bool use_current_user
;
68 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
;
124 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
125 struct lttng_trace_chunk_registry_element
{
126 struct lttng_trace_chunk chunk
;
128 /* Weak and only set when added. */
129 struct lttng_trace_chunk_registry
*registry
;
130 struct cds_lfht_node trace_chunk_registry_ht_node
;
131 /* call_rcu delayed reclaim. */
132 struct rcu_head rcu_node
;
136 struct lttng_trace_chunk_registry
{
141 struct fs_handle_untracked
{
142 struct fs_handle parent
;
145 struct lttng_directory_handle
*directory_handle
;
151 static int fs_handle_untracked_get_fd(struct fs_handle
*handle
);
152 static void fs_handle_untracked_put_fd(struct fs_handle
*handle
);
153 static int fs_handle_untracked_unlink(struct fs_handle
*handle
);
154 static int fs_handle_untracked_close(struct fs_handle
*handle
);
156 static const char *lttng_trace_chunk_command_type_str(lttng_trace_chunk_command_type type
)
159 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
160 return "move to completed chunk folder";
161 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
162 return "no operation";
163 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
165 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
:
172 static chunk_command
close_command_get_post_release_func(lttng_trace_chunk_command_type type
)
175 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
176 return lttng_trace_chunk_move_to_completed_post_release
;
177 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
178 return lttng_trace_chunk_no_operation
;
179 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
180 return lttng_trace_chunk_delete_post_release
;
181 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
:
188 static struct fs_handle
*fs_handle_untracked_create(struct lttng_directory_handle
*directory_handle
,
192 struct fs_handle_untracked
*handle
= nullptr;
193 bool reference_acquired
;
194 char *path_copy
= strdup(path
);
196 LTTNG_ASSERT(fd
>= 0);
198 PERROR("Failed to copy file path while creating untracked filesystem handle");
202 handle
= zmalloc
<fs_handle_untracked
>();
204 PERROR("Failed to allocate untracked filesystem handle");
208 handle
->parent
= (typeof(handle
->parent
)){
209 .get_fd
= fs_handle_untracked_get_fd
,
210 .put_fd
= fs_handle_untracked_put_fd
,
211 .unlink
= fs_handle_untracked_unlink
,
212 .close
= fs_handle_untracked_close
,
216 reference_acquired
= lttng_directory_handle_get(directory_handle
);
217 LTTNG_ASSERT(reference_acquired
);
218 handle
->location
.directory_handle
= directory_handle
;
219 /* Ownership is transferred. */
220 handle
->location
.path
= path_copy
;
224 return handle
? &handle
->parent
: nullptr;
227 static int fs_handle_untracked_get_fd(struct fs_handle
*_handle
)
229 struct fs_handle_untracked
*handle
=
230 lttng::utils::container_of(_handle
, &fs_handle_untracked::parent
);
235 static void fs_handle_untracked_put_fd(struct fs_handle
*_handle
__attribute__((unused
)))
240 static int fs_handle_untracked_unlink(struct fs_handle
*_handle
)
242 struct fs_handle_untracked
*handle
=
243 lttng::utils::container_of(_handle
, &fs_handle_untracked::parent
);
245 return lttng_directory_handle_unlink_file(handle
->location
.directory_handle
,
246 handle
->location
.path
);
249 static void fs_handle_untracked_destroy(struct fs_handle_untracked
*handle
)
251 lttng_directory_handle_put(handle
->location
.directory_handle
);
252 free(handle
->location
.path
);
256 static int fs_handle_untracked_close(struct fs_handle
*_handle
)
258 struct fs_handle_untracked
*handle
=
259 lttng::utils::container_of(_handle
, &fs_handle_untracked::parent
);
260 int ret
= close(handle
->fd
);
262 fs_handle_untracked_destroy(handle
);
267 lttng_trace_chunk_registry_element_equals(const struct lttng_trace_chunk_registry_element
*a
,
268 const struct lttng_trace_chunk_registry_element
*b
)
270 if (a
->session_id
!= b
->session_id
) {
273 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
276 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
284 static int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
, const void *key
)
286 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
288 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
289 element_b
= caa_container_of(node
, typeof(*element_b
), trace_chunk_registry_ht_node
);
290 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
294 lttng_trace_chunk_registry_element_hash(const struct lttng_trace_chunk_registry_element
*element
)
296 unsigned long hash
= hash_key_u64(&element
->session_id
, lttng_ht_seed
);
298 if (element
->chunk
.id
.is_set
) {
299 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
306 generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
, const time_t *close_timestamp
)
309 char *new_name
= nullptr;
310 char start_datetime
[ISO8601_STR_LEN
] = {};
311 /* Add 1 for a '-' prefix. */
312 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
314 ret
= time_to_iso8601_str(creation_timestamp
, start_datetime
, sizeof(start_datetime
));
316 ERR("Failed to format trace chunk start date time");
319 if (close_timestamp
) {
320 *end_datetime_suffix
= '-';
321 ret
= time_to_iso8601_str(
322 *close_timestamp
, end_datetime_suffix
+ 1, sizeof(end_datetime_suffix
) - 1);
324 ERR("Failed to format trace chunk end date time");
328 new_name
= calloc
<char>(GENERATED_CHUNK_NAME_LEN
);
330 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
333 ret
= snprintf(new_name
,
334 GENERATED_CHUNK_NAME_LEN
,
339 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
340 ERR("Failed to format trace chunk name");
350 static void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
352 urcu_ref_init(&chunk
->ref
);
353 pthread_mutex_init(&chunk
->lock
, nullptr);
354 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
355 lttng_dynamic_pointer_array_init(&chunk
->files
, free
);
358 static void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
360 if (chunk
->session_output_directory
) {
361 lttng_directory_handle_put(chunk
->session_output_directory
);
362 chunk
->session_output_directory
= nullptr;
364 if (chunk
->chunk_directory
) {
365 lttng_directory_handle_put(chunk
->chunk_directory
);
366 chunk
->chunk_directory
= nullptr;
369 chunk
->name
= nullptr;
371 chunk
->path
= nullptr;
372 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
373 lttng_dynamic_pointer_array_reset(&chunk
->files
);
374 pthread_mutex_destroy(&chunk
->lock
);
377 static struct lttng_trace_chunk
*lttng_trace_chunk_allocate()
379 struct lttng_trace_chunk
*chunk
= nullptr;
381 chunk
= zmalloc
<lttng_trace_chunk
>();
383 ERR("Failed to allocate trace chunk");
386 lttng_trace_chunk_init(chunk
);
391 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous()
393 DBG("Creating anonymous trace chunk");
394 return lttng_trace_chunk_allocate();
397 struct lttng_trace_chunk
*
398 lttng_trace_chunk_create(uint64_t chunk_id
, time_t chunk_creation_time
, const char *path
)
400 struct lttng_trace_chunk
*chunk
;
401 char chunk_creation_datetime_buf
[16] = {};
402 const char *chunk_creation_datetime_str
= "(formatting error)";
403 struct tm timeinfo_buf
, *timeinfo
;
405 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
409 /* Don't fail because of this; it is only used for logging. */
410 strftime_ret
= strftime(chunk_creation_datetime_buf
,
411 sizeof(chunk_creation_datetime_buf
),
415 chunk_creation_datetime_str
= chunk_creation_datetime_buf
;
419 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
421 chunk_creation_datetime_str
);
422 chunk
= lttng_trace_chunk_allocate();
427 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
428 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
430 chunk
->name
= generate_chunk_name(chunk_id
, chunk_creation_time
, nullptr);
432 ERR("Failed to allocate trace chunk name storage");
437 chunk
->path
= strdup(path
);
443 chunk
->path
= strdup(chunk
->name
);
450 DBG("Chunk name set to \"%s\"", chunk
->name
?: "(none)");
454 lttng_trace_chunk_put(chunk
);
458 void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk
*chunk
,
459 struct fd_tracker
*fd_tracker
)
461 LTTNG_ASSERT(!chunk
->session_output_directory
);
462 LTTNG_ASSERT(!chunk
->chunk_directory
);
463 LTTNG_ASSERT(lttng_dynamic_pointer_array_get_count(&chunk
->files
) == 0);
464 chunk
->fd_tracker
= fd_tracker
;
467 struct lttng_trace_chunk
*lttng_trace_chunk_copy(struct lttng_trace_chunk
*source_chunk
)
469 struct lttng_trace_chunk
*new_chunk
= lttng_trace_chunk_allocate();
475 pthread_mutex_lock(&source_chunk
->lock
);
477 * A new chunk is always a user; it shall create no new trace
480 new_chunk
->mode
= (typeof(new_chunk
->mode
)){
482 .value
= TRACE_CHUNK_MODE_USER
,
485 * top_level_directories is not copied as it is never used
486 * by _user_ mode chunks.
488 /* The new chunk is not part of a registry (yet, at least). */
489 new_chunk
->in_registry_element
= false;
490 new_chunk
->name_overridden
= source_chunk
->name_overridden
;
491 if (source_chunk
->name
) {
492 new_chunk
->name
= strdup(source_chunk
->name
);
493 if (!new_chunk
->name
) {
494 ERR("Failed to copy source trace chunk name in %s()", __FUNCTION__
);
498 if (source_chunk
->path
) {
499 new_chunk
->path
= strdup(source_chunk
->path
);
500 if (!new_chunk
->path
) {
501 ERR("Failed to copy source trace chunk path in %s()", __FUNCTION__
);
504 new_chunk
->id
= source_chunk
->id
;
505 new_chunk
->timestamp_creation
= source_chunk
->timestamp_creation
;
506 new_chunk
->timestamp_close
= source_chunk
->timestamp_close
;
507 new_chunk
->credentials
= source_chunk
->credentials
;
508 if (source_chunk
->session_output_directory
) {
509 const bool reference_acquired
=
510 lttng_directory_handle_get(source_chunk
->session_output_directory
);
512 LTTNG_ASSERT(reference_acquired
);
513 new_chunk
->session_output_directory
= source_chunk
->session_output_directory
;
515 if (source_chunk
->chunk_directory
) {
516 const bool reference_acquired
=
517 lttng_directory_handle_get(source_chunk
->chunk_directory
);
519 LTTNG_ASSERT(reference_acquired
);
520 new_chunk
->chunk_directory
= source_chunk
->chunk_directory
;
522 new_chunk
->close_command
= source_chunk
->close_command
;
523 new_chunk
->fd_tracker
= source_chunk
->fd_tracker
;
524 pthread_mutex_unlock(&source_chunk
->lock
);
528 pthread_mutex_unlock(&source_chunk
->lock
);
529 lttng_trace_chunk_put(new_chunk
);
533 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(struct lttng_trace_chunk
*chunk
,
536 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
538 pthread_mutex_lock(&chunk
->lock
);
539 if (chunk
->id
.is_set
) {
540 *id
= chunk
->id
.value
;
542 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
544 pthread_mutex_unlock(&chunk
->lock
);
548 enum lttng_trace_chunk_status
549 lttng_trace_chunk_get_creation_timestamp(struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
552 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
554 pthread_mutex_lock(&chunk
->lock
);
555 if (chunk
->timestamp_creation
.is_set
) {
556 *creation_ts
= chunk
->timestamp_creation
.value
;
558 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
560 pthread_mutex_unlock(&chunk
->lock
);
564 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_timestamp(struct lttng_trace_chunk
*chunk
,
567 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
569 pthread_mutex_lock(&chunk
->lock
);
570 if (chunk
->timestamp_close
.is_set
) {
571 *close_ts
= chunk
->timestamp_close
.value
;
573 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
575 pthread_mutex_unlock(&chunk
->lock
);
579 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(struct lttng_trace_chunk
*chunk
,
582 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
584 pthread_mutex_lock(&chunk
->lock
);
585 if (!chunk
->timestamp_creation
.is_set
) {
586 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
587 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
592 * Note: we do not enforce that the closing timestamp be greater or
593 * equal to the begin timestamp. These timestamps are used for
594 * generating the chunk name and should only be used in context where
595 * the monotonicity of time is not important. The source of those
596 * timestamps is NOT monotonic and represent the system calendar time,
597 * also know as the wall time.
599 if (chunk
->timestamp_creation
.value
> close_ts
) {
600 WARN("Set trace chunk close timestamp: close timestamp is before creation timestamp, begin : %ld, close : %ld",
601 chunk
->timestamp_creation
.value
,
605 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
606 if (!chunk
->name_overridden
) {
608 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
609 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
612 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
616 pthread_mutex_unlock(&chunk
->lock
);
620 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(struct lttng_trace_chunk
*chunk
,
622 bool *name_overridden
)
624 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
626 pthread_mutex_lock(&chunk
->lock
);
627 if (name_overridden
) {
628 *name_overridden
= chunk
->name_overridden
;
631 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
636 pthread_mutex_unlock(&chunk
->lock
);
640 bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk
*chunk
)
642 bool name_overridden
;
644 pthread_mutex_lock(&chunk
->lock
);
645 name_overridden
= chunk
->name_overridden
;
646 pthread_mutex_unlock(&chunk
->lock
);
647 return name_overridden
;
650 static bool is_valid_chunk_name(const char *name
)
658 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
659 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
663 if (strchr(name
, '/') || strchr(name
, '.')) {
670 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(struct lttng_trace_chunk
*chunk
,
674 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
675 char *new_name
, *new_path
;
677 DBG("Override trace chunk name from %s to %s", chunk
->name
, name
);
678 if (!is_valid_chunk_name(name
)) {
679 ERR("Attempted to set an invalid name on a trace chunk: name = %s", name
?: "NULL");
680 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
684 pthread_mutex_lock(&chunk
->lock
);
685 if (!chunk
->id
.is_set
) {
686 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
688 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
692 new_name
= strdup(name
);
694 ERR("Failed to allocate new trace chunk name");
695 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
699 chunk
->name
= new_name
;
701 new_path
= strdup(name
);
703 ERR("Failed to allocate new trace chunk path");
704 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
708 chunk
->path
= new_path
;
710 chunk
->name_overridden
= true;
712 pthread_mutex_unlock(&chunk
->lock
);
717 static enum lttng_trace_chunk_status
718 lttng_trace_chunk_rename_path_no_lock(struct lttng_trace_chunk
*chunk
, const char *path
)
721 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
722 struct lttng_directory_handle
*rename_directory
= nullptr;
723 char *new_path
, *old_path
;
726 if (chunk
->name_overridden
) {
727 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
731 old_path
= chunk
->path
;
732 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path
, path
);
734 if ((!old_path
&& !path
) || (old_path
&& path
&& !strcmp(old_path
, path
))) {
738 * Use chunk name as path if NULL path is specified.
744 /* Renaming from "" to "" is not accepted. */
745 if (path
[0] == '\0' && old_path
[0] == '\0') {
746 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
751 * If a rename is performed on a chunk for which the chunk_directory
752 * is not set (yet), or the session_output_directory is not set
753 * (interacting with a relay daemon), there is no rename to perform.
755 if (!chunk
->chunk_directory
|| !chunk
->session_output_directory
) {
759 if (old_path
&& old_path
[0] != '\0' && path
[0] != '\0') {
760 /* Rename chunk directory. */
761 ret
= lttng_directory_handle_rename_as_user(
762 chunk
->session_output_directory
,
764 chunk
->session_output_directory
,
766 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
768 &chunk
->credentials
.value
.user
);
770 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
773 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
776 rename_directory
= chunk
->fd_tracker
?
777 fd_tracker_create_directory_handle_from_handle(
778 chunk
->fd_tracker
, chunk
->session_output_directory
, path
) :
779 lttng_directory_handle_create_from_handle(path
,
780 chunk
->session_output_directory
);
781 if (!rename_directory
) {
782 ERR("Failed to get handle to trace chunk rename directory");
783 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
787 /* Release old handle. */
788 lttng_directory_handle_put(chunk
->chunk_directory
);
790 * Transfer new handle reference to chunk as the current chunk
793 chunk
->chunk_directory
= rename_directory
;
794 rename_directory
= nullptr;
795 } else if (old_path
&& old_path
[0] == '\0') {
797 count
= lttng_dynamic_pointer_array_get_count(
798 &chunk
->top_level_directories
);
800 ret
= lttng_directory_handle_create_subdirectory_as_user(
801 chunk
->session_output_directory
,
804 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
806 &chunk
->credentials
.value
.user
);
808 PERROR("Failed to create trace chunk rename directory \"%s\"", path
);
809 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
813 rename_directory
= lttng_directory_handle_create_from_handle(
814 path
, chunk
->session_output_directory
);
815 if (!rename_directory
) {
816 ERR("Failed to get handle to trace chunk rename directory");
817 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
821 /* Move toplevel directories. */
822 for (i
= 0; i
< count
; i
++) {
823 const char *top_level_name
=
824 (const char *) lttng_dynamic_pointer_array_get_pointer(
825 &chunk
->top_level_directories
, i
);
827 ret
= lttng_directory_handle_rename_as_user(
828 chunk
->chunk_directory
,
832 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
834 &chunk
->credentials
.value
.user
);
836 PERROR("Failed to move \"%s\" to trace chunk rename directory",
838 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
842 /* Release old handle. */
843 lttng_directory_handle_put(chunk
->chunk_directory
);
845 * Transfer new handle reference to chunk as the current chunk
848 chunk
->chunk_directory
= rename_directory
;
849 rename_directory
= nullptr;
850 } else if (old_path
) {
852 count
= lttng_dynamic_pointer_array_get_count(
853 &chunk
->top_level_directories
);
854 const bool reference_acquired
=
855 lttng_directory_handle_get(chunk
->session_output_directory
);
857 LTTNG_ASSERT(reference_acquired
);
858 rename_directory
= chunk
->session_output_directory
;
860 /* Move toplevel directories. */
861 for (i
= 0; i
< count
; i
++) {
862 const char *top_level_name
=
863 (const char *) lttng_dynamic_pointer_array_get_pointer(
864 &chunk
->top_level_directories
, i
);
866 ret
= lttng_directory_handle_rename_as_user(
867 chunk
->chunk_directory
,
871 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
873 &chunk
->credentials
.value
.user
);
875 PERROR("Failed to move \"%s\" to trace chunk rename directory",
877 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
881 /* Release old handle. */
882 lttng_directory_handle_put(chunk
->chunk_directory
);
884 * Transfer new handle reference to chunk as the current chunk
887 chunk
->chunk_directory
= rename_directory
;
888 rename_directory
= nullptr;
890 /* Remove old directory. */
891 status
= (lttng_trace_chunk_status
) lttng_directory_handle_remove_subdirectory(
892 chunk
->session_output_directory
, old_path
);
893 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
894 ERR("Error removing subdirectory '%s' file when deleting chunk", old_path
);
898 /* Unexpected !old_path && !path. */
899 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
904 new_path
= strdup(path
);
906 ERR("Failed to allocate new trace chunk path");
907 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
911 chunk
->path
= new_path
;
913 lttng_directory_handle_put(rename_directory
);
917 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path(struct lttng_trace_chunk
*chunk
,
921 enum lttng_trace_chunk_status status
;
923 pthread_mutex_lock(&chunk
->lock
);
924 status
= lttng_trace_chunk_rename_path_no_lock(chunk
, path
);
925 pthread_mutex_unlock(&chunk
->lock
);
930 enum lttng_trace_chunk_status
931 lttng_trace_chunk_get_credentials(struct lttng_trace_chunk
*chunk
,
932 struct lttng_credentials
*credentials
)
934 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
936 pthread_mutex_lock(&chunk
->lock
);
937 if (chunk
->credentials
.is_set
) {
938 if (chunk
->credentials
.value
.use_current_user
) {
939 LTTNG_OPTIONAL_SET(&credentials
->uid
, geteuid());
940 LTTNG_OPTIONAL_SET(&credentials
->gid
, getegid());
942 *credentials
= chunk
->credentials
.value
.user
;
945 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
947 pthread_mutex_unlock(&chunk
->lock
);
951 enum lttng_trace_chunk_status
952 lttng_trace_chunk_set_credentials(struct lttng_trace_chunk
*chunk
,
953 const struct lttng_credentials
*user_credentials
)
955 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
956 const struct chunk_credentials credentials
= {
957 .use_current_user
= false,
958 .user
= *user_credentials
,
961 pthread_mutex_lock(&chunk
->lock
);
962 if (chunk
->credentials
.is_set
) {
963 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
966 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
968 pthread_mutex_unlock(&chunk
->lock
);
972 enum lttng_trace_chunk_status
973 lttng_trace_chunk_set_credentials_current_user(struct lttng_trace_chunk
*chunk
)
975 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
976 const struct chunk_credentials credentials
= {
977 .use_current_user
= true,
981 pthread_mutex_lock(&chunk
->lock
);
982 if (chunk
->credentials
.is_set
) {
983 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
986 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
988 pthread_mutex_unlock(&chunk
->lock
);
992 enum lttng_trace_chunk_status
993 lttng_trace_chunk_set_as_owner(struct lttng_trace_chunk
*chunk
,
994 struct lttng_directory_handle
*session_output_directory
)
997 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
998 struct lttng_directory_handle
*chunk_directory_handle
= nullptr;
999 bool reference_acquired
;
1001 pthread_mutex_lock(&chunk
->lock
);
1002 if (chunk
->mode
.is_set
) {
1003 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1006 if (!chunk
->credentials
.is_set
) {
1008 * Fatal error, credentials must be set before a
1009 * directory is created.
1011 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
1012 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1015 if (chunk
->path
&& chunk
->path
[0] != '\0') {
1016 ret
= lttng_directory_handle_create_subdirectory_as_user(
1017 session_output_directory
,
1020 !chunk
->credentials
.value
.use_current_user
?
1021 &chunk
->credentials
.value
.user
:
1024 PERROR("Failed to create chunk output directory \"%s\"", chunk
->path
);
1025 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1028 chunk_directory_handle
= chunk
->fd_tracker
?
1029 fd_tracker_create_directory_handle_from_handle(
1030 chunk
->fd_tracker
, session_output_directory
, chunk
->path
) :
1031 lttng_directory_handle_create_from_handle(chunk
->path
,
1032 session_output_directory
);
1033 if (!chunk_directory_handle
) {
1034 /* The function already logs on all error paths. */
1035 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1040 * A nameless chunk does not need its own output directory.
1041 * The session's output directory will be used.
1043 reference_acquired
= lttng_directory_handle_get(session_output_directory
);
1045 LTTNG_ASSERT(reference_acquired
);
1046 chunk_directory_handle
= session_output_directory
;
1048 chunk
->chunk_directory
= chunk_directory_handle
;
1049 chunk_directory_handle
= nullptr;
1050 reference_acquired
= lttng_directory_handle_get(session_output_directory
);
1051 LTTNG_ASSERT(reference_acquired
);
1052 chunk
->session_output_directory
= session_output_directory
;
1053 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
1055 pthread_mutex_unlock(&chunk
->lock
);
1059 enum lttng_trace_chunk_status
1060 lttng_trace_chunk_set_as_user(struct lttng_trace_chunk
*chunk
,
1061 struct lttng_directory_handle
*chunk_directory
)
1063 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1064 bool reference_acquired
;
1066 pthread_mutex_lock(&chunk
->lock
);
1067 if (chunk
->mode
.is_set
) {
1068 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1071 if (!chunk
->credentials
.is_set
) {
1072 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
1073 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1076 reference_acquired
= lttng_directory_handle_get(chunk_directory
);
1077 LTTNG_ASSERT(reference_acquired
);
1078 chunk
->chunk_directory
= chunk_directory
;
1079 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
1081 pthread_mutex_unlock(&chunk
->lock
);
1085 enum lttng_trace_chunk_status
1086 lttng_trace_chunk_get_session_output_directory_handle(struct lttng_trace_chunk
*chunk
,
1087 struct lttng_directory_handle
**handle
)
1089 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1091 pthread_mutex_lock(&chunk
->lock
);
1092 if (!chunk
->session_output_directory
) {
1093 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1097 const bool reference_acquired
=
1098 lttng_directory_handle_get(chunk
->session_output_directory
);
1100 LTTNG_ASSERT(reference_acquired
);
1101 *handle
= chunk
->session_output_directory
;
1104 pthread_mutex_unlock(&chunk
->lock
);
1108 enum lttng_trace_chunk_status
1109 lttng_trace_chunk_borrow_chunk_directory_handle(struct lttng_trace_chunk
*chunk
,
1110 const struct lttng_directory_handle
**handle
)
1112 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1114 pthread_mutex_lock(&chunk
->lock
);
1115 if (!chunk
->chunk_directory
) {
1116 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1120 *handle
= chunk
->chunk_directory
;
1122 pthread_mutex_unlock(&chunk
->lock
);
1126 /* Add a top-level directory to the trace chunk if it was previously unknown. */
1127 static int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
, const char *new_path
)
1131 size_t i
, count
= lttng_dynamic_pointer_array_get_count(&chunk
->top_level_directories
);
1132 const char *new_path_separator_pos
= strchr(new_path
, '/');
1133 const ptrdiff_t new_path_top_level_len
=
1134 new_path_separator_pos
? new_path_separator_pos
- new_path
: strlen(new_path
);
1136 for (i
= 0; i
< count
; i
++) {
1137 const char *path
= (const char *) lttng_dynamic_pointer_array_get_pointer(
1138 &chunk
->top_level_directories
, i
);
1139 const ptrdiff_t path_top_level_len
= strlen(path
);
1141 if (path_top_level_len
!= new_path_top_level_len
) {
1144 if (!strncmp(path
, new_path
, path_top_level_len
)) {
1151 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
1153 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1155 chunk
->name
?: "(unnamed)");
1157 PERROR("Failed to copy path");
1161 ret
= lttng_dynamic_pointer_array_add_pointer(&chunk
->top_level_directories
, copy
);
1163 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1172 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(struct lttng_trace_chunk
*chunk
,
1176 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1178 DBG("Creating trace chunk subdirectory \"%s\"", path
);
1179 pthread_mutex_lock(&chunk
->lock
);
1180 if (!chunk
->credentials
.is_set
) {
1182 * Fatal error, credentials must be set before a
1183 * directory is created.
1185 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1187 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1190 if (!chunk
->mode
.is_set
|| chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
1191 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1193 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1196 if (!chunk
->chunk_directory
) {
1197 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1199 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1203 ERR("Refusing to create absolute trace chunk directory \"%s\"", path
);
1204 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1207 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
1208 chunk
->chunk_directory
,
1211 chunk
->credentials
.value
.use_current_user
? nullptr :
1212 &chunk
->credentials
.value
.user
);
1214 PERROR("Failed to create trace chunk subdirectory \"%s\"", path
);
1215 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1218 ret
= add_top_level_directory_unique(chunk
, path
);
1220 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1224 pthread_mutex_unlock(&chunk
->lock
);
1229 * TODO: Implement O(1) lookup.
1232 lttng_trace_chunk_find_file(struct lttng_trace_chunk
*chunk
, const char *path
, size_t *index
)
1236 count
= lttng_dynamic_pointer_array_get_count(&chunk
->files
);
1237 for (i
= 0; i
< count
; i
++) {
1238 const char *iter_path
=
1239 (const char *) lttng_dynamic_pointer_array_get_pointer(&chunk
->files
, i
);
1240 if (!strcmp(iter_path
, path
)) {
1250 static enum lttng_trace_chunk_status
lttng_trace_chunk_add_file(struct lttng_trace_chunk
*chunk
,
1255 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1257 if (lttng_trace_chunk_find_file(chunk
, path
, nullptr)) {
1258 return LTTNG_TRACE_CHUNK_STATUS_OK
;
1260 DBG("Adding new file \"%s\" to trace chunk \"%s\"", path
, chunk
->name
?: "(unnamed)");
1261 copy
= strdup(path
);
1263 PERROR("Failed to copy path");
1264 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1267 ret
= lttng_dynamic_pointer_array_add_pointer(&chunk
->files
, copy
);
1269 ERR("Allocation failure while adding file to a trace chunk");
1271 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1278 static void lttng_trace_chunk_remove_file(struct lttng_trace_chunk
*chunk
, const char *path
)
1284 found
= lttng_trace_chunk_find_file(chunk
, path
, &index
);
1288 ret
= lttng_dynamic_pointer_array_remove_pointer(&chunk
->files
, index
);
1292 static enum lttng_trace_chunk_status
1293 _lttng_trace_chunk_open_fs_handle_locked(struct lttng_trace_chunk
*chunk
,
1294 const char *file_path
,
1297 struct fs_handle
**out_handle
,
1298 bool expect_no_file
)
1301 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1303 DBG("Opening trace chunk file \"%s\"", file_path
);
1304 if (!chunk
->credentials
.is_set
) {
1306 * Fatal error, credentials must be set before a
1309 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1311 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1314 if (!chunk
->chunk_directory
) {
1315 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1317 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1320 status
= lttng_trace_chunk_add_file(chunk
, file_path
);
1321 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1324 if (chunk
->fd_tracker
) {
1325 LTTNG_ASSERT(chunk
->credentials
.value
.use_current_user
);
1326 *out_handle
= fd_tracker_open_fs_handle(
1327 chunk
->fd_tracker
, chunk
->chunk_directory
, file_path
, flags
, &mode
);
1328 ret
= *out_handle
? 0 : -1;
1330 ret
= lttng_directory_handle_open_file_as_user(
1331 chunk
->chunk_directory
,
1335 chunk
->credentials
.value
.use_current_user
? nullptr :
1336 &chunk
->credentials
.value
.user
);
1339 fs_handle_untracked_create(chunk
->chunk_directory
, file_path
, ret
);
1341 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1347 if (errno
== ENOENT
&& expect_no_file
) {
1348 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
1350 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1354 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1356 lttng_trace_chunk_remove_file(chunk
, file_path
);
1363 enum lttng_trace_chunk_status
lttng_trace_chunk_open_fs_handle(struct lttng_trace_chunk
*chunk
,
1364 const char *file_path
,
1367 struct fs_handle
**out_handle
,
1368 bool expect_no_file
)
1370 enum lttng_trace_chunk_status status
;
1372 pthread_mutex_lock(&chunk
->lock
);
1373 status
= _lttng_trace_chunk_open_fs_handle_locked(
1374 chunk
, file_path
, flags
, mode
, out_handle
, expect_no_file
);
1375 pthread_mutex_unlock(&chunk
->lock
);
1379 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(struct lttng_trace_chunk
*chunk
,
1380 const char *file_path
,
1384 bool expect_no_file
)
1386 enum lttng_trace_chunk_status status
;
1387 struct fs_handle
*fs_handle
;
1389 pthread_mutex_lock(&chunk
->lock
);
1391 * Using this method is never valid when an fd_tracker is being
1392 * used since the resulting file descriptor would not be tracked.
1394 LTTNG_ASSERT(!chunk
->fd_tracker
);
1395 status
= _lttng_trace_chunk_open_fs_handle_locked(
1396 chunk
, file_path
, flags
, mode
, &fs_handle
, expect_no_file
);
1397 pthread_mutex_unlock(&chunk
->lock
);
1399 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
1400 *out_fd
= fs_handle_get_fd(fs_handle
);
1402 * Does not close the fd; we just "unbox" it from the fs_handle.
1404 fs_handle_untracked_destroy(
1405 lttng::utils::container_of(fs_handle
, &fs_handle_untracked::parent
));
1411 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
, const char *file_path
)
1414 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1416 DBG("Unlinking trace chunk file \"%s\"", file_path
);
1417 pthread_mutex_lock(&chunk
->lock
);
1418 if (!chunk
->credentials
.is_set
) {
1420 * Fatal error, credentials must be set before a
1423 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1425 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1428 if (!chunk
->chunk_directory
) {
1429 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1431 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1434 ret
= lttng_directory_handle_unlink_file_as_user(chunk
->chunk_directory
,
1436 chunk
->credentials
.value
.use_current_user
?
1438 &chunk
->credentials
.value
.user
);
1440 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1443 lttng_trace_chunk_remove_file(chunk
, file_path
);
1445 pthread_mutex_unlock(&chunk
->lock
);
1449 static int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk
*chunk
,
1453 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1455 DBG("Recursively removing trace chunk directory \"%s\"", path
);
1456 pthread_mutex_lock(&chunk
->lock
);
1457 if (!chunk
->credentials
.is_set
) {
1459 * Fatal error, credentials must be set before a
1460 * directory is removed.
1462 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1464 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1467 if (!chunk
->chunk_directory
) {
1468 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1470 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1473 ret
= lttng_directory_handle_remove_subdirectory_recursive_as_user(
1474 chunk
->chunk_directory
,
1476 chunk
->credentials
.value
.use_current_user
? nullptr :
1477 &chunk
->credentials
.value
.user
,
1478 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
);
1480 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1484 pthread_mutex_unlock(&chunk
->lock
);
1488 static int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk
*trace_chunk
)
1491 char *archived_chunk_name
= nullptr;
1492 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
1493 const time_t creation_timestamp
= LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
1494 const time_t close_timestamp
= LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
1495 struct lttng_directory_handle
*archived_chunks_directory
= nullptr;
1496 enum lttng_trace_chunk_status status
;
1498 if (!trace_chunk
->mode
.is_set
|| trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
1499 !trace_chunk
->session_output_directory
) {
1501 * This command doesn't need to run if the output is remote
1502 * or if the trace chunk is not owned by this process.
1507 LTTNG_ASSERT(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
1508 LTTNG_ASSERT(!trace_chunk
->name_overridden
);
1509 LTTNG_ASSERT(trace_chunk
->path
);
1511 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
, &close_timestamp
);
1512 if (!archived_chunk_name
) {
1513 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1518 ret
= lttng_directory_handle_create_subdirectory_as_user(
1519 trace_chunk
->session_output_directory
,
1520 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1522 !trace_chunk
->credentials
.value
.use_current_user
?
1523 &trace_chunk
->credentials
.value
.user
:
1526 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1527 "\" directory for archived trace chunks");
1531 archived_chunks_directory
= trace_chunk
->fd_tracker
?
1532 fd_tracker_create_directory_handle_from_handle(
1533 trace_chunk
->fd_tracker
,
1534 trace_chunk
->session_output_directory
,
1535 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
) :
1536 lttng_directory_handle_create_from_handle(DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1537 trace_chunk
->session_output_directory
);
1538 if (!archived_chunks_directory
) {
1539 PERROR("Failed to get handle to archived trace chunks directory");
1545 * Make sure chunk is renamed to old directory if not already done by
1546 * the creation of the next chunk. This happens if a rotation is
1547 * performed while tracing is stopped.
1549 if (!trace_chunk
->path
|| strcmp(trace_chunk
->path
, DEFAULT_CHUNK_TMP_OLD_DIRECTORY
) != 0) {
1550 status
= lttng_trace_chunk_rename_path_no_lock(trace_chunk
,
1551 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1552 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1553 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1559 ret
= lttng_directory_handle_rename_as_user(
1560 trace_chunk
->session_output_directory
,
1562 archived_chunks_directory
,
1563 archived_chunk_name
,
1564 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1566 &trace_chunk
->credentials
.value
.user
);
1568 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1570 archived_chunk_name
);
1574 lttng_directory_handle_put(archived_chunks_directory
);
1575 free(archived_chunk_name
);
1579 static int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
1580 __attribute__((unused
)))
1585 static int lttng_trace_chunk_delete_post_release_user(struct lttng_trace_chunk
*trace_chunk
)
1589 DBG("Trace chunk \"delete\" close command post-release (User)");
1591 /* Unlink all files. */
1592 while (lttng_dynamic_pointer_array_get_count(&trace_chunk
->files
) != 0) {
1593 enum lttng_trace_chunk_status status
;
1597 path
= (const char *) lttng_dynamic_pointer_array_get_pointer(&trace_chunk
->files
,
1599 DBG("Unlink file: %s", path
);
1601 (lttng_trace_chunk_status
) lttng_trace_chunk_unlink_file(trace_chunk
, path
);
1602 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1603 ERR("Error unlinking file '%s' when deleting chunk", path
);
1612 static int lttng_trace_chunk_delete_post_release_owner(struct lttng_trace_chunk
*trace_chunk
)
1614 enum lttng_trace_chunk_status status
;
1618 ret
= lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1623 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1625 LTTNG_ASSERT(trace_chunk
->session_output_directory
);
1626 LTTNG_ASSERT(trace_chunk
->chunk_directory
);
1628 /* Remove empty directories. */
1629 count
= lttng_dynamic_pointer_array_get_count(&trace_chunk
->top_level_directories
);
1631 for (i
= 0; i
< count
; i
++) {
1632 const char *top_level_name
= (const char *) lttng_dynamic_pointer_array_get_pointer(
1633 &trace_chunk
->top_level_directories
, i
);
1635 status
= (lttng_trace_chunk_status
) lttng_trace_chunk_remove_subdirectory_recursive(
1636 trace_chunk
, top_level_name
);
1637 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1638 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1645 lttng_directory_handle_put(trace_chunk
->chunk_directory
);
1646 trace_chunk
->chunk_directory
= nullptr;
1648 if (trace_chunk
->path
&& trace_chunk
->path
[0] != '\0') {
1649 status
= (lttng_trace_chunk_status
)
1650 lttng_directory_handle_remove_subdirectory(
1651 trace_chunk
->session_output_directory
, trace_chunk
->path
);
1652 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1653 ERR("Error removing subdirectory '%s' file when deleting chunk",
1659 free(trace_chunk
->path
);
1660 trace_chunk
->path
= nullptr;
1666 * For local files, session and consumer daemons all run the delete hook. The
1667 * consumer daemons have the list of files to unlink, and technically the
1668 * session daemon is the owner of the chunk. Unlink all files owned by each
1671 static int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk
*trace_chunk
)
1673 if (!trace_chunk
->chunk_directory
) {
1677 if (trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
) {
1678 return lttng_trace_chunk_delete_post_release_owner(trace_chunk
);
1680 return lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1684 enum lttng_trace_chunk_status
1685 lttng_trace_chunk_get_close_command(struct lttng_trace_chunk
*chunk
,
1686 enum lttng_trace_chunk_command_type
*command_type
)
1688 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1690 pthread_mutex_lock(&chunk
->lock
);
1691 if (chunk
->close_command
.is_set
) {
1692 *command_type
= chunk
->close_command
.value
;
1693 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1695 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1697 pthread_mutex_unlock(&chunk
->lock
);
1701 enum lttng_trace_chunk_status
1702 lttng_trace_chunk_set_close_command(struct lttng_trace_chunk
*chunk
,
1703 enum lttng_trace_chunk_command_type close_command
)
1705 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1707 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1708 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1709 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1713 pthread_mutex_lock(&chunk
->lock
);
1714 if (chunk
->close_command
.is_set
) {
1715 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1716 lttng_trace_chunk_command_type_str(chunk
->close_command
.value
),
1717 lttng_trace_chunk_command_type_str(close_command
));
1719 DBG("Setting trace chunk close command to \"%s\"",
1720 lttng_trace_chunk_command_type_str(close_command
));
1723 * Unset close command for no-op for backward compatibility with relayd
1726 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1727 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1729 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1731 pthread_mutex_unlock(&chunk
->lock
);
1736 const char *lttng_trace_chunk_command_type_get_name(enum lttng_trace_chunk_command_type command
)
1739 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1740 return "move to completed trace chunk folder";
1741 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1742 return "no operation";
1743 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1750 bool lttng_trace_chunk_ids_equal(const struct lttng_trace_chunk
*chunk_a
,
1751 const struct lttng_trace_chunk
*chunk_b
)
1755 if (chunk_a
== chunk_b
) {
1760 if (!!chunk_a
^ !!chunk_b
) {
1764 if (chunk_a
->id
.is_set
^ chunk_a
->id
.is_set
) {
1765 /* One id is set and not the other, thus they are not equal. */
1769 if (!chunk_a
->id
.is_set
) {
1770 /* Both ids are unset. */
1773 equal
= chunk_a
->id
.value
== chunk_b
->id
.value
;
1780 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1782 return urcu_ref_get_unless_zero(&chunk
->ref
);
1785 static void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1787 struct lttng_trace_chunk_registry_element
*element
=
1788 lttng::utils::container_of(node
, <tng_trace_chunk_registry_element::rcu_node
);
1793 static void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1795 struct lttng_trace_chunk
*chunk
= lttng::utils::container_of(ref
, <tng_trace_chunk::ref
);
1797 if (chunk
->close_command
.is_set
) {
1798 chunk_command func
=
1799 close_command_get_post_release_func(chunk
->close_command
.value
);
1802 ERR("Trace chunk post-release command %s has failed.",
1803 lttng_trace_chunk_command_type_str(chunk
->close_command
.value
));
1807 if (chunk
->in_registry_element
) {
1808 struct lttng_trace_chunk_registry_element
*element
;
1811 * Release internal chunk attributes immediately and
1812 * only use the deferred `call_rcu` work to reclaim the
1815 * This ensures that file handles are released as soon as
1816 * possible which works around a problem we encounter with PRAM fs
1817 * mounts (and possibly other non-POSIX compliant file systems):
1818 * directories that contain files which are open can't be
1821 * This means that the recording of a snapshot could be
1822 * completed, but that it would be impossible for the user to
1823 * delete it until the deferred clean-up released the file
1824 * handles to its contents.
1826 lttng_trace_chunk_fini(chunk
);
1828 element
= lttng::utils::container_of(chunk
,
1829 <tng_trace_chunk_registry_element::chunk
);
1830 if (element
->registry
) {
1831 lttng::urcu::read_lock_guard read_lock
;
1832 cds_lfht_del(element
->registry
->ht
, &element
->trace_chunk_registry_ht_node
);
1833 call_rcu(&element
->rcu_node
, free_lttng_trace_chunk_registry_element
);
1835 /* Never published, can be free'd immediately. */
1836 free_lttng_trace_chunk_registry_element(&element
->rcu_node
);
1839 /* Not RCU-protected, free immediately. */
1840 lttng_trace_chunk_fini(chunk
);
1845 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1850 LTTNG_ASSERT(chunk
->ref
.refcount
);
1851 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1854 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create()
1856 struct lttng_trace_chunk_registry
*registry
;
1858 registry
= zmalloc
<lttng_trace_chunk_registry
>();
1863 registry
->ht
= cds_lfht_new(
1864 DEFAULT_HT_SIZE
, 1, 0, CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, nullptr);
1865 if (!registry
->ht
) {
1871 lttng_trace_chunk_registry_destroy(registry
);
1875 void lttng_trace_chunk_registry_destroy(struct lttng_trace_chunk_registry
*registry
)
1881 int ret
= cds_lfht_destroy(registry
->ht
, nullptr);
1887 static struct lttng_trace_chunk_registry_element
*
1888 lttng_trace_chunk_registry_element_create_from_chunk(struct lttng_trace_chunk
*chunk
,
1889 uint64_t session_id
)
1891 struct lttng_trace_chunk_registry_element
*element
=
1892 zmalloc
<lttng_trace_chunk_registry_element
>();
1897 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1898 element
->session_id
= session_id
;
1900 element
->chunk
= *chunk
;
1901 lttng_trace_chunk_init(&element
->chunk
);
1902 if (chunk
->session_output_directory
) {
1903 /* Transferred ownership. */
1904 element
->chunk
.session_output_directory
= chunk
->session_output_directory
;
1905 chunk
->session_output_directory
= nullptr;
1907 if (chunk
->chunk_directory
) {
1908 /* Transferred ownership. */
1909 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1910 chunk
->chunk_directory
= nullptr;
1913 * The original chunk becomes invalid; the name and path attributes are
1914 * transferred to the new chunk instance.
1916 chunk
->name
= nullptr;
1917 chunk
->path
= nullptr;
1918 element
->chunk
.fd_tracker
= chunk
->fd_tracker
;
1919 element
->chunk
.in_registry_element
= true;
1924 struct lttng_trace_chunk
*
1925 lttng_trace_chunk_registry_publish_chunk(struct lttng_trace_chunk_registry
*registry
,
1926 uint64_t session_id
,
1927 struct lttng_trace_chunk
*chunk
)
1931 return lttng_trace_chunk_registry_publish_chunk(registry
, session_id
, chunk
, &unused
);
1934 struct lttng_trace_chunk
*
1935 lttng_trace_chunk_registry_publish_chunk(struct lttng_trace_chunk_registry
*registry
,
1936 uint64_t session_id
,
1937 struct lttng_trace_chunk
*chunk
,
1938 bool *previously_published
)
1940 struct lttng_trace_chunk_registry_element
*element
;
1941 unsigned long element_hash
;
1943 pthread_mutex_lock(&chunk
->lock
);
1944 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
, session_id
);
1945 pthread_mutex_unlock(&chunk
->lock
);
1947 lttng::urcu::read_lock_guard read_lock
;
1952 * chunk is now invalid, the only valid operation is a 'put' from the
1956 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1959 struct cds_lfht_node
*published_node
;
1960 struct lttng_trace_chunk
*published_chunk
;
1961 struct lttng_trace_chunk_registry_element
*published_element
;
1963 published_node
= cds_lfht_add_unique(registry
->ht
,
1965 lttng_trace_chunk_registry_element_match
,
1967 &element
->trace_chunk_registry_ht_node
);
1968 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
1969 /* Successfully published the new element. */
1970 element
->registry
= registry
;
1971 /* Acquire a reference for the caller. */
1972 if (lttng_trace_chunk_get(&element
->chunk
)) {
1973 *previously_published
= false;
1977 * Another thread concurrently unpublished the
1978 * trace chunk. This is currently unexpected.
1980 * Re-attempt to publish.
1982 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1988 * An equivalent trace chunk was published before this trace
1989 * chunk. Attempt to acquire a reference to the one that was
1990 * already published and release the reference to the copy we
1991 * created if successful.
1993 published_element
= lttng::utils::container_of(
1995 <tng_trace_chunk_registry_element::trace_chunk_registry_ht_node
);
1996 published_chunk
= &published_element
->chunk
;
1997 if (lttng_trace_chunk_get(published_chunk
)) {
1998 lttng_trace_chunk_put(&element
->chunk
);
1999 element
= published_element
;
2000 *previously_published
= true;
2004 * A reference to the previously published trace chunk could not
2005 * be acquired. Hence, retry to publish our copy of the trace
2010 return element
? &element
->chunk
: nullptr;
2014 * Note that the caller must be registered as an RCU thread.
2015 * However, it does not need to hold the RCU read lock. The RCU read lock is
2016 * acquired to perform the look-up in the registry's hash table and held until
2017 * after a reference to the "found" trace chunk is acquired.
2019 * IOW, holding a reference guarantees the existence of the object for the
2022 static struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
2023 const struct lttng_trace_chunk_registry
*registry
, uint64_t session_id
, uint64_t *chunk_id
)
2025 lttng_trace_chunk_registry_element target_element
{};
2027 target_element
.chunk
.id
.is_set
= !!chunk_id
;
2028 target_element
.chunk
.id
.value
= chunk_id
? *chunk_id
: 0;
2029 target_element
.session_id
= session_id
;
2031 const unsigned long element_hash
= lttng_trace_chunk_registry_element_hash(&target_element
);
2032 struct cds_lfht_node
*published_node
;
2033 struct lttng_trace_chunk_registry_element
*published_element
;
2034 struct lttng_trace_chunk
*published_chunk
= nullptr;
2035 struct cds_lfht_iter iter
;
2037 lttng::urcu::read_lock_guard read_lock
;
2038 cds_lfht_lookup(registry
->ht
,
2040 lttng_trace_chunk_registry_element_match
,
2043 published_node
= cds_lfht_iter_get_node(&iter
);
2044 if (!published_node
) {
2048 published_element
= lttng::utils::container_of(
2049 published_node
, <tng_trace_chunk_registry_element::trace_chunk_registry_ht_node
);
2050 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
2051 published_chunk
= &published_element
->chunk
;
2054 return published_chunk
;
2057 struct lttng_trace_chunk
*lttng_trace_chunk_registry_find_chunk(
2058 const struct lttng_trace_chunk_registry
*registry
, uint64_t session_id
, uint64_t chunk_id
)
2060 return _lttng_trace_chunk_registry_find_chunk(registry
, session_id
, &chunk_id
);
2063 int lttng_trace_chunk_registry_chunk_exists(const struct lttng_trace_chunk_registry
*registry
,
2064 uint64_t session_id
,
2069 lttng_trace_chunk_registry_element target_element
;
2071 target_element
.chunk
.id
.is_set
= true;
2072 target_element
.chunk
.id
.value
= chunk_id
;
2073 target_element
.session_id
= session_id
;
2075 const unsigned long element_hash
= lttng_trace_chunk_registry_element_hash(&target_element
);
2076 struct cds_lfht_node
*published_node
;
2077 struct cds_lfht_iter iter
;
2079 lttng::urcu::read_lock_guard read_lock
;
2080 cds_lfht_lookup(registry
->ht
,
2082 lttng_trace_chunk_registry_element_match
,
2085 published_node
= cds_lfht_iter_get_node(&iter
);
2086 if (!published_node
) {
2087 *chunk_exists
= false;
2091 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
2096 struct lttng_trace_chunk
*
2097 lttng_trace_chunk_registry_find_anonymous_chunk(const struct lttng_trace_chunk_registry
*registry
,
2098 uint64_t session_id
)
2100 return _lttng_trace_chunk_registry_find_chunk(registry
, session_id
, nullptr);
2104 lttng_trace_chunk_registry_put_each_chunk(const struct lttng_trace_chunk_registry
*registry
)
2106 struct cds_lfht_iter iter
;
2107 struct lttng_trace_chunk_registry_element
*chunk_element
;
2108 unsigned int trace_chunks_left
= 0;
2110 DBG("Releasing trace chunk registry to all trace chunks");
2113 lttng::urcu::read_lock_guard read_lock
;
2115 cds_lfht_for_each_entry (
2116 registry
->ht
, &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
2117 const char *chunk_id_str
= "none";
2118 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
2120 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
2121 if (chunk_element
->chunk
.id
.is_set
) {
2124 fmt_ret
= snprintf(chunk_id_buf
,
2125 sizeof(chunk_id_buf
),
2127 chunk_element
->chunk
.id
.value
);
2128 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
2129 chunk_id_str
= "formatting error";
2131 chunk_id_str
= chunk_id_buf
;
2135 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
2136 "chunk_id = %s, name = \"%s\", status = %s",
2137 chunk_element
->session_id
,
2139 chunk_element
->chunk
.name
?: "none",
2140 chunk_element
->chunk
.close_command
.is_set
? "open" : "closed");
2141 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
2142 lttng_trace_chunk_put(&chunk_element
->chunk
);
2143 trace_chunks_left
++;
2147 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
, __FUNCTION__
);
2149 return trace_chunks_left
;