2 * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * SPDX-License-Identifier: LGPL-2.1-only
8 #include <common/compat/directory-handle.hpp>
9 #include <common/credentials.hpp>
10 #include <common/defaults.hpp>
11 #include <common/dynamic-array.hpp>
12 #include <common/error.hpp>
13 #include <common/fd-tracker/fd-tracker.hpp>
14 #include <common/fd-tracker/utils.hpp>
15 #include <common/fs-handle.hpp>
16 #include <common/fs-handle-internal.hpp>
17 #include <common/hashtable/hashtable.hpp>
18 #include <common/hashtable/utils.hpp>
19 #include <common/optional.hpp>
20 #include <common/string-utils/format.hpp>
21 #include <common/time.hpp>
22 #include <common/trace-chunk-registry.hpp>
23 #include <common/trace-chunk.hpp>
24 #include <common/utils.hpp>
25 #include <lttng/constant.h>
31 #include <urcu/rculfhash.h>
35 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
36 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
38 #define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
39 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
41 enum trace_chunk_mode
{
42 TRACE_CHUNK_MODE_USER
,
43 TRACE_CHUNK_MODE_OWNER
,
47 * Callback to invoke on release of a trace chunk. Note that there is no
48 * need to 'lock' the trace chunk during the execution of these callbacks
49 * since only one thread may access a chunk during its destruction (the last
50 * to release its reference to the chunk).
52 typedef int (*chunk_command
)(struct lttng_trace_chunk
*trace_chunk
);
54 /* Move a completed trace chunk to the 'completed' trace archive folder. */
56 int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk
*trace_chunk
);
59 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
);
60 /* Unlink old chunk files. */
62 int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk
*trace_chunk
);
64 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
65 struct lttng_trace_chunk
*chunk
, const char *path
);
68 struct chunk_credentials
{
69 bool use_current_user
;
70 struct lttng_credentials user
;
75 * NOTE: Make sure to update:
76 * - lttng_trace_chunk_copy(),
77 * - lttng_trace_chunk_registry_element_create_from_chunk()
78 * if you modify this structure.
80 struct lttng_trace_chunk
{
83 LTTNG_OPTIONAL(enum trace_chunk_mode
) mode
;
85 * First-level directories created within the trace chunk.
86 * Elements are of type 'char *'.
88 * Only used by _owner_ mode chunks.
90 struct lttng_dynamic_pointer_array top_level_directories
;
92 * All files contained within the trace chunk.
93 * Array of paths (char *).
95 struct lttng_dynamic_pointer_array files
;
96 /* Is contained within an lttng_trace_chunk_registry_element? */
97 bool in_registry_element
;
101 /* An unset id means the chunk is anonymous. */
102 LTTNG_OPTIONAL(uint64_t) id
;
105 * The creation and close timestamps are NOT monotonic.
106 * They must not be used in context were monotonicity is required.
108 LTTNG_OPTIONAL(time_t) timestamp_creation
;
109 LTTNG_OPTIONAL(time_t) timestamp_close
;
111 LTTNG_OPTIONAL(struct chunk_credentials
) credentials
;
112 struct lttng_directory_handle
*session_output_directory
;
113 struct lttng_directory_handle
*chunk_directory
;
114 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type
) close_command
;
116 * fd_tracker instance through which file descriptors should be
119 * An fd_tracker always outlives any trace chunk; there is no
120 * need to perform any reference counting of that object.
122 struct fd_tracker
*fd_tracker
;
126 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
127 struct lttng_trace_chunk_registry_element
{
128 struct lttng_trace_chunk chunk
;
130 /* Weak and only set when added. */
131 struct lttng_trace_chunk_registry
*registry
;
132 struct cds_lfht_node trace_chunk_registry_ht_node
;
133 /* call_rcu delayed reclaim. */
134 struct rcu_head rcu_node
;
138 struct lttng_trace_chunk_registry
{
143 struct fs_handle_untracked
{
144 struct fs_handle parent
;
147 struct lttng_directory_handle
*directory_handle
;
154 int fs_handle_untracked_get_fd(struct fs_handle
*handle
);
156 void fs_handle_untracked_put_fd(struct fs_handle
*handle
);
158 int fs_handle_untracked_unlink(struct fs_handle
*handle
);
160 int fs_handle_untracked_close(struct fs_handle
*handle
);
163 const char *lttng_trace_chunk_command_type_str(
164 lttng_trace_chunk_command_type type
) {
166 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
167 return "move to completed chunk folder";
168 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
169 return "no operation";
170 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
172 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
:
180 chunk_command
close_command_get_post_release_func(
181 lttng_trace_chunk_command_type type
) {
183 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
184 return lttng_trace_chunk_move_to_completed_post_release
;
185 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
186 return lttng_trace_chunk_no_operation
;
187 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
188 return lttng_trace_chunk_delete_post_release
;
189 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
:
197 struct fs_handle
*fs_handle_untracked_create(
198 struct lttng_directory_handle
*directory_handle
,
202 struct fs_handle_untracked
*handle
= NULL
;
203 bool reference_acquired
;
204 char *path_copy
= strdup(path
);
206 LTTNG_ASSERT(fd
>= 0);
208 PERROR("Failed to copy file path while creating untracked filesystem handle");
212 handle
= zmalloc
<fs_handle_untracked
>();
214 PERROR("Failed to allocate untracked filesystem handle");
218 handle
->parent
= (typeof(handle
->parent
)) {
219 .get_fd
= fs_handle_untracked_get_fd
,
220 .put_fd
= fs_handle_untracked_put_fd
,
221 .unlink
= fs_handle_untracked_unlink
,
222 .close
= fs_handle_untracked_close
,
226 reference_acquired
= lttng_directory_handle_get(directory_handle
);
227 LTTNG_ASSERT(reference_acquired
);
228 handle
->location
.directory_handle
= directory_handle
;
229 /* Ownership is transferred. */
230 handle
->location
.path
= path_copy
;
234 return handle
? &handle
->parent
: NULL
;
238 int fs_handle_untracked_get_fd(struct fs_handle
*_handle
)
240 struct fs_handle_untracked
*handle
= lttng::utils::container_of(
241 _handle
, &fs_handle_untracked::parent
);
247 void fs_handle_untracked_put_fd(struct fs_handle
*_handle
__attribute__((unused
)))
253 int fs_handle_untracked_unlink(struct fs_handle
*_handle
)
255 struct fs_handle_untracked
*handle
= lttng::utils::container_of(
256 _handle
, &fs_handle_untracked::parent
);
258 return lttng_directory_handle_unlink_file(
259 handle
->location
.directory_handle
,
260 handle
->location
.path
);
264 void fs_handle_untracked_destroy(struct fs_handle_untracked
*handle
)
266 lttng_directory_handle_put(handle
->location
.directory_handle
);
267 free(handle
->location
.path
);
272 int fs_handle_untracked_close(struct fs_handle
*_handle
)
274 struct fs_handle_untracked
*handle
= lttng::utils::container_of(
275 _handle
, &fs_handle_untracked::parent
);
276 int ret
= close(handle
->fd
);
278 fs_handle_untracked_destroy(handle
);
283 bool lttng_trace_chunk_registry_element_equals(
284 const struct lttng_trace_chunk_registry_element
*a
,
285 const struct lttng_trace_chunk_registry_element
*b
)
287 if (a
->session_id
!= b
->session_id
) {
290 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
293 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
302 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
,
305 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
307 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
308 element_b
= caa_container_of(node
, typeof(*element_b
),
309 trace_chunk_registry_ht_node
);
310 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
314 unsigned long lttng_trace_chunk_registry_element_hash(
315 const struct lttng_trace_chunk_registry_element
*element
)
317 unsigned long hash
= hash_key_u64(&element
->session_id
,
320 if (element
->chunk
.id
.is_set
) {
321 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
328 char *generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
,
329 const time_t *close_timestamp
)
332 char *new_name
= NULL
;
333 char start_datetime
[ISO8601_STR_LEN
] = {};
334 /* Add 1 for a '-' prefix. */
335 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
337 ret
= time_to_iso8601_str(
339 start_datetime
, sizeof(start_datetime
));
341 ERR("Failed to format trace chunk start date time");
344 if (close_timestamp
) {
345 *end_datetime_suffix
= '-';
346 ret
= time_to_iso8601_str(
348 end_datetime_suffix
+ 1,
349 sizeof(end_datetime_suffix
) - 1);
351 ERR("Failed to format trace chunk end date time");
355 new_name
= calloc
<char>(GENERATED_CHUNK_NAME_LEN
);
357 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
360 ret
= snprintf(new_name
, GENERATED_CHUNK_NAME_LEN
, "%s%s-%" PRIu64
,
361 start_datetime
, end_datetime_suffix
, chunk_id
);
362 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
363 ERR("Failed to format trace chunk name");
374 void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
376 urcu_ref_init(&chunk
->ref
);
377 pthread_mutex_init(&chunk
->lock
, NULL
);
378 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
379 lttng_dynamic_pointer_array_init(&chunk
->files
, free
);
383 void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
385 if (chunk
->session_output_directory
) {
386 lttng_directory_handle_put(
387 chunk
->session_output_directory
);
388 chunk
->session_output_directory
= NULL
;
390 if (chunk
->chunk_directory
) {
391 lttng_directory_handle_put(chunk
->chunk_directory
);
392 chunk
->chunk_directory
= NULL
;
398 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
399 lttng_dynamic_pointer_array_reset(&chunk
->files
);
400 pthread_mutex_destroy(&chunk
->lock
);
404 struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
406 struct lttng_trace_chunk
*chunk
= NULL
;
408 chunk
= zmalloc
<lttng_trace_chunk
>();
410 ERR("Failed to allocate trace chunk");
413 lttng_trace_chunk_init(chunk
);
418 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
420 DBG("Creating anonymous trace chunk");
421 return lttng_trace_chunk_allocate();
424 struct lttng_trace_chunk
*lttng_trace_chunk_create(
425 uint64_t chunk_id
, time_t chunk_creation_time
, const char *path
)
427 struct lttng_trace_chunk
*chunk
;
428 char chunk_creation_datetime_buf
[16] = {};
429 const char *chunk_creation_datetime_str
= "(formatting error)";
430 struct tm timeinfo_buf
, *timeinfo
;
432 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
436 /* Don't fail because of this; it is only used for logging. */
437 strftime_ret
= strftime(chunk_creation_datetime_buf
,
438 sizeof(chunk_creation_datetime_buf
),
439 "%Y%m%d-%H%M%S", timeinfo
);
441 chunk_creation_datetime_str
=
442 chunk_creation_datetime_buf
;
446 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
447 chunk_id
, chunk_creation_datetime_str
);
448 chunk
= lttng_trace_chunk_allocate();
453 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
454 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
456 chunk
->name
= generate_chunk_name(chunk_id
,
457 chunk_creation_time
, NULL
);
459 ERR("Failed to allocate trace chunk name storage");
464 chunk
->path
= strdup(path
);
470 chunk
->path
= strdup(chunk
->name
);
477 DBG("Chunk name set to \"%s\"", chunk
->name
? : "(none)");
481 lttng_trace_chunk_put(chunk
);
485 void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk
*chunk
,
486 struct fd_tracker
*fd_tracker
)
488 LTTNG_ASSERT(!chunk
->session_output_directory
);
489 LTTNG_ASSERT(!chunk
->chunk_directory
);
490 LTTNG_ASSERT(lttng_dynamic_pointer_array_get_count(&chunk
->files
) == 0);
491 chunk
->fd_tracker
= fd_tracker
;
494 struct lttng_trace_chunk
*lttng_trace_chunk_copy(
495 struct lttng_trace_chunk
*source_chunk
)
497 struct lttng_trace_chunk
*new_chunk
= lttng_trace_chunk_allocate();
503 pthread_mutex_lock(&source_chunk
->lock
);
505 * A new chunk is always a user; it shall create no new trace
508 new_chunk
->mode
= (typeof(new_chunk
->mode
)) {
510 .value
= TRACE_CHUNK_MODE_USER
,
513 * top_level_directories is not copied as it is never used
514 * by _user_ mode chunks.
516 /* The new chunk is not part of a registry (yet, at least). */
517 new_chunk
->in_registry_element
= false;
518 new_chunk
->name_overridden
= source_chunk
->name_overridden
;
519 if (source_chunk
->name
) {
520 new_chunk
->name
= strdup(source_chunk
->name
);
521 if (!new_chunk
->name
) {
522 ERR("Failed to copy source trace chunk name in %s()",
527 if (source_chunk
->path
) {
528 new_chunk
->path
= strdup(source_chunk
->path
);
529 if (!new_chunk
->path
) {
530 ERR("Failed to copy source trace chunk path in %s()",
534 new_chunk
->id
= source_chunk
->id
;
535 new_chunk
->timestamp_creation
= source_chunk
->timestamp_creation
;
536 new_chunk
->timestamp_close
= source_chunk
->timestamp_close
;
537 new_chunk
->credentials
= source_chunk
->credentials
;
538 if (source_chunk
->session_output_directory
) {
539 const bool reference_acquired
= lttng_directory_handle_get(
540 source_chunk
->session_output_directory
);
542 LTTNG_ASSERT(reference_acquired
);
543 new_chunk
->session_output_directory
=
544 source_chunk
->session_output_directory
;
546 if (source_chunk
->chunk_directory
) {
547 const bool reference_acquired
= lttng_directory_handle_get(
548 source_chunk
->chunk_directory
);
550 LTTNG_ASSERT(reference_acquired
);
551 new_chunk
->chunk_directory
= source_chunk
->chunk_directory
;
553 new_chunk
->close_command
= source_chunk
->close_command
;
554 new_chunk
->fd_tracker
= source_chunk
->fd_tracker
;
555 pthread_mutex_unlock(&source_chunk
->lock
);
559 pthread_mutex_unlock(&source_chunk
->lock
);
560 lttng_trace_chunk_put(new_chunk
);
564 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(
565 struct lttng_trace_chunk
*chunk
, uint64_t *id
)
567 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
569 pthread_mutex_lock(&chunk
->lock
);
570 if (chunk
->id
.is_set
) {
571 *id
= chunk
->id
.value
;
573 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
575 pthread_mutex_unlock(&chunk
->lock
);
579 enum lttng_trace_chunk_status
lttng_trace_chunk_get_creation_timestamp(
580 struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
583 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
585 pthread_mutex_lock(&chunk
->lock
);
586 if (chunk
->timestamp_creation
.is_set
) {
587 *creation_ts
= chunk
->timestamp_creation
.value
;
589 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
591 pthread_mutex_unlock(&chunk
->lock
);
595 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_timestamp(
596 struct lttng_trace_chunk
*chunk
, time_t *close_ts
)
598 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
600 pthread_mutex_lock(&chunk
->lock
);
601 if (chunk
->timestamp_close
.is_set
) {
602 *close_ts
= chunk
->timestamp_close
.value
;
604 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
606 pthread_mutex_unlock(&chunk
->lock
);
610 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(
611 struct lttng_trace_chunk
*chunk
, time_t close_ts
)
613 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
615 pthread_mutex_lock(&chunk
->lock
);
616 if (!chunk
->timestamp_creation
.is_set
) {
617 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
618 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
623 * Note: we do not enforce that the closing timestamp be greater or
624 * equal to the begin timestamp. These timestamps are used for
625 * generating the chunk name and should only be used in context where
626 * the monotonicity of time is not important. The source of those
627 * timestamps is NOT monotonic and represent the system calendar time,
628 * also know as the wall time.
630 if (chunk
->timestamp_creation
.value
> close_ts
) {
631 WARN("Set trace chunk close timestamp: close timestamp is before creation timestamp, begin : %ld, close : %ld",
632 chunk
->timestamp_creation
.value
, close_ts
);
635 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
636 if (!chunk
->name_overridden
) {
638 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
639 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
642 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
646 pthread_mutex_unlock(&chunk
->lock
);
650 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(
651 struct lttng_trace_chunk
*chunk
, const char **name
,
652 bool *name_overridden
)
654 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
656 pthread_mutex_lock(&chunk
->lock
);
657 if (name_overridden
) {
658 *name_overridden
= chunk
->name_overridden
;
661 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
666 pthread_mutex_unlock(&chunk
->lock
);
670 bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk
*chunk
)
672 bool name_overridden
;
674 pthread_mutex_lock(&chunk
->lock
);
675 name_overridden
= chunk
->name_overridden
;
676 pthread_mutex_unlock(&chunk
->lock
);
677 return name_overridden
;
681 bool is_valid_chunk_name(const char *name
)
689 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
690 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
694 if (strchr(name
, '/') || strchr(name
, '.')) {
701 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
702 struct lttng_trace_chunk
*chunk
, const char *name
)
705 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
706 char *new_name
, *new_path
;
708 DBG("Override trace chunk name from %s to %s", chunk
->name
, name
);
709 if (!is_valid_chunk_name(name
)) {
710 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
712 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
716 pthread_mutex_lock(&chunk
->lock
);
717 if (!chunk
->id
.is_set
) {
718 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
720 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
724 new_name
= strdup(name
);
726 ERR("Failed to allocate new trace chunk name");
727 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
731 chunk
->name
= new_name
;
733 new_path
= strdup(name
);
735 ERR("Failed to allocate new trace chunk path");
736 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
740 chunk
->path
= new_path
;
742 chunk
->name_overridden
= true;
744 pthread_mutex_unlock(&chunk
->lock
);
750 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
751 struct lttng_trace_chunk
*chunk
, const char *path
)
754 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
755 struct lttng_directory_handle
*rename_directory
= NULL
;
756 char *new_path
, *old_path
;
759 if (chunk
->name_overridden
) {
760 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
764 old_path
= chunk
->path
;
765 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path
, path
);
767 if ((!old_path
&& !path
) ||
768 (old_path
&& path
&& !strcmp(old_path
, path
))) {
772 * Use chunk name as path if NULL path is specified.
778 /* Renaming from "" to "" is not accepted. */
779 if (path
[0] == '\0' && old_path
[0] == '\0') {
780 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
785 * If a rename is performed on a chunk for which the chunk_directory
786 * is not set (yet), or the session_output_directory is not set
787 * (interacting with a relay daemon), there is no rename to perform.
789 if (!chunk
->chunk_directory
||
790 !chunk
->session_output_directory
) {
794 if (old_path
&& old_path
[0] != '\0' && path
[0] != '\0') {
795 /* Rename chunk directory. */
796 ret
= lttng_directory_handle_rename_as_user(
797 chunk
->session_output_directory
,
799 chunk
->session_output_directory
,
801 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
803 &chunk
->credentials
.value
.user
);
805 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
807 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
810 rename_directory
= chunk
->fd_tracker
?
811 fd_tracker_create_directory_handle_from_handle(
813 chunk
->session_output_directory
,
815 lttng_directory_handle_create_from_handle(
817 chunk
->session_output_directory
);
818 if (!rename_directory
) {
819 ERR("Failed to get handle to trace chunk rename directory");
820 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
824 /* Release old handle. */
825 lttng_directory_handle_put(chunk
->chunk_directory
);
827 * Transfer new handle reference to chunk as the current chunk
830 chunk
->chunk_directory
= rename_directory
;
831 rename_directory
= NULL
;
832 } else if (old_path
&& old_path
[0] == '\0') {
833 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
834 &chunk
->top_level_directories
);
836 ret
= lttng_directory_handle_create_subdirectory_as_user(
837 chunk
->session_output_directory
,
840 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
842 &chunk
->credentials
.value
.user
);
844 PERROR("Failed to create trace chunk rename directory \"%s\"",
846 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
850 rename_directory
= lttng_directory_handle_create_from_handle(
851 path
, chunk
->session_output_directory
);
852 if (!rename_directory
) {
853 ERR("Failed to get handle to trace chunk rename directory");
854 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
858 /* Move toplevel directories. */
859 for (i
= 0; i
< count
; i
++) {
860 const char *top_level_name
=
861 (const char *) lttng_dynamic_pointer_array_get_pointer(
862 &chunk
->top_level_directories
, i
);
864 ret
= lttng_directory_handle_rename_as_user(
865 chunk
->chunk_directory
,
869 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
871 &chunk
->credentials
.value
.user
);
873 PERROR("Failed to move \"%s\" to trace chunk rename directory",
875 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
879 /* Release old handle. */
880 lttng_directory_handle_put(chunk
->chunk_directory
);
882 * Transfer new handle reference to chunk as the current chunk
885 chunk
->chunk_directory
= rename_directory
;
886 rename_directory
= NULL
;
887 } else if (old_path
) {
888 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
889 &chunk
->top_level_directories
);
890 const bool reference_acquired
= lttng_directory_handle_get(
891 chunk
->session_output_directory
);
893 LTTNG_ASSERT(reference_acquired
);
894 rename_directory
= chunk
->session_output_directory
;
896 /* Move toplevel directories. */
897 for (i
= 0; i
< count
; i
++) {
898 const char *top_level_name
=
899 (const char *) lttng_dynamic_pointer_array_get_pointer(
900 &chunk
->top_level_directories
, i
);
902 ret
= lttng_directory_handle_rename_as_user(
903 chunk
->chunk_directory
,
907 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
909 &chunk
->credentials
.value
.user
);
911 PERROR("Failed to move \"%s\" to trace chunk rename directory",
913 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
917 /* Release old handle. */
918 lttng_directory_handle_put(chunk
->chunk_directory
);
920 * Transfer new handle reference to chunk as the current chunk
923 chunk
->chunk_directory
= rename_directory
;
924 rename_directory
= NULL
;
926 /* Remove old directory. */
927 status
= (lttng_trace_chunk_status
) lttng_directory_handle_remove_subdirectory(
928 chunk
->session_output_directory
,
930 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
931 ERR("Error removing subdirectory '%s' file when deleting chunk",
936 /* Unexpected !old_path && !path. */
937 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
942 new_path
= strdup(path
);
944 ERR("Failed to allocate new trace chunk path");
945 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
949 chunk
->path
= new_path
;
951 lttng_directory_handle_put(rename_directory
);
955 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path(
956 struct lttng_trace_chunk
*chunk
, const char *path
)
959 enum lttng_trace_chunk_status status
;
961 pthread_mutex_lock(&chunk
->lock
);
962 status
= lttng_trace_chunk_rename_path_no_lock(chunk
, path
);
963 pthread_mutex_unlock(&chunk
->lock
);
968 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
969 struct lttng_trace_chunk
*chunk
,
970 struct lttng_credentials
*credentials
)
972 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
974 pthread_mutex_lock(&chunk
->lock
);
975 if (chunk
->credentials
.is_set
) {
976 if (chunk
->credentials
.value
.use_current_user
) {
977 LTTNG_OPTIONAL_SET(&credentials
->uid
, geteuid());
978 LTTNG_OPTIONAL_SET(&credentials
->gid
, getegid());
980 *credentials
= chunk
->credentials
.value
.user
;
983 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
985 pthread_mutex_unlock(&chunk
->lock
);
989 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
990 struct lttng_trace_chunk
*chunk
,
991 const struct lttng_credentials
*user_credentials
)
993 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
994 const struct chunk_credentials credentials
= {
995 .use_current_user
= false,
996 .user
= *user_credentials
,
999 pthread_mutex_lock(&chunk
->lock
);
1000 if (chunk
->credentials
.is_set
) {
1001 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1004 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
1006 pthread_mutex_unlock(&chunk
->lock
);
1010 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
1011 struct lttng_trace_chunk
*chunk
)
1013 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1014 const struct chunk_credentials credentials
= {
1015 .use_current_user
= true,
1019 pthread_mutex_lock(&chunk
->lock
);
1020 if (chunk
->credentials
.is_set
) {
1021 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1024 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
1026 pthread_mutex_unlock(&chunk
->lock
);
1031 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
1032 struct lttng_trace_chunk
*chunk
,
1033 struct lttng_directory_handle
*session_output_directory
)
1036 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1037 struct lttng_directory_handle
*chunk_directory_handle
= NULL
;
1038 bool reference_acquired
;
1040 pthread_mutex_lock(&chunk
->lock
);
1041 if (chunk
->mode
.is_set
) {
1042 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1045 if (!chunk
->credentials
.is_set
) {
1047 * Fatal error, credentials must be set before a
1048 * directory is created.
1050 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
1051 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1054 if (chunk
->path
&& chunk
->path
[0] != '\0') {
1055 ret
= lttng_directory_handle_create_subdirectory_as_user(
1056 session_output_directory
,
1059 !chunk
->credentials
.value
.use_current_user
?
1060 &chunk
->credentials
.value
.user
: NULL
);
1062 PERROR("Failed to create chunk output directory \"%s\"",
1064 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1067 chunk_directory_handle
=
1069 fd_tracker_create_directory_handle_from_handle(
1071 session_output_directory
,
1073 lttng_directory_handle_create_from_handle(
1075 session_output_directory
);
1076 if (!chunk_directory_handle
) {
1077 /* The function already logs on all error paths. */
1078 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1083 * A nameless chunk does not need its own output directory.
1084 * The session's output directory will be used.
1086 reference_acquired
= lttng_directory_handle_get(
1087 session_output_directory
);
1089 LTTNG_ASSERT(reference_acquired
);
1090 chunk_directory_handle
= session_output_directory
;
1092 chunk
->chunk_directory
= chunk_directory_handle
;
1093 chunk_directory_handle
= NULL
;
1094 reference_acquired
= lttng_directory_handle_get(
1095 session_output_directory
);
1096 LTTNG_ASSERT(reference_acquired
);
1097 chunk
->session_output_directory
= session_output_directory
;
1098 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
1100 pthread_mutex_unlock(&chunk
->lock
);
1104 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
1105 struct lttng_trace_chunk
*chunk
,
1106 struct lttng_directory_handle
*chunk_directory
)
1108 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1109 bool reference_acquired
;
1111 pthread_mutex_lock(&chunk
->lock
);
1112 if (chunk
->mode
.is_set
) {
1113 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1116 if (!chunk
->credentials
.is_set
) {
1117 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
1118 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1121 reference_acquired
= lttng_directory_handle_get(chunk_directory
);
1122 LTTNG_ASSERT(reference_acquired
);
1123 chunk
->chunk_directory
= chunk_directory
;
1124 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
1126 pthread_mutex_unlock(&chunk
->lock
);
1130 enum lttng_trace_chunk_status
1131 lttng_trace_chunk_get_session_output_directory_handle(
1132 struct lttng_trace_chunk
*chunk
,
1133 struct lttng_directory_handle
**handle
)
1135 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1137 pthread_mutex_lock(&chunk
->lock
);
1138 if (!chunk
->session_output_directory
) {
1139 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1143 const bool reference_acquired
= lttng_directory_handle_get(
1144 chunk
->session_output_directory
);
1146 LTTNG_ASSERT(reference_acquired
);
1147 *handle
= chunk
->session_output_directory
;
1150 pthread_mutex_unlock(&chunk
->lock
);
1154 enum lttng_trace_chunk_status
lttng_trace_chunk_borrow_chunk_directory_handle(
1155 struct lttng_trace_chunk
*chunk
,
1156 const struct lttng_directory_handle
**handle
)
1158 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1160 pthread_mutex_lock(&chunk
->lock
);
1161 if (!chunk
->chunk_directory
) {
1162 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1166 *handle
= chunk
->chunk_directory
;
1168 pthread_mutex_unlock(&chunk
->lock
);
1172 /* Add a top-level directory to the trace chunk if it was previously unknown. */
1174 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
1175 const char *new_path
)
1179 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
1180 &chunk
->top_level_directories
);
1181 const char *new_path_separator_pos
= strchr(new_path
, '/');
1182 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
1183 new_path_separator_pos
- new_path
: strlen(new_path
);
1185 for (i
= 0; i
< count
; i
++) {
1186 const char *path
= (const char *) lttng_dynamic_pointer_array_get_pointer(
1187 &chunk
->top_level_directories
, i
);
1188 const ptrdiff_t path_top_level_len
= strlen(path
);
1190 if (path_top_level_len
!= new_path_top_level_len
) {
1193 if (!strncmp(path
, new_path
, path_top_level_len
)) {
1200 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
1202 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1203 new_path
, chunk
->name
? : "(unnamed)");
1205 PERROR("Failed to copy path");
1209 ret
= lttng_dynamic_pointer_array_add_pointer(
1210 &chunk
->top_level_directories
, copy
);
1212 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1221 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
1222 struct lttng_trace_chunk
*chunk
,
1226 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1228 DBG("Creating trace chunk subdirectory \"%s\"", path
);
1229 pthread_mutex_lock(&chunk
->lock
);
1230 if (!chunk
->credentials
.is_set
) {
1232 * Fatal error, credentials must be set before a
1233 * directory is created.
1235 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1237 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1240 if (!chunk
->mode
.is_set
||
1241 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
1242 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1244 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1247 if (!chunk
->chunk_directory
) {
1248 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1250 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1254 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1256 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1259 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
1260 chunk
->chunk_directory
, path
,
1262 chunk
->credentials
.value
.use_current_user
?
1263 NULL
: &chunk
->credentials
.value
.user
);
1265 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1267 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1270 ret
= add_top_level_directory_unique(chunk
, path
);
1272 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1276 pthread_mutex_unlock(&chunk
->lock
);
1281 * TODO: Implement O(1) lookup.
1284 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk
*chunk
,
1285 const char *path
, size_t *index
)
1289 count
= lttng_dynamic_pointer_array_get_count(&chunk
->files
);
1290 for (i
= 0; i
< count
; i
++) {
1291 const char *iter_path
=
1292 (const char *) lttng_dynamic_pointer_array_get_pointer(
1294 if (!strcmp(iter_path
, path
)) {
1305 enum lttng_trace_chunk_status
lttng_trace_chunk_add_file(
1306 struct lttng_trace_chunk
*chunk
,
1311 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1313 if (lttng_trace_chunk_find_file(chunk
, path
, NULL
)) {
1314 return LTTNG_TRACE_CHUNK_STATUS_OK
;
1316 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1317 path
, chunk
->name
? : "(unnamed)");
1318 copy
= strdup(path
);
1320 PERROR("Failed to copy path");
1321 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1324 ret
= lttng_dynamic_pointer_array_add_pointer(
1325 &chunk
->files
, copy
);
1327 ERR("Allocation failure while adding file to a trace chunk");
1329 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1337 void lttng_trace_chunk_remove_file(
1338 struct lttng_trace_chunk
*chunk
,
1345 found
= lttng_trace_chunk_find_file(chunk
, path
, &index
);
1349 ret
= lttng_dynamic_pointer_array_remove_pointer(
1350 &chunk
->files
, index
);
1355 enum lttng_trace_chunk_status
_lttng_trace_chunk_open_fs_handle_locked(
1356 struct lttng_trace_chunk
*chunk
,
1357 const char *file_path
,
1360 struct fs_handle
**out_handle
,
1361 bool expect_no_file
)
1364 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1366 DBG("Opening trace chunk file \"%s\"", file_path
);
1367 if (!chunk
->credentials
.is_set
) {
1369 * Fatal error, credentials must be set before a
1372 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1374 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1377 if (!chunk
->chunk_directory
) {
1378 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1380 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1383 status
= lttng_trace_chunk_add_file(chunk
, file_path
);
1384 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1387 if (chunk
->fd_tracker
) {
1388 LTTNG_ASSERT(chunk
->credentials
.value
.use_current_user
);
1389 *out_handle
= fd_tracker_open_fs_handle(chunk
->fd_tracker
,
1390 chunk
->chunk_directory
, file_path
, flags
, &mode
);
1391 ret
= *out_handle
? 0 : -1;
1393 ret
= lttng_directory_handle_open_file_as_user(
1394 chunk
->chunk_directory
, file_path
, flags
, mode
,
1395 chunk
->credentials
.value
.use_current_user
?
1397 &chunk
->credentials
.value
.user
);
1399 *out_handle
= fs_handle_untracked_create(
1400 chunk
->chunk_directory
, file_path
, ret
);
1402 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1408 if (errno
== ENOENT
&& expect_no_file
) {
1409 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
1411 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1412 file_path
, flags
, (int) mode
);
1413 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1415 lttng_trace_chunk_remove_file(chunk
, file_path
);
1422 enum lttng_trace_chunk_status
lttng_trace_chunk_open_fs_handle(
1423 struct lttng_trace_chunk
*chunk
,
1424 const char *file_path
,
1427 struct fs_handle
**out_handle
,
1428 bool expect_no_file
)
1430 enum lttng_trace_chunk_status status
;
1432 pthread_mutex_lock(&chunk
->lock
);
1433 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1434 flags
, mode
, out_handle
, expect_no_file
);
1435 pthread_mutex_unlock(&chunk
->lock
);
1439 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
1440 struct lttng_trace_chunk
*chunk
,
1441 const char *file_path
,
1445 bool expect_no_file
)
1447 enum lttng_trace_chunk_status status
;
1448 struct fs_handle
*fs_handle
;
1450 pthread_mutex_lock(&chunk
->lock
);
1452 * Using this method is never valid when an fd_tracker is being
1453 * used since the resulting file descriptor would not be tracked.
1455 LTTNG_ASSERT(!chunk
->fd_tracker
);
1456 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1457 flags
, mode
, &fs_handle
, expect_no_file
);
1458 pthread_mutex_unlock(&chunk
->lock
);
1460 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
1461 *out_fd
= fs_handle_get_fd(fs_handle
);
1463 * Does not close the fd; we just "unbox" it from the fs_handle.
1465 fs_handle_untracked_destroy(lttng::utils::container_of(
1466 fs_handle
, &fs_handle_untracked::parent
));
1472 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
1473 const char *file_path
)
1476 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1478 DBG("Unlinking trace chunk file \"%s\"", file_path
);
1479 pthread_mutex_lock(&chunk
->lock
);
1480 if (!chunk
->credentials
.is_set
) {
1482 * Fatal error, credentials must be set before a
1485 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1487 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1490 if (!chunk
->chunk_directory
) {
1491 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1493 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1496 ret
= lttng_directory_handle_unlink_file_as_user(
1497 chunk
->chunk_directory
, file_path
,
1498 chunk
->credentials
.value
.use_current_user
?
1499 NULL
: &chunk
->credentials
.value
.user
);
1501 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1504 lttng_trace_chunk_remove_file(chunk
, file_path
);
1506 pthread_mutex_unlock(&chunk
->lock
);
1511 int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk
*chunk
,
1515 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1517 DBG("Recursively removing trace chunk directory \"%s\"", path
);
1518 pthread_mutex_lock(&chunk
->lock
);
1519 if (!chunk
->credentials
.is_set
) {
1521 * Fatal error, credentials must be set before a
1522 * directory is removed.
1524 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1526 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1529 if (!chunk
->chunk_directory
) {
1530 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1532 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1535 ret
= lttng_directory_handle_remove_subdirectory_recursive_as_user(
1536 chunk
->chunk_directory
, path
,
1537 chunk
->credentials
.value
.use_current_user
?
1538 NULL
: &chunk
->credentials
.value
.user
,
1539 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
);
1541 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1545 pthread_mutex_unlock(&chunk
->lock
);
1550 int lttng_trace_chunk_move_to_completed_post_release(
1551 struct lttng_trace_chunk
*trace_chunk
)
1554 char *archived_chunk_name
= NULL
;
1555 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
1556 const time_t creation_timestamp
=
1557 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
1558 const time_t close_timestamp
=
1559 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
1560 struct lttng_directory_handle
*archived_chunks_directory
= NULL
;
1561 enum lttng_trace_chunk_status status
;
1563 if (!trace_chunk
->mode
.is_set
||
1564 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
1565 !trace_chunk
->session_output_directory
) {
1567 * This command doesn't need to run if the output is remote
1568 * or if the trace chunk is not owned by this process.
1573 LTTNG_ASSERT(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
1574 LTTNG_ASSERT(!trace_chunk
->name_overridden
);
1575 LTTNG_ASSERT(trace_chunk
->path
);
1577 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
1579 if (!archived_chunk_name
) {
1580 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1585 ret
= lttng_directory_handle_create_subdirectory_as_user(
1586 trace_chunk
->session_output_directory
,
1587 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1589 !trace_chunk
->credentials
.value
.use_current_user
?
1590 &trace_chunk
->credentials
.value
.user
:
1593 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1594 "\" directory for archived trace chunks");
1598 archived_chunks_directory
= trace_chunk
->fd_tracker
?
1599 fd_tracker_create_directory_handle_from_handle(
1600 trace_chunk
->fd_tracker
,
1601 trace_chunk
->session_output_directory
,
1602 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
) :
1603 lttng_directory_handle_create_from_handle(
1604 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1605 trace_chunk
->session_output_directory
);
1606 if (!archived_chunks_directory
) {
1607 PERROR("Failed to get handle to archived trace chunks directory");
1613 * Make sure chunk is renamed to old directory if not already done by
1614 * the creation of the next chunk. This happens if a rotation is
1615 * performed while tracing is stopped.
1617 if (!trace_chunk
->path
|| strcmp(trace_chunk
->path
,
1618 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
)) {
1619 status
= lttng_trace_chunk_rename_path_no_lock(trace_chunk
,
1620 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1621 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1622 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1628 ret
= lttng_directory_handle_rename_as_user(
1629 trace_chunk
->session_output_directory
,
1631 archived_chunks_directory
,
1632 archived_chunk_name
,
1633 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1635 &trace_chunk
->credentials
.value
.user
);
1637 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1639 archived_chunk_name
);
1643 lttng_directory_handle_put(archived_chunks_directory
);
1644 free(archived_chunk_name
);
1649 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
__attribute__((unused
)))
1655 int lttng_trace_chunk_delete_post_release_user(
1656 struct lttng_trace_chunk
*trace_chunk
)
1660 DBG("Trace chunk \"delete\" close command post-release (User)");
1662 /* Unlink all files. */
1663 while (lttng_dynamic_pointer_array_get_count(&trace_chunk
->files
) != 0) {
1664 enum lttng_trace_chunk_status status
;
1668 path
= (const char *) lttng_dynamic_pointer_array_get_pointer(
1669 &trace_chunk
->files
, 0);
1670 DBG("Unlink file: %s", path
);
1671 status
= (lttng_trace_chunk_status
) lttng_trace_chunk_unlink_file(trace_chunk
, path
);
1672 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1673 ERR("Error unlinking file '%s' when deleting chunk", path
);
1683 int lttng_trace_chunk_delete_post_release_owner(
1684 struct lttng_trace_chunk
*trace_chunk
)
1686 enum lttng_trace_chunk_status status
;
1690 ret
= lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1695 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1697 LTTNG_ASSERT(trace_chunk
->session_output_directory
);
1698 LTTNG_ASSERT(trace_chunk
->chunk_directory
);
1700 /* Remove empty directories. */
1701 count
= lttng_dynamic_pointer_array_get_count(
1702 &trace_chunk
->top_level_directories
);
1704 for (i
= 0; i
< count
; i
++) {
1705 const char *top_level_name
=
1706 (const char *) lttng_dynamic_pointer_array_get_pointer(
1707 &trace_chunk
->top_level_directories
, i
);
1709 status
= (lttng_trace_chunk_status
) lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk
, top_level_name
);
1710 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1711 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1718 lttng_directory_handle_put(trace_chunk
->chunk_directory
);
1719 trace_chunk
->chunk_directory
= NULL
;
1721 if (trace_chunk
->path
&& trace_chunk
->path
[0] != '\0') {
1722 status
= (lttng_trace_chunk_status
) lttng_directory_handle_remove_subdirectory(
1723 trace_chunk
->session_output_directory
,
1725 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1726 ERR("Error removing subdirectory '%s' file when deleting chunk",
1732 free(trace_chunk
->path
);
1733 trace_chunk
->path
= NULL
;
1739 * For local files, session and consumer daemons all run the delete hook. The
1740 * consumer daemons have the list of files to unlink, and technically the
1741 * session daemon is the owner of the chunk. Unlink all files owned by each
1745 int lttng_trace_chunk_delete_post_release(
1746 struct lttng_trace_chunk
*trace_chunk
)
1748 if (!trace_chunk
->chunk_directory
) {
1752 if (trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
) {
1753 return lttng_trace_chunk_delete_post_release_owner(trace_chunk
);
1755 return lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1759 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
1760 struct lttng_trace_chunk
*chunk
,
1761 enum lttng_trace_chunk_command_type
*command_type
)
1763 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1765 pthread_mutex_lock(&chunk
->lock
);
1766 if (chunk
->close_command
.is_set
) {
1767 *command_type
= chunk
->close_command
.value
;
1768 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1770 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1772 pthread_mutex_unlock(&chunk
->lock
);
1776 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
1777 struct lttng_trace_chunk
*chunk
,
1778 enum lttng_trace_chunk_command_type close_command
)
1780 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1782 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1783 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1784 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1788 pthread_mutex_lock(&chunk
->lock
);
1789 if (chunk
->close_command
.is_set
) {
1790 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1791 lttng_trace_chunk_command_type_str(chunk
->close_command
.value
),
1792 lttng_trace_chunk_command_type_str(close_command
));
1794 DBG("Setting trace chunk close command to \"%s\"",
1795 lttng_trace_chunk_command_type_str(close_command
));
1798 * Unset close command for no-op for backward compatibility with relayd
1801 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1802 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1804 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1806 pthread_mutex_unlock(&chunk
->lock
);
1811 const char *lttng_trace_chunk_command_type_get_name(
1812 enum lttng_trace_chunk_command_type command
)
1815 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1816 return "move to completed trace chunk folder";
1817 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1818 return "no operation";
1819 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1826 bool lttng_trace_chunk_ids_equal(const struct lttng_trace_chunk
*chunk_a
,
1827 const struct lttng_trace_chunk
*chunk_b
)
1831 if (chunk_a
== chunk_b
) {
1836 if (!!chunk_a
^ !!chunk_b
) {
1840 if (chunk_a
->id
.is_set
^ chunk_a
->id
.is_set
) {
1841 /* One id is set and not the other, thus they are not equal. */
1845 if (!chunk_a
->id
.is_set
) {
1846 /* Both ids are unset. */
1849 equal
= chunk_a
->id
.value
== chunk_b
->id
.value
;
1856 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1858 return urcu_ref_get_unless_zero(&chunk
->ref
);
1862 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1864 struct lttng_trace_chunk_registry_element
*element
= lttng::utils::container_of(
1865 node
, <tng_trace_chunk_registry_element::rcu_node
);
1871 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1873 struct lttng_trace_chunk
*chunk
= lttng::utils::container_of(ref
, <tng_trace_chunk::ref
);
1875 if (chunk
->close_command
.is_set
) {
1876 chunk_command func
= close_command_get_post_release_func(chunk
->close_command
.value
);
1879 ERR("Trace chunk post-release command %s has failed.",
1880 lttng_trace_chunk_command_type_str(chunk
->close_command
.value
));
1884 if (chunk
->in_registry_element
) {
1885 struct lttng_trace_chunk_registry_element
*element
;
1888 * Release internal chunk attributes immediately and
1889 * only use the deferred `call_rcu` work to reclaim the
1892 * This ensures that file handles are released as soon as
1893 * possible which works around a problem we encounter with PRAM fs
1894 * mounts (and possibly other non-POSIX compliant file systems):
1895 * directories that contain files which are open can't be
1898 * This means that the recording of a snapshot could be
1899 * completed, but that it would be impossible for the user to
1900 * delete it until the deferred clean-up released the file
1901 * handles to its contents.
1903 lttng_trace_chunk_fini(chunk
);
1905 element
= lttng::utils::container_of(
1906 chunk
, <tng_trace_chunk_registry_element::chunk
);
1907 if (element
->registry
) {
1909 cds_lfht_del(element
->registry
->ht
,
1910 &element
->trace_chunk_registry_ht_node
);
1912 call_rcu(&element
->rcu_node
,
1913 free_lttng_trace_chunk_registry_element
);
1915 /* Never published, can be free'd immediately. */
1916 free_lttng_trace_chunk_registry_element(
1917 &element
->rcu_node
);
1920 /* Not RCU-protected, free immediately. */
1921 lttng_trace_chunk_fini(chunk
);
1926 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1931 LTTNG_ASSERT(chunk
->ref
.refcount
);
1932 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1935 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1937 struct lttng_trace_chunk_registry
*registry
;
1939 registry
= zmalloc
<lttng_trace_chunk_registry
>();
1944 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1945 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1946 if (!registry
->ht
) {
1952 lttng_trace_chunk_registry_destroy(registry
);
1956 void lttng_trace_chunk_registry_destroy(
1957 struct lttng_trace_chunk_registry
*registry
)
1963 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1970 struct lttng_trace_chunk_registry_element
*
1971 lttng_trace_chunk_registry_element_create_from_chunk(
1972 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1974 struct lttng_trace_chunk_registry_element
*element
=
1975 zmalloc
<lttng_trace_chunk_registry_element
>();
1980 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1981 element
->session_id
= session_id
;
1983 element
->chunk
= *chunk
;
1984 lttng_trace_chunk_init(&element
->chunk
);
1985 if (chunk
->session_output_directory
) {
1986 /* Transferred ownership. */
1987 element
->chunk
.session_output_directory
=
1988 chunk
->session_output_directory
;
1989 chunk
->session_output_directory
= NULL
;
1991 if (chunk
->chunk_directory
) {
1992 /* Transferred ownership. */
1993 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1994 chunk
->chunk_directory
= NULL
;
1997 * The original chunk becomes invalid; the name and path attributes are
1998 * transferred to the new chunk instance.
2002 element
->chunk
.fd_tracker
= chunk
->fd_tracker
;
2003 element
->chunk
.in_registry_element
= true;
2008 struct lttng_trace_chunk
*
2009 lttng_trace_chunk_registry_publish_chunk(
2010 struct lttng_trace_chunk_registry
*registry
,
2011 uint64_t session_id
,
2012 struct lttng_trace_chunk
*chunk
)
2016 return lttng_trace_chunk_registry_publish_chunk(
2017 registry
, session_id
, chunk
, &unused
);
2020 struct lttng_trace_chunk
*
2021 lttng_trace_chunk_registry_publish_chunk(
2022 struct lttng_trace_chunk_registry
*registry
,
2023 uint64_t session_id
, struct lttng_trace_chunk
*chunk
,
2024 bool *previously_published
)
2026 struct lttng_trace_chunk_registry_element
*element
;
2027 unsigned long element_hash
;
2029 pthread_mutex_lock(&chunk
->lock
);
2030 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
2032 pthread_mutex_unlock(&chunk
->lock
);
2037 * chunk is now invalid, the only valid operation is a 'put' from the
2041 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
2045 struct cds_lfht_node
*published_node
;
2046 struct lttng_trace_chunk
*published_chunk
;
2047 struct lttng_trace_chunk_registry_element
*published_element
;
2049 published_node
= cds_lfht_add_unique(registry
->ht
,
2051 lttng_trace_chunk_registry_element_match
,
2053 &element
->trace_chunk_registry_ht_node
);
2054 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
2055 /* Successfully published the new element. */
2056 element
->registry
= registry
;
2057 /* Acquire a reference for the caller. */
2058 if (lttng_trace_chunk_get(&element
->chunk
)) {
2059 *previously_published
= false;
2063 * Another thread concurrently unpublished the
2064 * trace chunk. This is currently unexpected.
2066 * Re-attempt to publish.
2068 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
2074 * An equivalent trace chunk was published before this trace
2075 * chunk. Attempt to acquire a reference to the one that was
2076 * already published and release the reference to the copy we
2077 * created if successful.
2079 published_element
= lttng::utils::container_of(published_node
,
2080 <tng_trace_chunk_registry_element::trace_chunk_registry_ht_node
);
2081 published_chunk
= &published_element
->chunk
;
2082 if (lttng_trace_chunk_get(published_chunk
)) {
2083 lttng_trace_chunk_put(&element
->chunk
);
2084 element
= published_element
;
2085 *previously_published
= true;
2089 * A reference to the previously published trace chunk could not
2090 * be acquired. Hence, retry to publish our copy of the trace
2096 return element
? &element
->chunk
: NULL
;
2100 * Note that the caller must be registered as an RCU thread.
2101 * However, it does not need to hold the RCU read lock. The RCU read lock is
2102 * acquired to perform the look-up in the registry's hash table and held until
2103 * after a reference to the "found" trace chunk is acquired.
2105 * IOW, holding a reference guarantees the existence of the object for the
2109 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
2110 const struct lttng_trace_chunk_registry
*registry
,
2111 uint64_t session_id
, uint64_t *chunk_id
)
2113 lttng_trace_chunk_registry_element target_element
{};
2115 target_element
.chunk
.id
.is_set
= !!chunk_id
;
2116 target_element
.chunk
.id
.value
= chunk_id
? *chunk_id
: 0;
2117 target_element
.session_id
= session_id
;
2119 const unsigned long element_hash
=
2120 lttng_trace_chunk_registry_element_hash(
2122 struct cds_lfht_node
*published_node
;
2123 struct lttng_trace_chunk_registry_element
*published_element
;
2124 struct lttng_trace_chunk
*published_chunk
= NULL
;
2125 struct cds_lfht_iter iter
;
2128 cds_lfht_lookup(registry
->ht
,
2130 lttng_trace_chunk_registry_element_match
,
2133 published_node
= cds_lfht_iter_get_node(&iter
);
2134 if (!published_node
) {
2138 published_element
= lttng::utils::container_of(published_node
,
2139 <tng_trace_chunk_registry_element::trace_chunk_registry_ht_node
);
2140 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
2141 published_chunk
= &published_element
->chunk
;
2145 return published_chunk
;
2148 struct lttng_trace_chunk
*
2149 lttng_trace_chunk_registry_find_chunk(
2150 const struct lttng_trace_chunk_registry
*registry
,
2151 uint64_t session_id
, uint64_t chunk_id
)
2153 return _lttng_trace_chunk_registry_find_chunk(registry
,
2154 session_id
, &chunk_id
);
2157 int lttng_trace_chunk_registry_chunk_exists(
2158 const struct lttng_trace_chunk_registry
*registry
,
2159 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
2162 lttng_trace_chunk_registry_element target_element
;
2164 target_element
.chunk
.id
.is_set
= true;
2165 target_element
.chunk
.id
.value
= chunk_id
;
2166 target_element
.session_id
= session_id
;
2168 const unsigned long element_hash
=
2169 lttng_trace_chunk_registry_element_hash(
2171 struct cds_lfht_node
*published_node
;
2172 struct cds_lfht_iter iter
;
2175 cds_lfht_lookup(registry
->ht
,
2177 lttng_trace_chunk_registry_element_match
,
2180 published_node
= cds_lfht_iter_get_node(&iter
);
2181 if (!published_node
) {
2182 *chunk_exists
= false;
2186 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
2192 struct lttng_trace_chunk
*
2193 lttng_trace_chunk_registry_find_anonymous_chunk(
2194 const struct lttng_trace_chunk_registry
*registry
,
2195 uint64_t session_id
)
2197 return _lttng_trace_chunk_registry_find_chunk(registry
,
2201 unsigned int lttng_trace_chunk_registry_put_each_chunk(
2202 const struct lttng_trace_chunk_registry
*registry
)
2204 struct cds_lfht_iter iter
;
2205 struct lttng_trace_chunk_registry_element
*chunk_element
;
2206 unsigned int trace_chunks_left
= 0;
2208 DBG("Releasing trace chunk registry to all trace chunks");
2210 cds_lfht_for_each_entry(registry
->ht
,
2211 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
2212 const char *chunk_id_str
= "none";
2213 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
2215 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
2216 if (chunk_element
->chunk
.id
.is_set
) {
2219 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
2221 chunk_element
->chunk
.id
.value
);
2222 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
2223 chunk_id_str
= "formatting error";
2225 chunk_id_str
= chunk_id_buf
;
2229 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
2230 "chunk_id = %s, name = \"%s\", status = %s",
2231 chunk_element
->session_id
,
2233 chunk_element
->chunk
.name
? : "none",
2234 chunk_element
->chunk
.close_command
.is_set
?
2236 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
2237 lttng_trace_chunk_put(&chunk_element
->chunk
);
2238 trace_chunks_left
++;
2241 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
2244 return trace_chunks_left
;