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.h>
9 #include <common/credentials.h>
10 #include <common/defaults.h>
11 #include <common/dynamic-array.h>
12 #include <common/error.h>
13 #include <common/fd-tracker/fd-tracker.h>
14 #include <common/fs-handle-internal.h>
15 #include <common/hashtable/hashtable.h>
16 #include <common/hashtable/utils.h>
17 #include <common/optional.h>
18 #include <common/string-utils/format.h>
19 #include <common/time.h>
20 #include <common/trace-chunk-registry.h>
21 #include <common/trace-chunk.h>
22 #include <common/utils.h>
23 #include <lttng/constant.h>
29 #include <urcu/rculfhash.h>
33 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
34 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
36 #define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
37 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
39 enum trace_chunk_mode
{
40 TRACE_CHUNK_MODE_USER
,
41 TRACE_CHUNK_MODE_OWNER
,
45 * Callback to invoke on release of a trace chunk. Note that there is no
46 * need to 'lock' the trace chunk during the execution of these callbacks
47 * since only one thread may access a chunk during its destruction (the last
48 * to release its reference to the chunk).
50 typedef int (*chunk_command
)(struct lttng_trace_chunk
*trace_chunk
);
52 /* Move a completed trace chunk to the 'completed' trace archive folder. */
54 int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk
*trace_chunk
);
57 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
);
58 /* Unlink old chunk files. */
60 int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk
*trace_chunk
);
62 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
63 struct lttng_trace_chunk
*chunk
, const char *path
);
65 struct chunk_credentials
{
66 bool use_current_user
;
67 struct lttng_credentials user
;
71 * NOTE: Make sure to update:
72 * - lttng_trace_chunk_copy(),
73 * - lttng_trace_chunk_registry_element_create_from_chunk()
74 * if you modify this structure.
76 struct lttng_trace_chunk
{
79 LTTNG_OPTIONAL(enum trace_chunk_mode
) mode
;
81 * First-level directories created within the trace chunk.
82 * Elements are of type 'char *'.
84 * Only used by _owner_ mode chunks.
86 struct lttng_dynamic_pointer_array top_level_directories
;
88 * All files contained within the trace chunk.
89 * Array of paths (char *).
91 struct lttng_dynamic_pointer_array files
;
92 /* Is contained within an lttng_trace_chunk_registry_element? */
93 bool in_registry_element
;
97 /* An unset id means the chunk is anonymous. */
98 LTTNG_OPTIONAL(uint64_t) id
;
99 LTTNG_OPTIONAL(time_t) timestamp_creation
;
100 LTTNG_OPTIONAL(time_t) timestamp_close
;
101 LTTNG_OPTIONAL(struct chunk_credentials
) credentials
;
102 struct lttng_directory_handle
*session_output_directory
;
103 struct lttng_directory_handle
*chunk_directory
;
104 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type
) close_command
;
106 * fd_tracker instance through which file descriptors should be
109 * An fd_tracker always outlives any trace chunk; there is no
110 * need to perform any reference counting of that object.
112 struct fd_tracker
*fd_tracker
;
115 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
116 struct lttng_trace_chunk_registry_element
{
117 struct lttng_trace_chunk chunk
;
119 /* Weak and only set when added. */
120 struct lttng_trace_chunk_registry
*registry
;
121 struct cds_lfht_node trace_chunk_registry_ht_node
;
122 /* call_rcu delayed reclaim. */
123 struct rcu_head rcu_node
;
126 struct lttng_trace_chunk_registry
{
130 struct fs_handle_untracked
{
131 struct fs_handle parent
;
134 struct lttng_directory_handle
*directory_handle
;
140 int fs_handle_untracked_get_fd(struct fs_handle
*handle
);
142 void fs_handle_untracked_put_fd(struct fs_handle
*handle
);
144 int fs_handle_untracked_unlink(struct fs_handle
*handle
);
146 int fs_handle_untracked_close(struct fs_handle
*handle
);
149 char *close_command_names
[] = {
150 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
151 "move to completed chunk folder",
152 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
154 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
159 chunk_command close_command_post_release_funcs
[] = {
160 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
161 lttng_trace_chunk_move_to_completed_post_release
,
162 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
163 lttng_trace_chunk_no_operation
,
164 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
165 lttng_trace_chunk_delete_post_release
,
169 struct fs_handle
*fs_handle_untracked_create(
170 struct lttng_directory_handle
*directory_handle
,
174 struct fs_handle_untracked
*handle
= NULL
;
175 bool reference_acquired
;
176 char *path_copy
= strdup(path
);
180 PERROR("Failed to copy file path while creating untracked filesystem handle");
184 handle
= zmalloc(sizeof(typeof(*handle
)));
186 PERROR("Failed to allocate untracked filesystem handle");
190 handle
->parent
= (typeof(handle
->parent
)) {
191 .get_fd
= fs_handle_untracked_get_fd
,
192 .put_fd
= fs_handle_untracked_put_fd
,
193 .unlink
= fs_handle_untracked_unlink
,
194 .close
= fs_handle_untracked_close
,
198 reference_acquired
= lttng_directory_handle_get(directory_handle
);
199 assert(reference_acquired
);
200 handle
->location
.directory_handle
= directory_handle
;
201 /* Ownership is transferred. */
202 handle
->location
.path
= path_copy
;
206 return handle
? &handle
->parent
: NULL
;
210 int fs_handle_untracked_get_fd(struct fs_handle
*_handle
)
212 struct fs_handle_untracked
*handle
= container_of(
213 _handle
, struct fs_handle_untracked
, parent
);
219 void fs_handle_untracked_put_fd(struct fs_handle
*_handle
)
225 int fs_handle_untracked_unlink(struct fs_handle
*_handle
)
227 struct fs_handle_untracked
*handle
= container_of(
228 _handle
, struct fs_handle_untracked
, parent
);
230 return lttng_directory_handle_unlink_file(
231 handle
->location
.directory_handle
,
232 handle
->location
.path
);
236 void fs_handle_untracked_destroy(struct fs_handle_untracked
*handle
)
238 lttng_directory_handle_put(handle
->location
.directory_handle
);
239 free(handle
->location
.path
);
244 int fs_handle_untracked_close(struct fs_handle
*_handle
)
246 struct fs_handle_untracked
*handle
= container_of(
247 _handle
, struct fs_handle_untracked
, parent
);
248 int ret
= close(handle
->fd
);
250 fs_handle_untracked_destroy(handle
);
255 bool lttng_trace_chunk_registry_element_equals(
256 const struct lttng_trace_chunk_registry_element
*a
,
257 const struct lttng_trace_chunk_registry_element
*b
)
259 if (a
->session_id
!= b
->session_id
) {
262 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
265 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
274 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
,
277 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
279 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
280 element_b
= caa_container_of(node
, typeof(*element_b
),
281 trace_chunk_registry_ht_node
);
282 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
286 unsigned long lttng_trace_chunk_registry_element_hash(
287 const struct lttng_trace_chunk_registry_element
*element
)
289 unsigned long hash
= hash_key_u64(&element
->session_id
,
292 if (element
->chunk
.id
.is_set
) {
293 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
300 char *generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
,
301 const time_t *close_timestamp
)
304 char *new_name
= NULL
;
305 char start_datetime
[ISO8601_STR_LEN
] = {};
306 /* Add 1 for a '-' prefix. */
307 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
309 ret
= time_to_iso8601_str(
311 start_datetime
, sizeof(start_datetime
));
313 ERR("Failed to format trace chunk start date time");
316 if (close_timestamp
) {
317 *end_datetime_suffix
= '-';
318 ret
= time_to_iso8601_str(
320 end_datetime_suffix
+ 1,
321 sizeof(end_datetime_suffix
) - 1);
323 ERR("Failed to format trace chunk end date time");
327 new_name
= zmalloc(GENERATED_CHUNK_NAME_LEN
);
329 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
332 ret
= snprintf(new_name
, GENERATED_CHUNK_NAME_LEN
, "%s%s-%" PRIu64
,
333 start_datetime
, end_datetime_suffix
, chunk_id
);
334 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
335 ERR("Failed to format trace chunk name");
346 void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
348 urcu_ref_init(&chunk
->ref
);
349 pthread_mutex_init(&chunk
->lock
, NULL
);
350 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
351 lttng_dynamic_pointer_array_init(&chunk
->files
, free
);
355 void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
357 if (chunk
->session_output_directory
) {
358 lttng_directory_handle_put(
359 chunk
->session_output_directory
);
360 chunk
->session_output_directory
= NULL
;
362 if (chunk
->chunk_directory
) {
363 lttng_directory_handle_put(chunk
->chunk_directory
);
364 chunk
->chunk_directory
= NULL
;
370 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
371 lttng_dynamic_pointer_array_reset(&chunk
->files
);
372 pthread_mutex_destroy(&chunk
->lock
);
376 struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
378 struct lttng_trace_chunk
*chunk
= NULL
;
380 chunk
= zmalloc(sizeof(*chunk
));
382 ERR("Failed to allocate trace chunk");
385 lttng_trace_chunk_init(chunk
);
391 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
393 DBG("Creating anonymous trace chunk");
394 return lttng_trace_chunk_allocate();
398 struct lttng_trace_chunk
*lttng_trace_chunk_create(
399 uint64_t chunk_id
, time_t chunk_creation_time
, const char *path
)
401 struct lttng_trace_chunk
*chunk
;
402 char chunk_creation_datetime_buf
[16] = {};
403 const char *chunk_creation_datetime_str
= "(formatting error)";
404 struct tm timeinfo_buf
, *timeinfo
;
406 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
410 /* Don't fail because of this; it is only used for logging. */
411 strftime_ret
= strftime(chunk_creation_datetime_buf
,
412 sizeof(chunk_creation_datetime_buf
),
413 "%Y%m%d-%H%M%S", timeinfo
);
415 chunk_creation_datetime_str
=
416 chunk_creation_datetime_buf
;
420 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
421 chunk_id
, 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
,
431 chunk_creation_time
, NULL
);
433 ERR("Failed to allocate trace chunk name storage");
438 chunk
->path
= strdup(path
);
444 chunk
->path
= strdup(chunk
->name
);
451 DBG("Chunk name set to \"%s\"", chunk
->name
? : "(none)");
455 lttng_trace_chunk_put(chunk
);
460 void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk
*chunk
,
461 struct fd_tracker
*fd_tracker
)
463 assert(!chunk
->session_output_directory
);
464 assert(!chunk
->chunk_directory
);
465 assert(lttng_dynamic_pointer_array_get_count(&chunk
->files
) == 0);
466 chunk
->fd_tracker
= fd_tracker
;
470 struct lttng_trace_chunk
*lttng_trace_chunk_copy(
471 struct lttng_trace_chunk
*source_chunk
)
473 struct lttng_trace_chunk
*new_chunk
= lttng_trace_chunk_allocate();
479 pthread_mutex_lock(&source_chunk
->lock
);
481 * A new chunk is always a user; it shall create no new trace
484 new_chunk
->mode
= (typeof(new_chunk
->mode
)) {
486 .value
= TRACE_CHUNK_MODE_USER
,
489 * top_level_directories is not copied as it is never used
490 * by _user_ mode chunks.
492 /* The new chunk is not part of a registry (yet, at least). */
493 new_chunk
->in_registry_element
= false;
494 new_chunk
->name_overridden
= source_chunk
->name_overridden
;
495 if (source_chunk
->name
) {
496 new_chunk
->name
= strdup(source_chunk
->name
);
497 if (!new_chunk
->name
) {
498 ERR("Failed to copy source trace chunk name in %s()",
503 if (source_chunk
->path
) {
504 new_chunk
->path
= strdup(source_chunk
->path
);
505 if (!new_chunk
->path
) {
506 ERR("Failed to copy source trace chunk path in %s()",
510 new_chunk
->id
= source_chunk
->id
;
511 new_chunk
->timestamp_creation
= source_chunk
->timestamp_creation
;
512 new_chunk
->timestamp_close
= source_chunk
->timestamp_close
;
513 new_chunk
->credentials
= source_chunk
->credentials
;
514 if (source_chunk
->session_output_directory
) {
515 const bool reference_acquired
= lttng_directory_handle_get(
516 source_chunk
->session_output_directory
);
518 assert(reference_acquired
);
519 new_chunk
->session_output_directory
=
520 source_chunk
->session_output_directory
;
522 if (source_chunk
->chunk_directory
) {
523 const bool reference_acquired
= lttng_directory_handle_get(
524 source_chunk
->chunk_directory
);
526 assert(reference_acquired
);
527 new_chunk
->chunk_directory
= source_chunk
->chunk_directory
;
529 new_chunk
->close_command
= source_chunk
->close_command
;
530 new_chunk
->fd_tracker
= source_chunk
->fd_tracker
;
531 pthread_mutex_unlock(&source_chunk
->lock
);
535 pthread_mutex_unlock(&source_chunk
->lock
);
536 lttng_trace_chunk_put(new_chunk
);
541 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(
542 struct lttng_trace_chunk
*chunk
, uint64_t *id
)
544 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
546 pthread_mutex_lock(&chunk
->lock
);
547 if (chunk
->id
.is_set
) {
548 *id
= chunk
->id
.value
;
550 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
552 pthread_mutex_unlock(&chunk
->lock
);
557 enum lttng_trace_chunk_status
lttng_trace_chunk_get_creation_timestamp(
558 struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
561 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
563 pthread_mutex_lock(&chunk
->lock
);
564 if (chunk
->timestamp_creation
.is_set
) {
565 *creation_ts
= chunk
->timestamp_creation
.value
;
567 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
569 pthread_mutex_unlock(&chunk
->lock
);
574 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_timestamp(
575 struct lttng_trace_chunk
*chunk
, time_t *close_ts
)
577 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
579 pthread_mutex_lock(&chunk
->lock
);
580 if (chunk
->timestamp_close
.is_set
) {
581 *close_ts
= chunk
->timestamp_close
.value
;
583 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
585 pthread_mutex_unlock(&chunk
->lock
);
590 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(
591 struct lttng_trace_chunk
*chunk
, time_t close_ts
)
593 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
595 pthread_mutex_lock(&chunk
->lock
);
596 if (!chunk
->timestamp_creation
.is_set
) {
597 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
598 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
601 if (chunk
->timestamp_creation
.value
> close_ts
) {
602 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
603 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
606 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
607 if (!chunk
->name_overridden
) {
609 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
610 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
613 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
617 pthread_mutex_unlock(&chunk
->lock
);
622 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(
623 struct lttng_trace_chunk
*chunk
, const char **name
,
624 bool *name_overridden
)
626 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
628 pthread_mutex_lock(&chunk
->lock
);
629 if (name_overridden
) {
630 *name_overridden
= chunk
->name_overridden
;
633 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
638 pthread_mutex_unlock(&chunk
->lock
);
643 bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk
*chunk
)
645 bool name_overridden
;
647 pthread_mutex_lock(&chunk
->lock
);
648 name_overridden
= chunk
->name_overridden
;
649 pthread_mutex_unlock(&chunk
->lock
);
650 return name_overridden
;
654 bool is_valid_chunk_name(const char *name
)
662 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
663 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
667 if (strchr(name
, '/') || strchr(name
, '.')) {
675 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
676 struct lttng_trace_chunk
*chunk
, const char *name
)
679 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
680 char *new_name
, *new_path
;
682 DBG("Override trace chunk name from %s to %s", chunk
->name
, name
);
683 if (!is_valid_chunk_name(name
)) {
684 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
686 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
690 pthread_mutex_lock(&chunk
->lock
);
691 if (!chunk
->id
.is_set
) {
692 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
694 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
698 new_name
= strdup(name
);
700 ERR("Failed to allocate new trace chunk name");
701 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
705 chunk
->name
= new_name
;
707 new_path
= strdup(name
);
709 ERR("Failed to allocate new trace chunk path");
710 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
714 chunk
->path
= new_path
;
716 chunk
->name_overridden
= true;
718 pthread_mutex_unlock(&chunk
->lock
);
724 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
725 struct lttng_trace_chunk
*chunk
, const char *path
)
728 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
729 struct lttng_directory_handle
*rename_directory
= NULL
;
730 char *new_path
, *old_path
;
733 if (chunk
->name_overridden
) {
734 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
738 old_path
= chunk
->path
;
739 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path
, path
);
741 if ((!old_path
&& !path
) ||
742 (old_path
&& path
&& !strcmp(old_path
, path
))) {
746 * Use chunk name as path if NULL path is specified.
752 /* Renaming from "" to "" is not accepted. */
753 if (path
[0] == '\0' && old_path
[0] == '\0') {
754 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
759 * If a rename is performed on a chunk for which the chunk_directory
760 * is not set (yet), or the session_output_directory is not set
761 * (interacting with a relay daemon), there is no rename to perform.
763 if (!chunk
->chunk_directory
||
764 !chunk
->session_output_directory
) {
768 if (old_path
[0] != '\0' && path
[0] != '\0') {
769 /* Rename chunk directory. */
770 ret
= lttng_directory_handle_rename_as_user(
771 chunk
->session_output_directory
,
773 chunk
->session_output_directory
,
775 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
777 &chunk
->credentials
.value
.user
);
779 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
781 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
784 rename_directory
= lttng_directory_handle_create_from_handle(
786 chunk
->session_output_directory
);
787 if (!rename_directory
) {
788 ERR("Failed to get handle to trace chunk rename directory");
789 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
793 /* Release old handle. */
794 lttng_directory_handle_put(chunk
->chunk_directory
);
796 * Transfer new handle reference to chunk as the current chunk
799 chunk
->chunk_directory
= rename_directory
;
800 rename_directory
= NULL
;
801 } else if (old_path
[0] == '\0') {
802 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
803 &chunk
->top_level_directories
);
805 ret
= lttng_directory_handle_create_subdirectory_as_user(
806 chunk
->session_output_directory
,
809 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
811 &chunk
->credentials
.value
.user
);
813 PERROR("Failed to create trace chunk rename directory \"%s\"",
815 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
819 rename_directory
= lttng_directory_handle_create_from_handle(
820 path
, chunk
->session_output_directory
);
821 if (!rename_directory
) {
822 ERR("Failed to get handle to trace chunk rename directory");
823 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
827 /* Move toplevel directories. */
828 for (i
= 0; i
< count
; i
++) {
829 const char *top_level_name
=
830 lttng_dynamic_pointer_array_get_pointer(
831 &chunk
->top_level_directories
, i
);
833 ret
= lttng_directory_handle_rename_as_user(
834 chunk
->chunk_directory
,
838 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
840 &chunk
->credentials
.value
.user
);
842 PERROR("Failed to move \"%s\" to trace chunk rename directory",
844 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
848 /* Release old handle. */
849 lttng_directory_handle_put(chunk
->chunk_directory
);
851 * Transfer new handle reference to chunk as the current chunk
854 chunk
->chunk_directory
= rename_directory
;
855 rename_directory
= NULL
;
857 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
858 &chunk
->top_level_directories
);
859 const bool reference_acquired
= lttng_directory_handle_get(
860 chunk
->session_output_directory
);
862 assert(reference_acquired
);
863 rename_directory
= chunk
->session_output_directory
;
865 /* Move toplevel directories. */
866 for (i
= 0; i
< count
; i
++) {
867 const char *top_level_name
=
868 lttng_dynamic_pointer_array_get_pointer(
869 &chunk
->top_level_directories
, i
);
871 ret
= lttng_directory_handle_rename_as_user(
872 chunk
->chunk_directory
,
876 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
878 &chunk
->credentials
.value
.user
);
880 PERROR("Failed to move \"%s\" to trace chunk rename directory",
882 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
886 /* Release old handle. */
887 lttng_directory_handle_put(chunk
->chunk_directory
);
889 * Transfer new handle reference to chunk as the current chunk
892 chunk
->chunk_directory
= rename_directory
;
893 rename_directory
= NULL
;
895 /* Remove old directory. */
896 status
= lttng_directory_handle_remove_subdirectory(
897 chunk
->session_output_directory
,
899 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
900 ERR("Error removing subdirectory '%s' file when deleting chunk",
909 new_path
= strdup(path
);
911 ERR("Failed to allocate new trace chunk path");
912 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
919 chunk
->path
= new_path
;
921 lttng_directory_handle_put(rename_directory
);
926 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path(
927 struct lttng_trace_chunk
*chunk
, const char *path
)
930 enum lttng_trace_chunk_status status
;
932 pthread_mutex_lock(&chunk
->lock
);
933 status
= lttng_trace_chunk_rename_path_no_lock(chunk
, path
);
934 pthread_mutex_unlock(&chunk
->lock
);
940 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
941 struct lttng_trace_chunk
*chunk
,
942 struct lttng_credentials
*credentials
)
944 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
946 pthread_mutex_lock(&chunk
->lock
);
947 if (chunk
->credentials
.is_set
) {
948 if (chunk
->credentials
.value
.use_current_user
) {
949 credentials
->uid
= geteuid();
950 credentials
->gid
= getegid();
952 *credentials
= chunk
->credentials
.value
.user
;
955 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
957 pthread_mutex_unlock(&chunk
->lock
);
962 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
963 struct lttng_trace_chunk
*chunk
,
964 const struct lttng_credentials
*user_credentials
)
966 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
967 const struct chunk_credentials credentials
= {
968 .user
= *user_credentials
,
969 .use_current_user
= false,
972 pthread_mutex_lock(&chunk
->lock
);
973 if (chunk
->credentials
.is_set
) {
974 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
977 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
979 pthread_mutex_unlock(&chunk
->lock
);
984 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
985 struct lttng_trace_chunk
*chunk
)
987 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
988 const struct chunk_credentials credentials
= {
989 .use_current_user
= true,
992 pthread_mutex_lock(&chunk
->lock
);
993 if (chunk
->credentials
.is_set
) {
994 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
997 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
999 pthread_mutex_unlock(&chunk
->lock
);
1005 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
1006 struct lttng_trace_chunk
*chunk
,
1007 struct lttng_directory_handle
*session_output_directory
)
1010 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1011 struct lttng_directory_handle
*chunk_directory_handle
= NULL
;
1012 bool reference_acquired
;
1014 pthread_mutex_lock(&chunk
->lock
);
1015 if (chunk
->mode
.is_set
) {
1016 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1019 if (!chunk
->credentials
.is_set
) {
1021 * Fatal error, credentials must be set before a
1022 * directory is created.
1024 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
1025 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1028 if (chunk
->path
[0] != '\0') {
1029 ret
= lttng_directory_handle_create_subdirectory_as_user(
1030 session_output_directory
,
1033 !chunk
->credentials
.value
.use_current_user
?
1034 &chunk
->credentials
.value
.user
: NULL
);
1036 PERROR("Failed to create chunk output directory \"%s\"",
1038 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1041 chunk_directory_handle
=
1042 lttng_directory_handle_create_from_handle(
1044 session_output_directory
);
1045 if (!chunk_directory_handle
) {
1046 /* The function already logs on all error paths. */
1047 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1052 * A nameless chunk does not need its own output directory.
1053 * The session's output directory will be used.
1055 const bool reference_acquired
=
1056 lttng_directory_handle_get(
1057 session_output_directory
);
1059 assert(reference_acquired
);
1060 chunk_directory_handle
= session_output_directory
;
1062 chunk
->chunk_directory
= chunk_directory_handle
;
1063 chunk_directory_handle
= NULL
;
1064 reference_acquired
= lttng_directory_handle_get(
1065 session_output_directory
);
1066 assert(reference_acquired
);
1067 chunk
->session_output_directory
= session_output_directory
;
1068 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
1070 pthread_mutex_unlock(&chunk
->lock
);
1075 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
1076 struct lttng_trace_chunk
*chunk
,
1077 struct lttng_directory_handle
*chunk_directory
)
1079 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1080 bool reference_acquired
;
1082 pthread_mutex_lock(&chunk
->lock
);
1083 if (chunk
->mode
.is_set
) {
1084 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1087 if (!chunk
->credentials
.is_set
) {
1088 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
1089 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1092 reference_acquired
= lttng_directory_handle_get(chunk_directory
);
1093 assert(reference_acquired
);
1094 chunk
->chunk_directory
= chunk_directory
;
1095 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
1097 pthread_mutex_unlock(&chunk
->lock
);
1102 enum lttng_trace_chunk_status
1103 lttng_trace_chunk_get_session_output_directory_handle(
1104 struct lttng_trace_chunk
*chunk
,
1105 struct lttng_directory_handle
**handle
)
1107 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1109 pthread_mutex_lock(&chunk
->lock
);
1110 if (!chunk
->session_output_directory
) {
1111 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1115 const bool reference_acquired
= lttng_directory_handle_get(
1116 chunk
->session_output_directory
);
1118 assert(reference_acquired
);
1119 *handle
= chunk
->session_output_directory
;
1122 pthread_mutex_unlock(&chunk
->lock
);
1127 enum lttng_trace_chunk_status
lttng_trace_chunk_borrow_chunk_directory_handle(
1128 struct lttng_trace_chunk
*chunk
,
1129 const struct lttng_directory_handle
**handle
)
1131 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1133 pthread_mutex_lock(&chunk
->lock
);
1134 if (!chunk
->chunk_directory
) {
1135 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1139 *handle
= chunk
->chunk_directory
;
1141 pthread_mutex_unlock(&chunk
->lock
);
1145 /* Add a top-level directory to the trace chunk if it was previously unknown. */
1147 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
1148 const char *new_path
)
1152 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
1153 &chunk
->top_level_directories
);
1154 const char *new_path_separator_pos
= strchr(new_path
, '/');
1155 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
1156 new_path_separator_pos
- new_path
: strlen(new_path
);
1158 for (i
= 0; i
< count
; i
++) {
1159 const char *path
= lttng_dynamic_pointer_array_get_pointer(
1160 &chunk
->top_level_directories
, i
);
1161 const ptrdiff_t path_top_level_len
= strlen(path
);
1163 if (path_top_level_len
!= new_path_top_level_len
) {
1166 if (!strncmp(path
, new_path
, path_top_level_len
)) {
1173 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
1175 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1176 new_path
, chunk
->name
? : "(unnamed)");
1178 PERROR("Failed to copy path");
1182 ret
= lttng_dynamic_pointer_array_add_pointer(
1183 &chunk
->top_level_directories
, copy
);
1185 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1195 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
1196 struct lttng_trace_chunk
*chunk
,
1200 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1202 DBG("Creating trace chunk subdirectory \"%s\"", path
);
1203 pthread_mutex_lock(&chunk
->lock
);
1204 if (!chunk
->credentials
.is_set
) {
1206 * Fatal error, credentials must be set before a
1207 * directory is created.
1209 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1211 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1214 if (!chunk
->mode
.is_set
||
1215 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
1216 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1218 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1221 if (!chunk
->chunk_directory
) {
1222 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1224 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1228 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1230 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1233 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
1234 chunk
->chunk_directory
, path
,
1236 chunk
->credentials
.value
.use_current_user
?
1237 NULL
: &chunk
->credentials
.value
.user
);
1239 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1241 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1244 ret
= add_top_level_directory_unique(chunk
, path
);
1246 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1250 pthread_mutex_unlock(&chunk
->lock
);
1255 * TODO: Implement O(1) lookup.
1258 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk
*chunk
,
1259 const char *path
, size_t *index
)
1263 count
= lttng_dynamic_pointer_array_get_count(&chunk
->files
);
1264 for (i
= 0; i
< count
; i
++) {
1265 const char *iter_path
=
1266 lttng_dynamic_pointer_array_get_pointer(
1268 if (!strcmp(iter_path
, path
)) {
1279 enum lttng_trace_chunk_status
lttng_trace_chunk_add_file(
1280 struct lttng_trace_chunk
*chunk
,
1285 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1287 if (lttng_trace_chunk_find_file(chunk
, path
, NULL
)) {
1288 return LTTNG_TRACE_CHUNK_STATUS_OK
;
1290 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1291 path
, chunk
->name
? : "(unnamed)");
1292 copy
= strdup(path
);
1294 PERROR("Failed to copy path");
1295 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1298 ret
= lttng_dynamic_pointer_array_add_pointer(
1299 &chunk
->files
, copy
);
1301 ERR("Allocation failure while adding file to a trace chunk");
1303 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1311 void lttng_trace_chunk_remove_file(
1312 struct lttng_trace_chunk
*chunk
,
1319 found
= lttng_trace_chunk_find_file(chunk
, path
, &index
);
1323 ret
= lttng_dynamic_pointer_array_remove_pointer(
1324 &chunk
->files
, index
);
1329 enum lttng_trace_chunk_status
_lttng_trace_chunk_open_fs_handle_locked(
1330 struct lttng_trace_chunk
*chunk
,
1331 const char *file_path
,
1334 struct fs_handle
**out_handle
,
1335 bool expect_no_file
)
1338 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1340 DBG("Opening trace chunk file \"%s\"", file_path
);
1341 if (!chunk
->credentials
.is_set
) {
1343 * Fatal error, credentials must be set before a
1346 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1348 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1351 if (!chunk
->chunk_directory
) {
1352 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1354 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1357 status
= lttng_trace_chunk_add_file(chunk
, file_path
);
1358 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1361 if (chunk
->fd_tracker
) {
1362 assert(chunk
->credentials
.value
.use_current_user
);
1363 *out_handle
= fd_tracker_open_fs_handle(chunk
->fd_tracker
,
1364 chunk
->chunk_directory
, file_path
, flags
, &mode
);
1365 ret
= *out_handle
? 0 : -1;
1367 ret
= lttng_directory_handle_open_file_as_user(
1368 chunk
->chunk_directory
, file_path
, flags
, mode
,
1369 chunk
->credentials
.value
.use_current_user
?
1371 &chunk
->credentials
.value
.user
);
1373 *out_handle
= fs_handle_untracked_create(
1374 chunk
->chunk_directory
, file_path
, ret
);
1376 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1382 if (errno
== ENOENT
&& expect_no_file
) {
1383 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
1385 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1386 file_path
, flags
, (int) mode
);
1387 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1389 lttng_trace_chunk_remove_file(chunk
, file_path
);
1397 enum lttng_trace_chunk_status
lttng_trace_chunk_open_fs_handle(
1398 struct lttng_trace_chunk
*chunk
,
1399 const char *file_path
,
1402 struct fs_handle
**out_handle
,
1403 bool expect_no_file
)
1405 enum lttng_trace_chunk_status status
;
1407 pthread_mutex_lock(&chunk
->lock
);
1408 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1409 flags
, mode
, out_handle
, expect_no_file
);
1410 pthread_mutex_unlock(&chunk
->lock
);
1415 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
1416 struct lttng_trace_chunk
*chunk
,
1417 const char *file_path
,
1421 bool expect_no_file
)
1423 enum lttng_trace_chunk_status status
;
1424 struct fs_handle
*fs_handle
;
1426 pthread_mutex_lock(&chunk
->lock
);
1428 * Using this method is never valid when an fd_tracker is being
1429 * used since the resulting file descriptor would not be tracked.
1431 assert(!chunk
->fd_tracker
);
1432 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1433 flags
, mode
, &fs_handle
, expect_no_file
);
1434 pthread_mutex_unlock(&chunk
->lock
);
1436 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
1437 *out_fd
= fs_handle_get_fd(fs_handle
);
1439 * Does not close the fd; we just "unbox" it from the fs_handle.
1441 fs_handle_untracked_destroy(container_of(
1442 fs_handle
, struct fs_handle_untracked
, parent
));
1449 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
1450 const char *file_path
)
1453 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1455 DBG("Unlinking trace chunk file \"%s\"", file_path
);
1456 pthread_mutex_lock(&chunk
->lock
);
1457 if (!chunk
->credentials
.is_set
) {
1459 * Fatal error, credentials must be set before a
1462 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1464 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1467 if (!chunk
->chunk_directory
) {
1468 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1470 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1473 ret
= lttng_directory_handle_unlink_file_as_user(
1474 chunk
->chunk_directory
, file_path
,
1475 chunk
->credentials
.value
.use_current_user
?
1476 NULL
: &chunk
->credentials
.value
.user
);
1478 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1481 lttng_trace_chunk_remove_file(chunk
, file_path
);
1483 pthread_mutex_unlock(&chunk
->lock
);
1488 int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk
*chunk
,
1492 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1494 DBG("Recursively removing trace chunk directory \"%s\"", path
);
1495 pthread_mutex_lock(&chunk
->lock
);
1496 if (!chunk
->credentials
.is_set
) {
1498 * Fatal error, credentials must be set before a
1499 * directory is removed.
1501 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1503 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1506 if (!chunk
->chunk_directory
) {
1507 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1509 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1512 ret
= lttng_directory_handle_remove_subdirectory_recursive_as_user(
1513 chunk
->chunk_directory
, path
,
1514 chunk
->credentials
.value
.use_current_user
?
1515 NULL
: &chunk
->credentials
.value
.user
,
1516 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
);
1518 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1522 pthread_mutex_unlock(&chunk
->lock
);
1527 int lttng_trace_chunk_move_to_completed_post_release(
1528 struct lttng_trace_chunk
*trace_chunk
)
1531 char *archived_chunk_name
= NULL
;
1532 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
1533 const time_t creation_timestamp
=
1534 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
1535 const time_t close_timestamp
=
1536 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
1537 struct lttng_directory_handle
*archived_chunks_directory
= NULL
;
1538 enum lttng_trace_chunk_status status
;
1540 if (!trace_chunk
->mode
.is_set
||
1541 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
1542 !trace_chunk
->session_output_directory
) {
1544 * This command doesn't need to run if the output is remote
1545 * or if the trace chunk is not owned by this process.
1550 assert(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
1551 assert(!trace_chunk
->name_overridden
);
1552 assert(trace_chunk
->path
);
1554 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
1556 if (!archived_chunk_name
) {
1557 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1562 ret
= lttng_directory_handle_create_subdirectory_as_user(
1563 trace_chunk
->session_output_directory
,
1564 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1566 !trace_chunk
->credentials
.value
.use_current_user
?
1567 &trace_chunk
->credentials
.value
.user
:
1570 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1571 "\" directory for archived trace chunks");
1575 archived_chunks_directory
= lttng_directory_handle_create_from_handle(
1576 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1577 trace_chunk
->session_output_directory
);
1578 if (!archived_chunks_directory
) {
1579 PERROR("Failed to get handle to archived trace chunks directory");
1585 * Make sure chunk is renamed to old directory if not already done by
1586 * the creation of the next chunk. This happens if a rotation is
1587 * performed while tracing is stopped.
1589 if (!trace_chunk
->path
|| strcmp(trace_chunk
->path
,
1590 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
)) {
1591 status
= lttng_trace_chunk_rename_path_no_lock(trace_chunk
,
1592 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1593 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1594 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1600 ret
= lttng_directory_handle_rename_as_user(
1601 trace_chunk
->session_output_directory
,
1603 archived_chunks_directory
,
1604 archived_chunk_name
,
1605 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1607 &trace_chunk
->credentials
.value
.user
);
1609 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1611 archived_chunk_name
);
1615 lttng_directory_handle_put(archived_chunks_directory
);
1616 free(archived_chunk_name
);
1621 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
)
1627 int lttng_trace_chunk_delete_post_release_user(
1628 struct lttng_trace_chunk
*trace_chunk
)
1632 DBG("Trace chunk \"delete\" close command post-release (User)");
1634 /* Unlink all files. */
1635 while (lttng_dynamic_pointer_array_get_count(&trace_chunk
->files
) != 0) {
1636 enum lttng_trace_chunk_status status
;
1640 path
= lttng_dynamic_pointer_array_get_pointer(
1641 &trace_chunk
->files
, 0);
1642 DBG("Unlink file: %s", path
);
1643 status
= lttng_trace_chunk_unlink_file(trace_chunk
, path
);
1644 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1645 ERR("Error unlinking file '%s' when deleting chunk", path
);
1655 int lttng_trace_chunk_delete_post_release_owner(
1656 struct lttng_trace_chunk
*trace_chunk
)
1658 enum lttng_trace_chunk_status status
;
1662 ret
= lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1667 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1669 assert(trace_chunk
->session_output_directory
);
1670 assert(trace_chunk
->chunk_directory
);
1672 /* Remove empty directories. */
1673 count
= lttng_dynamic_pointer_array_get_count(
1674 &trace_chunk
->top_level_directories
);
1676 for (i
= 0; i
< count
; i
++) {
1677 const char *top_level_name
=
1678 lttng_dynamic_pointer_array_get_pointer(
1679 &trace_chunk
->top_level_directories
, i
);
1681 status
= lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk
, top_level_name
);
1682 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1683 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1690 lttng_directory_handle_put(trace_chunk
->chunk_directory
);
1691 trace_chunk
->chunk_directory
= NULL
;
1693 if (trace_chunk
->path
&& trace_chunk
->path
[0] != '\0') {
1694 status
= lttng_directory_handle_remove_subdirectory(
1695 trace_chunk
->session_output_directory
,
1697 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1698 ERR("Error removing subdirectory '%s' file when deleting chunk",
1704 free(trace_chunk
->path
);
1705 trace_chunk
->path
= NULL
;
1711 * For local files, session and consumer daemons all run the delete hook. The
1712 * consumer daemons have the list of files to unlink, and technically the
1713 * session daemon is the owner of the chunk. Unlink all files owned by each
1717 int lttng_trace_chunk_delete_post_release(
1718 struct lttng_trace_chunk
*trace_chunk
)
1720 if (!trace_chunk
->chunk_directory
) {
1724 if (trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
) {
1725 return lttng_trace_chunk_delete_post_release_owner(trace_chunk
);
1727 return lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1732 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
1733 struct lttng_trace_chunk
*chunk
,
1734 enum lttng_trace_chunk_command_type
*command_type
)
1736 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1738 pthread_mutex_lock(&chunk
->lock
);
1739 if (chunk
->close_command
.is_set
) {
1740 *command_type
= chunk
->close_command
.value
;
1741 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1743 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1745 pthread_mutex_unlock(&chunk
->lock
);
1750 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
1751 struct lttng_trace_chunk
*chunk
,
1752 enum lttng_trace_chunk_command_type close_command
)
1754 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1756 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1757 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1758 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1762 pthread_mutex_lock(&chunk
->lock
);
1763 if (chunk
->close_command
.is_set
) {
1764 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1765 close_command_names
[chunk
->close_command
.value
],
1766 close_command_names
[close_command
]);
1768 DBG("Setting trace chunk close command to \"%s\"",
1769 close_command_names
[close_command
]);
1772 * Unset close command for no-op for backward compatibility with relayd
1775 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1776 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1778 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1780 pthread_mutex_unlock(&chunk
->lock
);
1786 const char *lttng_trace_chunk_command_type_get_name(
1787 enum lttng_trace_chunk_command_type command
)
1790 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1791 return "move to completed trace chunk folder";
1792 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1793 return "no operation";
1794 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1802 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1804 return urcu_ref_get_unless_zero(&chunk
->ref
);
1808 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1810 struct lttng_trace_chunk_registry_element
*element
=
1811 container_of(node
, typeof(*element
), rcu_node
);
1813 lttng_trace_chunk_fini(&element
->chunk
);
1818 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1820 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1823 if (chunk
->close_command
.is_set
) {
1824 if (close_command_post_release_funcs
[
1825 chunk
->close_command
.value
](chunk
)) {
1826 ERR("Trace chunk post-release command %s has failed.",
1827 close_command_names
[chunk
->close_command
.value
]);
1831 if (chunk
->in_registry_element
) {
1832 struct lttng_trace_chunk_registry_element
*element
;
1834 element
= container_of(chunk
, typeof(*element
), chunk
);
1835 if (element
->registry
) {
1837 cds_lfht_del(element
->registry
->ht
,
1838 &element
->trace_chunk_registry_ht_node
);
1840 call_rcu(&element
->rcu_node
,
1841 free_lttng_trace_chunk_registry_element
);
1843 /* Never published, can be free'd immediately. */
1844 free_lttng_trace_chunk_registry_element(
1845 &element
->rcu_node
);
1848 /* Not RCU-protected, free immediately. */
1849 lttng_trace_chunk_fini(chunk
);
1855 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1860 assert(chunk
->ref
.refcount
);
1861 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1865 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1867 struct lttng_trace_chunk_registry
*registry
;
1869 registry
= zmalloc(sizeof(*registry
));
1874 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1875 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1876 if (!registry
->ht
) {
1882 lttng_trace_chunk_registry_destroy(registry
);
1887 void lttng_trace_chunk_registry_destroy(
1888 struct lttng_trace_chunk_registry
*registry
)
1894 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1901 struct lttng_trace_chunk_registry_element
*
1902 lttng_trace_chunk_registry_element_create_from_chunk(
1903 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1905 struct lttng_trace_chunk_registry_element
*element
=
1906 zmalloc(sizeof(*element
));
1911 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1912 element
->session_id
= session_id
;
1914 element
->chunk
= *chunk
;
1915 lttng_trace_chunk_init(&element
->chunk
);
1916 if (chunk
->session_output_directory
) {
1917 /* Transferred ownership. */
1918 element
->chunk
.session_output_directory
=
1919 chunk
->session_output_directory
;
1920 chunk
->session_output_directory
= NULL
;
1922 if (chunk
->chunk_directory
) {
1923 /* Transferred ownership. */
1924 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1925 chunk
->chunk_directory
= NULL
;
1928 * The original chunk becomes invalid; the name and path attributes are
1929 * transferred to the new chunk instance.
1933 element
->chunk
.fd_tracker
= chunk
->fd_tracker
;
1934 element
->chunk
.in_registry_element
= true;
1940 struct lttng_trace_chunk
*
1941 lttng_trace_chunk_registry_publish_chunk(
1942 struct lttng_trace_chunk_registry
*registry
,
1943 uint64_t session_id
, struct lttng_trace_chunk
*chunk
)
1945 struct lttng_trace_chunk_registry_element
*element
;
1946 unsigned long element_hash
;
1948 pthread_mutex_lock(&chunk
->lock
);
1949 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
1951 pthread_mutex_unlock(&chunk
->lock
);
1956 * chunk is now invalid, the only valid operation is a 'put' from the
1960 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1964 struct cds_lfht_node
*published_node
;
1965 struct lttng_trace_chunk
*published_chunk
;
1966 struct lttng_trace_chunk_registry_element
*published_element
;
1968 published_node
= cds_lfht_add_unique(registry
->ht
,
1970 lttng_trace_chunk_registry_element_match
,
1972 &element
->trace_chunk_registry_ht_node
);
1973 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
1974 /* Successfully published the new element. */
1975 element
->registry
= registry
;
1976 /* Acquire a reference for the caller. */
1977 if (lttng_trace_chunk_get(&element
->chunk
)) {
1981 * Another thread concurrently unpublished the
1982 * trace chunk. This is currently unexpected.
1984 * Re-attempt to publish.
1986 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1992 * An equivalent trace chunk was published before this trace
1993 * chunk. Attempt to acquire a reference to the one that was
1994 * already published and release the reference to the copy we
1995 * created if successful.
1997 published_element
= container_of(published_node
,
1998 typeof(*published_element
),
1999 trace_chunk_registry_ht_node
);
2000 published_chunk
= &published_element
->chunk
;
2001 if (lttng_trace_chunk_get(published_chunk
)) {
2002 lttng_trace_chunk_put(&element
->chunk
);
2003 element
= published_element
;
2007 * A reference to the previously published trace chunk could not
2008 * be acquired. Hence, retry to publish our copy of the trace
2014 return element
? &element
->chunk
: NULL
;
2018 * Note that the caller must be registered as an RCU thread.
2019 * However, it does not need to hold the RCU read lock. The RCU read lock is
2020 * acquired to perform the look-up in the registry's hash table and held until
2021 * after a reference to the "found" trace chunk is acquired.
2023 * IOW, holding a reference guarantees the existence of the object for the
2027 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
2028 const struct lttng_trace_chunk_registry
*registry
,
2029 uint64_t session_id
, uint64_t *chunk_id
)
2031 const struct lttng_trace_chunk_registry_element target_element
= {
2032 .chunk
.id
.is_set
= !!chunk_id
,
2033 .chunk
.id
.value
= chunk_id
? *chunk_id
: 0,
2034 .session_id
= session_id
,
2036 const unsigned long element_hash
=
2037 lttng_trace_chunk_registry_element_hash(
2039 struct cds_lfht_node
*published_node
;
2040 struct lttng_trace_chunk_registry_element
*published_element
;
2041 struct lttng_trace_chunk
*published_chunk
= NULL
;
2042 struct cds_lfht_iter iter
;
2045 cds_lfht_lookup(registry
->ht
,
2047 lttng_trace_chunk_registry_element_match
,
2050 published_node
= cds_lfht_iter_get_node(&iter
);
2051 if (!published_node
) {
2055 published_element
= container_of(published_node
,
2056 typeof(*published_element
),
2057 trace_chunk_registry_ht_node
);
2058 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
2059 published_chunk
= &published_element
->chunk
;
2063 return published_chunk
;
2067 struct lttng_trace_chunk
*
2068 lttng_trace_chunk_registry_find_chunk(
2069 const struct lttng_trace_chunk_registry
*registry
,
2070 uint64_t session_id
, uint64_t chunk_id
)
2072 return _lttng_trace_chunk_registry_find_chunk(registry
,
2073 session_id
, &chunk_id
);
2077 int lttng_trace_chunk_registry_chunk_exists(
2078 const struct lttng_trace_chunk_registry
*registry
,
2079 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
2082 const struct lttng_trace_chunk_registry_element target_element
= {
2083 .chunk
.id
.is_set
= true,
2084 .chunk
.id
.value
= chunk_id
,
2085 .session_id
= session_id
,
2087 const unsigned long element_hash
=
2088 lttng_trace_chunk_registry_element_hash(
2090 struct cds_lfht_node
*published_node
;
2091 struct cds_lfht_iter iter
;
2094 cds_lfht_lookup(registry
->ht
,
2096 lttng_trace_chunk_registry_element_match
,
2099 published_node
= cds_lfht_iter_get_node(&iter
);
2100 if (!published_node
) {
2101 *chunk_exists
= false;
2105 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
2112 struct lttng_trace_chunk
*
2113 lttng_trace_chunk_registry_find_anonymous_chunk(
2114 const struct lttng_trace_chunk_registry
*registry
,
2115 uint64_t session_id
)
2117 return _lttng_trace_chunk_registry_find_chunk(registry
,
2122 unsigned int lttng_trace_chunk_registry_put_each_chunk(
2123 const struct lttng_trace_chunk_registry
*registry
)
2125 struct cds_lfht_iter iter
;
2126 struct lttng_trace_chunk_registry_element
*chunk_element
;
2127 unsigned int trace_chunks_left
= 0;
2129 DBG("Releasing trace chunk registry to all trace chunks");
2131 cds_lfht_for_each_entry(registry
->ht
,
2132 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
2133 const char *chunk_id_str
= "none";
2134 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
2136 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
2137 if (chunk_element
->chunk
.id
.is_set
) {
2140 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
2142 chunk_element
->chunk
.id
.value
);
2143 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
2144 chunk_id_str
= "formatting error";
2146 chunk_id_str
= chunk_id_buf
;
2150 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
2151 "chunk_id = %s, name = \"%s\", status = %s",
2152 chunk_element
->session_id
,
2154 chunk_element
->chunk
.name
? : "none",
2155 chunk_element
->chunk
.close_command
.is_set
?
2157 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
2158 lttng_trace_chunk_put(&chunk_element
->chunk
);
2159 trace_chunks_left
++;
2162 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
2165 return trace_chunks_left
;