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/fd-tracker/utils.h>
15 #include <common/fs-handle-internal.h>
16 #include <common/hashtable/hashtable.h>
17 #include <common/hashtable/utils.h>
18 #include <common/optional.h>
19 #include <common/string-utils/format.h>
20 #include <common/time.h>
21 #include <common/trace-chunk-registry.h>
22 #include <common/trace-chunk.h>
23 #include <common/utils.h>
24 #include <lttng/constant.h>
30 #include <urcu/rculfhash.h>
34 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
35 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
37 #define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
38 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
40 enum trace_chunk_mode
{
41 TRACE_CHUNK_MODE_USER
,
42 TRACE_CHUNK_MODE_OWNER
,
46 * Callback to invoke on release of a trace chunk. Note that there is no
47 * need to 'lock' the trace chunk during the execution of these callbacks
48 * since only one thread may access a chunk during its destruction (the last
49 * to release its reference to the chunk).
51 typedef int (*chunk_command
)(struct lttng_trace_chunk
*trace_chunk
);
53 /* Move a completed trace chunk to the 'completed' trace archive folder. */
55 int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk
*trace_chunk
);
58 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
);
59 /* Unlink old chunk files. */
61 int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk
*trace_chunk
);
63 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
64 struct lttng_trace_chunk
*chunk
, const char *path
);
66 struct chunk_credentials
{
67 bool use_current_user
;
68 struct lttng_credentials user
;
72 * NOTE: Make sure to update:
73 * - lttng_trace_chunk_copy(),
74 * - lttng_trace_chunk_registry_element_create_from_chunk()
75 * if you modify this structure.
77 struct lttng_trace_chunk
{
80 LTTNG_OPTIONAL(enum trace_chunk_mode
) mode
;
82 * First-level directories created within the trace chunk.
83 * Elements are of type 'char *'.
85 * Only used by _owner_ mode chunks.
87 struct lttng_dynamic_pointer_array top_level_directories
;
89 * All files contained within the trace chunk.
90 * Array of paths (char *).
92 struct lttng_dynamic_pointer_array files
;
93 /* Is contained within an lttng_trace_chunk_registry_element? */
94 bool in_registry_element
;
98 /* An unset id means the chunk is anonymous. */
99 LTTNG_OPTIONAL(uint64_t) id
;
102 * The creation and close timestamps are NOT monotonic.
103 * They must not be used in context were monotonicity is required.
105 LTTNG_OPTIONAL(time_t) timestamp_creation
;
106 LTTNG_OPTIONAL(time_t) timestamp_close
;
108 LTTNG_OPTIONAL(struct chunk_credentials
) credentials
;
109 struct lttng_directory_handle
*session_output_directory
;
110 struct lttng_directory_handle
*chunk_directory
;
111 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type
) close_command
;
113 * fd_tracker instance through which file descriptors should be
116 * An fd_tracker always outlives any trace chunk; there is no
117 * need to perform any reference counting of that object.
119 struct fd_tracker
*fd_tracker
;
122 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
123 struct lttng_trace_chunk_registry_element
{
124 struct lttng_trace_chunk chunk
;
126 /* Weak and only set when added. */
127 struct lttng_trace_chunk_registry
*registry
;
128 struct cds_lfht_node trace_chunk_registry_ht_node
;
129 /* call_rcu delayed reclaim. */
130 struct rcu_head rcu_node
;
133 struct lttng_trace_chunk_registry
{
137 struct fs_handle_untracked
{
138 struct fs_handle parent
;
141 struct lttng_directory_handle
*directory_handle
;
147 int fs_handle_untracked_get_fd(struct fs_handle
*handle
);
149 void fs_handle_untracked_put_fd(struct fs_handle
*handle
);
151 int fs_handle_untracked_unlink(struct fs_handle
*handle
);
153 int fs_handle_untracked_close(struct fs_handle
*handle
);
156 char *close_command_names
[] = {
157 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
158 "move to completed chunk folder",
159 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
161 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
166 chunk_command close_command_post_release_funcs
[] = {
167 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
168 lttng_trace_chunk_move_to_completed_post_release
,
169 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
170 lttng_trace_chunk_no_operation
,
171 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
172 lttng_trace_chunk_delete_post_release
,
176 struct fs_handle
*fs_handle_untracked_create(
177 struct lttng_directory_handle
*directory_handle
,
181 struct fs_handle_untracked
*handle
= NULL
;
182 bool reference_acquired
;
183 char *path_copy
= strdup(path
);
185 LTTNG_ASSERT(fd
>= 0);
187 PERROR("Failed to copy file path while creating untracked filesystem handle");
191 handle
= zmalloc(sizeof(typeof(*handle
)));
193 PERROR("Failed to allocate untracked filesystem handle");
197 handle
->parent
= (typeof(handle
->parent
)) {
198 .get_fd
= fs_handle_untracked_get_fd
,
199 .put_fd
= fs_handle_untracked_put_fd
,
200 .unlink
= fs_handle_untracked_unlink
,
201 .close
= fs_handle_untracked_close
,
205 reference_acquired
= lttng_directory_handle_get(directory_handle
);
206 LTTNG_ASSERT(reference_acquired
);
207 handle
->location
.directory_handle
= directory_handle
;
208 /* Ownership is transferred. */
209 handle
->location
.path
= path_copy
;
213 return handle
? &handle
->parent
: NULL
;
217 int fs_handle_untracked_get_fd(struct fs_handle
*_handle
)
219 struct fs_handle_untracked
*handle
= container_of(
220 _handle
, struct fs_handle_untracked
, parent
);
226 void fs_handle_untracked_put_fd(struct fs_handle
*_handle
)
232 int fs_handle_untracked_unlink(struct fs_handle
*_handle
)
234 struct fs_handle_untracked
*handle
= container_of(
235 _handle
, struct fs_handle_untracked
, parent
);
237 return lttng_directory_handle_unlink_file(
238 handle
->location
.directory_handle
,
239 handle
->location
.path
);
243 void fs_handle_untracked_destroy(struct fs_handle_untracked
*handle
)
245 lttng_directory_handle_put(handle
->location
.directory_handle
);
246 free(handle
->location
.path
);
251 int fs_handle_untracked_close(struct fs_handle
*_handle
)
253 struct fs_handle_untracked
*handle
= container_of(
254 _handle
, struct fs_handle_untracked
, parent
);
255 int ret
= close(handle
->fd
);
257 fs_handle_untracked_destroy(handle
);
262 bool lttng_trace_chunk_registry_element_equals(
263 const struct lttng_trace_chunk_registry_element
*a
,
264 const struct lttng_trace_chunk_registry_element
*b
)
266 if (a
->session_id
!= b
->session_id
) {
269 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
272 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
281 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
,
284 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
286 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
287 element_b
= caa_container_of(node
, typeof(*element_b
),
288 trace_chunk_registry_ht_node
);
289 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
293 unsigned long lttng_trace_chunk_registry_element_hash(
294 const struct lttng_trace_chunk_registry_element
*element
)
296 unsigned long hash
= hash_key_u64(&element
->session_id
,
299 if (element
->chunk
.id
.is_set
) {
300 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
307 char *generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
,
308 const time_t *close_timestamp
)
311 char *new_name
= NULL
;
312 char start_datetime
[ISO8601_STR_LEN
] = {};
313 /* Add 1 for a '-' prefix. */
314 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
316 ret
= time_to_iso8601_str(
318 start_datetime
, sizeof(start_datetime
));
320 ERR("Failed to format trace chunk start date time");
323 if (close_timestamp
) {
324 *end_datetime_suffix
= '-';
325 ret
= time_to_iso8601_str(
327 end_datetime_suffix
+ 1,
328 sizeof(end_datetime_suffix
) - 1);
330 ERR("Failed to format trace chunk end date time");
334 new_name
= zmalloc(GENERATED_CHUNK_NAME_LEN
);
336 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
339 ret
= snprintf(new_name
, GENERATED_CHUNK_NAME_LEN
, "%s%s-%" PRIu64
,
340 start_datetime
, end_datetime_suffix
, chunk_id
);
341 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
342 ERR("Failed to format trace chunk name");
353 void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
355 urcu_ref_init(&chunk
->ref
);
356 pthread_mutex_init(&chunk
->lock
, NULL
);
357 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
358 lttng_dynamic_pointer_array_init(&chunk
->files
, free
);
362 void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
364 if (chunk
->session_output_directory
) {
365 lttng_directory_handle_put(
366 chunk
->session_output_directory
);
367 chunk
->session_output_directory
= NULL
;
369 if (chunk
->chunk_directory
) {
370 lttng_directory_handle_put(chunk
->chunk_directory
);
371 chunk
->chunk_directory
= NULL
;
377 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
378 lttng_dynamic_pointer_array_reset(&chunk
->files
);
379 pthread_mutex_destroy(&chunk
->lock
);
383 struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
385 struct lttng_trace_chunk
*chunk
= NULL
;
387 chunk
= zmalloc(sizeof(*chunk
));
389 ERR("Failed to allocate trace chunk");
392 lttng_trace_chunk_init(chunk
);
397 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
399 DBG("Creating anonymous trace chunk");
400 return lttng_trace_chunk_allocate();
403 struct lttng_trace_chunk
*lttng_trace_chunk_create(
404 uint64_t chunk_id
, time_t chunk_creation_time
, const char *path
)
406 struct lttng_trace_chunk
*chunk
;
407 char chunk_creation_datetime_buf
[16] = {};
408 const char *chunk_creation_datetime_str
= "(formatting error)";
409 struct tm timeinfo_buf
, *timeinfo
;
411 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
415 /* Don't fail because of this; it is only used for logging. */
416 strftime_ret
= strftime(chunk_creation_datetime_buf
,
417 sizeof(chunk_creation_datetime_buf
),
418 "%Y%m%d-%H%M%S", timeinfo
);
420 chunk_creation_datetime_str
=
421 chunk_creation_datetime_buf
;
425 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
426 chunk_id
, chunk_creation_datetime_str
);
427 chunk
= lttng_trace_chunk_allocate();
432 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
433 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
435 chunk
->name
= generate_chunk_name(chunk_id
,
436 chunk_creation_time
, NULL
);
438 ERR("Failed to allocate trace chunk name storage");
443 chunk
->path
= strdup(path
);
449 chunk
->path
= strdup(chunk
->name
);
456 DBG("Chunk name set to \"%s\"", chunk
->name
? : "(none)");
460 lttng_trace_chunk_put(chunk
);
464 void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk
*chunk
,
465 struct fd_tracker
*fd_tracker
)
467 LTTNG_ASSERT(!chunk
->session_output_directory
);
468 LTTNG_ASSERT(!chunk
->chunk_directory
);
469 LTTNG_ASSERT(lttng_dynamic_pointer_array_get_count(&chunk
->files
) == 0);
470 chunk
->fd_tracker
= fd_tracker
;
473 struct lttng_trace_chunk
*lttng_trace_chunk_copy(
474 struct lttng_trace_chunk
*source_chunk
)
476 struct lttng_trace_chunk
*new_chunk
= lttng_trace_chunk_allocate();
482 pthread_mutex_lock(&source_chunk
->lock
);
484 * A new chunk is always a user; it shall create no new trace
487 new_chunk
->mode
= (typeof(new_chunk
->mode
)) {
489 .value
= TRACE_CHUNK_MODE_USER
,
492 * top_level_directories is not copied as it is never used
493 * by _user_ mode chunks.
495 /* The new chunk is not part of a registry (yet, at least). */
496 new_chunk
->in_registry_element
= false;
497 new_chunk
->name_overridden
= source_chunk
->name_overridden
;
498 if (source_chunk
->name
) {
499 new_chunk
->name
= strdup(source_chunk
->name
);
500 if (!new_chunk
->name
) {
501 ERR("Failed to copy source trace chunk name in %s()",
506 if (source_chunk
->path
) {
507 new_chunk
->path
= strdup(source_chunk
->path
);
508 if (!new_chunk
->path
) {
509 ERR("Failed to copy source trace chunk path in %s()",
513 new_chunk
->id
= source_chunk
->id
;
514 new_chunk
->timestamp_creation
= source_chunk
->timestamp_creation
;
515 new_chunk
->timestamp_close
= source_chunk
->timestamp_close
;
516 new_chunk
->credentials
= source_chunk
->credentials
;
517 if (source_chunk
->session_output_directory
) {
518 const bool reference_acquired
= lttng_directory_handle_get(
519 source_chunk
->session_output_directory
);
521 LTTNG_ASSERT(reference_acquired
);
522 new_chunk
->session_output_directory
=
523 source_chunk
->session_output_directory
;
525 if (source_chunk
->chunk_directory
) {
526 const bool reference_acquired
= lttng_directory_handle_get(
527 source_chunk
->chunk_directory
);
529 LTTNG_ASSERT(reference_acquired
);
530 new_chunk
->chunk_directory
= source_chunk
->chunk_directory
;
532 new_chunk
->close_command
= source_chunk
->close_command
;
533 new_chunk
->fd_tracker
= source_chunk
->fd_tracker
;
534 pthread_mutex_unlock(&source_chunk
->lock
);
538 pthread_mutex_unlock(&source_chunk
->lock
);
539 lttng_trace_chunk_put(new_chunk
);
543 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(
544 struct lttng_trace_chunk
*chunk
, uint64_t *id
)
546 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
548 pthread_mutex_lock(&chunk
->lock
);
549 if (chunk
->id
.is_set
) {
550 *id
= chunk
->id
.value
;
552 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
554 pthread_mutex_unlock(&chunk
->lock
);
558 enum lttng_trace_chunk_status
lttng_trace_chunk_get_creation_timestamp(
559 struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
562 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
564 pthread_mutex_lock(&chunk
->lock
);
565 if (chunk
->timestamp_creation
.is_set
) {
566 *creation_ts
= chunk
->timestamp_creation
.value
;
568 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
570 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
);
589 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(
590 struct lttng_trace_chunk
*chunk
, time_t close_ts
)
592 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
594 pthread_mutex_lock(&chunk
->lock
);
595 if (!chunk
->timestamp_creation
.is_set
) {
596 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
597 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
602 * Note: we do not enforce that the closing timestamp be greater or
603 * equal to the begin timestamp. These timestamps are used for
604 * generating the chunk name and should only be used in context where
605 * the monotonicity of time is not important. The source of those
606 * timestamps is NOT monotonic and represent the system calendar time,
607 * also know as the wall time.
609 if (chunk
->timestamp_creation
.value
> close_ts
) {
610 WARN("Set trace chunk close timestamp: close timestamp is before creation timestamp, begin : %ld, close : %ld",
611 chunk
->timestamp_creation
.value
, close_ts
);
614 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
615 if (!chunk
->name_overridden
) {
617 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
618 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
621 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
625 pthread_mutex_unlock(&chunk
->lock
);
629 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(
630 struct lttng_trace_chunk
*chunk
, const char **name
,
631 bool *name_overridden
)
633 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
635 pthread_mutex_lock(&chunk
->lock
);
636 if (name_overridden
) {
637 *name_overridden
= chunk
->name_overridden
;
640 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
645 pthread_mutex_unlock(&chunk
->lock
);
649 bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk
*chunk
)
651 bool name_overridden
;
653 pthread_mutex_lock(&chunk
->lock
);
654 name_overridden
= chunk
->name_overridden
;
655 pthread_mutex_unlock(&chunk
->lock
);
656 return name_overridden
;
660 bool is_valid_chunk_name(const char *name
)
668 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
669 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
673 if (strchr(name
, '/') || strchr(name
, '.')) {
680 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
681 struct lttng_trace_chunk
*chunk
, const char *name
)
684 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
685 char *new_name
, *new_path
;
687 DBG("Override trace chunk name from %s to %s", chunk
->name
, name
);
688 if (!is_valid_chunk_name(name
)) {
689 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
691 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
695 pthread_mutex_lock(&chunk
->lock
);
696 if (!chunk
->id
.is_set
) {
697 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
699 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
703 new_name
= strdup(name
);
705 ERR("Failed to allocate new trace chunk name");
706 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
710 chunk
->name
= new_name
;
712 new_path
= strdup(name
);
714 ERR("Failed to allocate new trace chunk path");
715 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
719 chunk
->path
= new_path
;
721 chunk
->name_overridden
= true;
723 pthread_mutex_unlock(&chunk
->lock
);
729 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
730 struct lttng_trace_chunk
*chunk
, const char *path
)
733 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
734 struct lttng_directory_handle
*rename_directory
= NULL
;
735 char *new_path
, *old_path
;
738 if (chunk
->name_overridden
) {
739 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
743 old_path
= chunk
->path
;
744 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path
, path
);
746 if ((!old_path
&& !path
) ||
747 (old_path
&& path
&& !strcmp(old_path
, path
))) {
751 * Use chunk name as path if NULL path is specified.
757 /* Renaming from "" to "" is not accepted. */
758 if (path
[0] == '\0' && old_path
[0] == '\0') {
759 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
764 * If a rename is performed on a chunk for which the chunk_directory
765 * is not set (yet), or the session_output_directory is not set
766 * (interacting with a relay daemon), there is no rename to perform.
768 if (!chunk
->chunk_directory
||
769 !chunk
->session_output_directory
) {
773 if (old_path
&& old_path
[0] != '\0' && path
[0] != '\0') {
774 /* Rename chunk directory. */
775 ret
= lttng_directory_handle_rename_as_user(
776 chunk
->session_output_directory
,
778 chunk
->session_output_directory
,
780 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
782 &chunk
->credentials
.value
.user
);
784 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
786 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
789 rename_directory
= chunk
->fd_tracker
?
790 fd_tracker_create_directory_handle_from_handle(
792 chunk
->session_output_directory
,
794 lttng_directory_handle_create_from_handle(
796 chunk
->session_output_directory
);
797 if (!rename_directory
) {
798 ERR("Failed to get handle to trace chunk rename directory");
799 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
803 /* Release old handle. */
804 lttng_directory_handle_put(chunk
->chunk_directory
);
806 * Transfer new handle reference to chunk as the current chunk
809 chunk
->chunk_directory
= rename_directory
;
810 rename_directory
= NULL
;
811 } else if (old_path
&& old_path
[0] == '\0') {
812 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
813 &chunk
->top_level_directories
);
815 ret
= lttng_directory_handle_create_subdirectory_as_user(
816 chunk
->session_output_directory
,
819 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
821 &chunk
->credentials
.value
.user
);
823 PERROR("Failed to create trace chunk rename directory \"%s\"",
825 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
829 rename_directory
= lttng_directory_handle_create_from_handle(
830 path
, chunk
->session_output_directory
);
831 if (!rename_directory
) {
832 ERR("Failed to get handle to trace chunk rename directory");
833 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
837 /* Move toplevel directories. */
838 for (i
= 0; i
< count
; i
++) {
839 const char *top_level_name
=
840 lttng_dynamic_pointer_array_get_pointer(
841 &chunk
->top_level_directories
, i
);
843 ret
= lttng_directory_handle_rename_as_user(
844 chunk
->chunk_directory
,
848 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
850 &chunk
->credentials
.value
.user
);
852 PERROR("Failed to move \"%s\" to trace chunk rename directory",
854 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
858 /* Release old handle. */
859 lttng_directory_handle_put(chunk
->chunk_directory
);
861 * Transfer new handle reference to chunk as the current chunk
864 chunk
->chunk_directory
= rename_directory
;
865 rename_directory
= NULL
;
866 } else if (old_path
) {
867 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
868 &chunk
->top_level_directories
);
869 const bool reference_acquired
= lttng_directory_handle_get(
870 chunk
->session_output_directory
);
872 LTTNG_ASSERT(reference_acquired
);
873 rename_directory
= chunk
->session_output_directory
;
875 /* Move toplevel directories. */
876 for (i
= 0; i
< count
; i
++) {
877 const char *top_level_name
=
878 lttng_dynamic_pointer_array_get_pointer(
879 &chunk
->top_level_directories
, i
);
881 ret
= lttng_directory_handle_rename_as_user(
882 chunk
->chunk_directory
,
886 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
888 &chunk
->credentials
.value
.user
);
890 PERROR("Failed to move \"%s\" to trace chunk rename directory",
892 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
896 /* Release old handle. */
897 lttng_directory_handle_put(chunk
->chunk_directory
);
899 * Transfer new handle reference to chunk as the current chunk
902 chunk
->chunk_directory
= rename_directory
;
903 rename_directory
= NULL
;
905 /* Remove old directory. */
906 status
= lttng_directory_handle_remove_subdirectory(
907 chunk
->session_output_directory
,
909 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
910 ERR("Error removing subdirectory '%s' file when deleting chunk",
915 /* Unexpected !old_path && !path. */
916 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
921 new_path
= strdup(path
);
923 ERR("Failed to allocate new trace chunk path");
924 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
928 chunk
->path
= new_path
;
930 lttng_directory_handle_put(rename_directory
);
934 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path(
935 struct lttng_trace_chunk
*chunk
, const char *path
)
938 enum lttng_trace_chunk_status status
;
940 pthread_mutex_lock(&chunk
->lock
);
941 status
= lttng_trace_chunk_rename_path_no_lock(chunk
, path
);
942 pthread_mutex_unlock(&chunk
->lock
);
947 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
948 struct lttng_trace_chunk
*chunk
,
949 struct lttng_credentials
*credentials
)
951 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
953 pthread_mutex_lock(&chunk
->lock
);
954 if (chunk
->credentials
.is_set
) {
955 if (chunk
->credentials
.value
.use_current_user
) {
956 LTTNG_OPTIONAL_SET(&credentials
->uid
, geteuid());
957 LTTNG_OPTIONAL_SET(&credentials
->gid
, getegid());
959 *credentials
= chunk
->credentials
.value
.user
;
962 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
964 pthread_mutex_unlock(&chunk
->lock
);
968 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
969 struct lttng_trace_chunk
*chunk
,
970 const struct lttng_credentials
*user_credentials
)
972 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
973 const struct chunk_credentials credentials
= {
974 .user
= *user_credentials
,
975 .use_current_user
= false,
978 pthread_mutex_lock(&chunk
->lock
);
979 if (chunk
->credentials
.is_set
) {
980 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
983 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
985 pthread_mutex_unlock(&chunk
->lock
);
989 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
990 struct lttng_trace_chunk
*chunk
)
992 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
993 const struct chunk_credentials credentials
= {
994 .use_current_user
= true,
997 pthread_mutex_lock(&chunk
->lock
);
998 if (chunk
->credentials
.is_set
) {
999 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1002 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
1004 pthread_mutex_unlock(&chunk
->lock
);
1009 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
1010 struct lttng_trace_chunk
*chunk
,
1011 struct lttng_directory_handle
*session_output_directory
)
1014 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1015 struct lttng_directory_handle
*chunk_directory_handle
= NULL
;
1016 bool reference_acquired
;
1018 pthread_mutex_lock(&chunk
->lock
);
1019 if (chunk
->mode
.is_set
) {
1020 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1023 if (!chunk
->credentials
.is_set
) {
1025 * Fatal error, credentials must be set before a
1026 * directory is created.
1028 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
1029 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1032 if (chunk
->path
&& chunk
->path
[0] != '\0') {
1033 ret
= lttng_directory_handle_create_subdirectory_as_user(
1034 session_output_directory
,
1037 !chunk
->credentials
.value
.use_current_user
?
1038 &chunk
->credentials
.value
.user
: NULL
);
1040 PERROR("Failed to create chunk output directory \"%s\"",
1042 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1045 chunk_directory_handle
=
1047 fd_tracker_create_directory_handle_from_handle(
1049 session_output_directory
,
1051 lttng_directory_handle_create_from_handle(
1053 session_output_directory
);
1054 if (!chunk_directory_handle
) {
1055 /* The function already logs on all error paths. */
1056 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1061 * A nameless chunk does not need its own output directory.
1062 * The session's output directory will be used.
1064 reference_acquired
= lttng_directory_handle_get(
1065 session_output_directory
);
1067 LTTNG_ASSERT(reference_acquired
);
1068 chunk_directory_handle
= session_output_directory
;
1070 chunk
->chunk_directory
= chunk_directory_handle
;
1071 chunk_directory_handle
= NULL
;
1072 reference_acquired
= lttng_directory_handle_get(
1073 session_output_directory
);
1074 LTTNG_ASSERT(reference_acquired
);
1075 chunk
->session_output_directory
= session_output_directory
;
1076 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
1078 pthread_mutex_unlock(&chunk
->lock
);
1082 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
1083 struct lttng_trace_chunk
*chunk
,
1084 struct lttng_directory_handle
*chunk_directory
)
1086 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1087 bool reference_acquired
;
1089 pthread_mutex_lock(&chunk
->lock
);
1090 if (chunk
->mode
.is_set
) {
1091 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1094 if (!chunk
->credentials
.is_set
) {
1095 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
1096 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1099 reference_acquired
= lttng_directory_handle_get(chunk_directory
);
1100 LTTNG_ASSERT(reference_acquired
);
1101 chunk
->chunk_directory
= chunk_directory
;
1102 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
1104 pthread_mutex_unlock(&chunk
->lock
);
1108 enum lttng_trace_chunk_status
1109 lttng_trace_chunk_get_session_output_directory_handle(
1110 struct lttng_trace_chunk
*chunk
,
1111 struct lttng_directory_handle
**handle
)
1113 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1115 pthread_mutex_lock(&chunk
->lock
);
1116 if (!chunk
->session_output_directory
) {
1117 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1121 const bool reference_acquired
= lttng_directory_handle_get(
1122 chunk
->session_output_directory
);
1124 LTTNG_ASSERT(reference_acquired
);
1125 *handle
= chunk
->session_output_directory
;
1128 pthread_mutex_unlock(&chunk
->lock
);
1132 enum lttng_trace_chunk_status
lttng_trace_chunk_borrow_chunk_directory_handle(
1133 struct lttng_trace_chunk
*chunk
,
1134 const struct lttng_directory_handle
**handle
)
1136 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1138 pthread_mutex_lock(&chunk
->lock
);
1139 if (!chunk
->chunk_directory
) {
1140 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1144 *handle
= chunk
->chunk_directory
;
1146 pthread_mutex_unlock(&chunk
->lock
);
1150 /* Add a top-level directory to the trace chunk if it was previously unknown. */
1152 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
1153 const char *new_path
)
1157 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
1158 &chunk
->top_level_directories
);
1159 const char *new_path_separator_pos
= strchr(new_path
, '/');
1160 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
1161 new_path_separator_pos
- new_path
: strlen(new_path
);
1163 for (i
= 0; i
< count
; i
++) {
1164 const char *path
= lttng_dynamic_pointer_array_get_pointer(
1165 &chunk
->top_level_directories
, i
);
1166 const ptrdiff_t path_top_level_len
= strlen(path
);
1168 if (path_top_level_len
!= new_path_top_level_len
) {
1171 if (!strncmp(path
, new_path
, path_top_level_len
)) {
1178 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
1180 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1181 new_path
, chunk
->name
? : "(unnamed)");
1183 PERROR("Failed to copy path");
1187 ret
= lttng_dynamic_pointer_array_add_pointer(
1188 &chunk
->top_level_directories
, copy
);
1190 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1199 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
1200 struct lttng_trace_chunk
*chunk
,
1204 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1206 DBG("Creating trace chunk subdirectory \"%s\"", path
);
1207 pthread_mutex_lock(&chunk
->lock
);
1208 if (!chunk
->credentials
.is_set
) {
1210 * Fatal error, credentials must be set before a
1211 * directory is created.
1213 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1215 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1218 if (!chunk
->mode
.is_set
||
1219 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
1220 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1222 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1225 if (!chunk
->chunk_directory
) {
1226 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1228 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1232 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1234 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1237 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
1238 chunk
->chunk_directory
, path
,
1240 chunk
->credentials
.value
.use_current_user
?
1241 NULL
: &chunk
->credentials
.value
.user
);
1243 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1245 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1248 ret
= add_top_level_directory_unique(chunk
, path
);
1250 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1254 pthread_mutex_unlock(&chunk
->lock
);
1259 * TODO: Implement O(1) lookup.
1262 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk
*chunk
,
1263 const char *path
, size_t *index
)
1267 count
= lttng_dynamic_pointer_array_get_count(&chunk
->files
);
1268 for (i
= 0; i
< count
; i
++) {
1269 const char *iter_path
=
1270 lttng_dynamic_pointer_array_get_pointer(
1272 if (!strcmp(iter_path
, path
)) {
1283 enum lttng_trace_chunk_status
lttng_trace_chunk_add_file(
1284 struct lttng_trace_chunk
*chunk
,
1289 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1291 if (lttng_trace_chunk_find_file(chunk
, path
, NULL
)) {
1292 return LTTNG_TRACE_CHUNK_STATUS_OK
;
1294 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1295 path
, chunk
->name
? : "(unnamed)");
1296 copy
= strdup(path
);
1298 PERROR("Failed to copy path");
1299 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1302 ret
= lttng_dynamic_pointer_array_add_pointer(
1303 &chunk
->files
, copy
);
1305 ERR("Allocation failure while adding file to a trace chunk");
1307 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1315 void lttng_trace_chunk_remove_file(
1316 struct lttng_trace_chunk
*chunk
,
1323 found
= lttng_trace_chunk_find_file(chunk
, path
, &index
);
1327 ret
= lttng_dynamic_pointer_array_remove_pointer(
1328 &chunk
->files
, index
);
1333 enum lttng_trace_chunk_status
_lttng_trace_chunk_open_fs_handle_locked(
1334 struct lttng_trace_chunk
*chunk
,
1335 const char *file_path
,
1338 struct fs_handle
**out_handle
,
1339 bool expect_no_file
)
1342 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1344 DBG("Opening trace chunk file \"%s\"", file_path
);
1345 if (!chunk
->credentials
.is_set
) {
1347 * Fatal error, credentials must be set before a
1350 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1352 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1355 if (!chunk
->chunk_directory
) {
1356 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1358 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1361 status
= lttng_trace_chunk_add_file(chunk
, file_path
);
1362 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1365 if (chunk
->fd_tracker
) {
1366 LTTNG_ASSERT(chunk
->credentials
.value
.use_current_user
);
1367 *out_handle
= fd_tracker_open_fs_handle(chunk
->fd_tracker
,
1368 chunk
->chunk_directory
, file_path
, flags
, &mode
);
1369 ret
= *out_handle
? 0 : -1;
1371 ret
= lttng_directory_handle_open_file_as_user(
1372 chunk
->chunk_directory
, file_path
, flags
, mode
,
1373 chunk
->credentials
.value
.use_current_user
?
1375 &chunk
->credentials
.value
.user
);
1377 *out_handle
= fs_handle_untracked_create(
1378 chunk
->chunk_directory
, file_path
, ret
);
1380 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1386 if (errno
== ENOENT
&& expect_no_file
) {
1387 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
1389 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1390 file_path
, flags
, (int) mode
);
1391 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1393 lttng_trace_chunk_remove_file(chunk
, file_path
);
1400 enum lttng_trace_chunk_status
lttng_trace_chunk_open_fs_handle(
1401 struct lttng_trace_chunk
*chunk
,
1402 const char *file_path
,
1405 struct fs_handle
**out_handle
,
1406 bool expect_no_file
)
1408 enum lttng_trace_chunk_status status
;
1410 pthread_mutex_lock(&chunk
->lock
);
1411 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1412 flags
, mode
, out_handle
, expect_no_file
);
1413 pthread_mutex_unlock(&chunk
->lock
);
1417 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
1418 struct lttng_trace_chunk
*chunk
,
1419 const char *file_path
,
1423 bool expect_no_file
)
1425 enum lttng_trace_chunk_status status
;
1426 struct fs_handle
*fs_handle
;
1428 pthread_mutex_lock(&chunk
->lock
);
1430 * Using this method is never valid when an fd_tracker is being
1431 * used since the resulting file descriptor would not be tracked.
1433 LTTNG_ASSERT(!chunk
->fd_tracker
);
1434 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1435 flags
, mode
, &fs_handle
, expect_no_file
);
1436 pthread_mutex_unlock(&chunk
->lock
);
1438 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
1439 *out_fd
= fs_handle_get_fd(fs_handle
);
1441 * Does not close the fd; we just "unbox" it from the fs_handle.
1443 fs_handle_untracked_destroy(container_of(
1444 fs_handle
, struct fs_handle_untracked
, parent
));
1450 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
1451 const char *file_path
)
1454 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1456 DBG("Unlinking trace chunk file \"%s\"", file_path
);
1457 pthread_mutex_lock(&chunk
->lock
);
1458 if (!chunk
->credentials
.is_set
) {
1460 * Fatal error, credentials must be set before a
1463 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1465 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1468 if (!chunk
->chunk_directory
) {
1469 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1471 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1474 ret
= lttng_directory_handle_unlink_file_as_user(
1475 chunk
->chunk_directory
, file_path
,
1476 chunk
->credentials
.value
.use_current_user
?
1477 NULL
: &chunk
->credentials
.value
.user
);
1479 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1482 lttng_trace_chunk_remove_file(chunk
, file_path
);
1484 pthread_mutex_unlock(&chunk
->lock
);
1489 int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk
*chunk
,
1493 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1495 DBG("Recursively removing trace chunk directory \"%s\"", path
);
1496 pthread_mutex_lock(&chunk
->lock
);
1497 if (!chunk
->credentials
.is_set
) {
1499 * Fatal error, credentials must be set before a
1500 * directory is removed.
1502 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1504 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1507 if (!chunk
->chunk_directory
) {
1508 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1510 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1513 ret
= lttng_directory_handle_remove_subdirectory_recursive_as_user(
1514 chunk
->chunk_directory
, path
,
1515 chunk
->credentials
.value
.use_current_user
?
1516 NULL
: &chunk
->credentials
.value
.user
,
1517 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
);
1519 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1523 pthread_mutex_unlock(&chunk
->lock
);
1528 int lttng_trace_chunk_move_to_completed_post_release(
1529 struct lttng_trace_chunk
*trace_chunk
)
1532 char *archived_chunk_name
= NULL
;
1533 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
1534 const time_t creation_timestamp
=
1535 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
1536 const time_t close_timestamp
=
1537 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
1538 struct lttng_directory_handle
*archived_chunks_directory
= NULL
;
1539 enum lttng_trace_chunk_status status
;
1541 if (!trace_chunk
->mode
.is_set
||
1542 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
1543 !trace_chunk
->session_output_directory
) {
1545 * This command doesn't need to run if the output is remote
1546 * or if the trace chunk is not owned by this process.
1551 LTTNG_ASSERT(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
1552 LTTNG_ASSERT(!trace_chunk
->name_overridden
);
1553 LTTNG_ASSERT(trace_chunk
->path
);
1555 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
1557 if (!archived_chunk_name
) {
1558 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1563 ret
= lttng_directory_handle_create_subdirectory_as_user(
1564 trace_chunk
->session_output_directory
,
1565 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1567 !trace_chunk
->credentials
.value
.use_current_user
?
1568 &trace_chunk
->credentials
.value
.user
:
1571 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1572 "\" directory for archived trace chunks");
1576 archived_chunks_directory
= trace_chunk
->fd_tracker
?
1577 fd_tracker_create_directory_handle_from_handle(
1578 trace_chunk
->fd_tracker
,
1579 trace_chunk
->session_output_directory
,
1580 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
) :
1581 lttng_directory_handle_create_from_handle(
1582 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1583 trace_chunk
->session_output_directory
);
1584 if (!archived_chunks_directory
) {
1585 PERROR("Failed to get handle to archived trace chunks directory");
1591 * Make sure chunk is renamed to old directory if not already done by
1592 * the creation of the next chunk. This happens if a rotation is
1593 * performed while tracing is stopped.
1595 if (!trace_chunk
->path
|| strcmp(trace_chunk
->path
,
1596 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
)) {
1597 status
= lttng_trace_chunk_rename_path_no_lock(trace_chunk
,
1598 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1599 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1600 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1606 ret
= lttng_directory_handle_rename_as_user(
1607 trace_chunk
->session_output_directory
,
1609 archived_chunks_directory
,
1610 archived_chunk_name
,
1611 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1613 &trace_chunk
->credentials
.value
.user
);
1615 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1617 archived_chunk_name
);
1621 lttng_directory_handle_put(archived_chunks_directory
);
1622 free(archived_chunk_name
);
1627 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
)
1633 int lttng_trace_chunk_delete_post_release_user(
1634 struct lttng_trace_chunk
*trace_chunk
)
1638 DBG("Trace chunk \"delete\" close command post-release (User)");
1640 /* Unlink all files. */
1641 while (lttng_dynamic_pointer_array_get_count(&trace_chunk
->files
) != 0) {
1642 enum lttng_trace_chunk_status status
;
1646 path
= lttng_dynamic_pointer_array_get_pointer(
1647 &trace_chunk
->files
, 0);
1648 DBG("Unlink file: %s", path
);
1649 status
= lttng_trace_chunk_unlink_file(trace_chunk
, path
);
1650 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1651 ERR("Error unlinking file '%s' when deleting chunk", path
);
1661 int lttng_trace_chunk_delete_post_release_owner(
1662 struct lttng_trace_chunk
*trace_chunk
)
1664 enum lttng_trace_chunk_status status
;
1668 ret
= lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1673 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1675 LTTNG_ASSERT(trace_chunk
->session_output_directory
);
1676 LTTNG_ASSERT(trace_chunk
->chunk_directory
);
1678 /* Remove empty directories. */
1679 count
= lttng_dynamic_pointer_array_get_count(
1680 &trace_chunk
->top_level_directories
);
1682 for (i
= 0; i
< count
; i
++) {
1683 const char *top_level_name
=
1684 lttng_dynamic_pointer_array_get_pointer(
1685 &trace_chunk
->top_level_directories
, i
);
1687 status
= lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk
, top_level_name
);
1688 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1689 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1696 lttng_directory_handle_put(trace_chunk
->chunk_directory
);
1697 trace_chunk
->chunk_directory
= NULL
;
1699 if (trace_chunk
->path
&& trace_chunk
->path
[0] != '\0') {
1700 status
= lttng_directory_handle_remove_subdirectory(
1701 trace_chunk
->session_output_directory
,
1703 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1704 ERR("Error removing subdirectory '%s' file when deleting chunk",
1710 free(trace_chunk
->path
);
1711 trace_chunk
->path
= NULL
;
1717 * For local files, session and consumer daemons all run the delete hook. The
1718 * consumer daemons have the list of files to unlink, and technically the
1719 * session daemon is the owner of the chunk. Unlink all files owned by each
1723 int lttng_trace_chunk_delete_post_release(
1724 struct lttng_trace_chunk
*trace_chunk
)
1726 if (!trace_chunk
->chunk_directory
) {
1730 if (trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
) {
1731 return lttng_trace_chunk_delete_post_release_owner(trace_chunk
);
1733 return lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1737 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
1738 struct lttng_trace_chunk
*chunk
,
1739 enum lttng_trace_chunk_command_type
*command_type
)
1741 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1743 pthread_mutex_lock(&chunk
->lock
);
1744 if (chunk
->close_command
.is_set
) {
1745 *command_type
= chunk
->close_command
.value
;
1746 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1748 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1750 pthread_mutex_unlock(&chunk
->lock
);
1754 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
1755 struct lttng_trace_chunk
*chunk
,
1756 enum lttng_trace_chunk_command_type close_command
)
1758 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1760 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1761 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1762 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1766 pthread_mutex_lock(&chunk
->lock
);
1767 if (chunk
->close_command
.is_set
) {
1768 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1769 close_command_names
[chunk
->close_command
.value
],
1770 close_command_names
[close_command
]);
1772 DBG("Setting trace chunk close command to \"%s\"",
1773 close_command_names
[close_command
]);
1776 * Unset close command for no-op for backward compatibility with relayd
1779 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1780 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1782 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1784 pthread_mutex_unlock(&chunk
->lock
);
1789 const char *lttng_trace_chunk_command_type_get_name(
1790 enum lttng_trace_chunk_command_type command
)
1793 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1794 return "move to completed trace chunk folder";
1795 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1796 return "no operation";
1797 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1804 bool lttng_trace_chunk_ids_equal(const struct lttng_trace_chunk
*chunk_a
,
1805 const struct lttng_trace_chunk
*chunk_b
)
1809 if (chunk_a
== chunk_b
) {
1814 if (!!chunk_a
^ !!chunk_b
) {
1818 if (chunk_a
->id
.is_set
^ chunk_a
->id
.is_set
) {
1819 /* One id is set and not the other, thus they are not equal. */
1823 if (!chunk_a
->id
.is_set
) {
1824 /* Both ids are unset. */
1827 equal
= chunk_a
->id
.value
== chunk_b
->id
.value
;
1834 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1836 return urcu_ref_get_unless_zero(&chunk
->ref
);
1840 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1842 struct lttng_trace_chunk_registry_element
*element
=
1843 container_of(node
, typeof(*element
), rcu_node
);
1845 lttng_trace_chunk_fini(&element
->chunk
);
1850 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1852 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1855 if (chunk
->close_command
.is_set
) {
1856 if (close_command_post_release_funcs
[
1857 chunk
->close_command
.value
](chunk
)) {
1858 ERR("Trace chunk post-release command %s has failed.",
1859 close_command_names
[chunk
->close_command
.value
]);
1863 if (chunk
->in_registry_element
) {
1864 struct lttng_trace_chunk_registry_element
*element
;
1866 element
= container_of(chunk
, typeof(*element
), chunk
);
1867 if (element
->registry
) {
1869 cds_lfht_del(element
->registry
->ht
,
1870 &element
->trace_chunk_registry_ht_node
);
1872 call_rcu(&element
->rcu_node
,
1873 free_lttng_trace_chunk_registry_element
);
1875 /* Never published, can be free'd immediately. */
1876 free_lttng_trace_chunk_registry_element(
1877 &element
->rcu_node
);
1880 /* Not RCU-protected, free immediately. */
1881 lttng_trace_chunk_fini(chunk
);
1886 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1891 LTTNG_ASSERT(chunk
->ref
.refcount
);
1892 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1895 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1897 struct lttng_trace_chunk_registry
*registry
;
1899 registry
= zmalloc(sizeof(*registry
));
1904 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1905 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1906 if (!registry
->ht
) {
1912 lttng_trace_chunk_registry_destroy(registry
);
1916 void lttng_trace_chunk_registry_destroy(
1917 struct lttng_trace_chunk_registry
*registry
)
1923 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1930 struct lttng_trace_chunk_registry_element
*
1931 lttng_trace_chunk_registry_element_create_from_chunk(
1932 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1934 struct lttng_trace_chunk_registry_element
*element
=
1935 zmalloc(sizeof(*element
));
1940 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1941 element
->session_id
= session_id
;
1943 element
->chunk
= *chunk
;
1944 lttng_trace_chunk_init(&element
->chunk
);
1945 if (chunk
->session_output_directory
) {
1946 /* Transferred ownership. */
1947 element
->chunk
.session_output_directory
=
1948 chunk
->session_output_directory
;
1949 chunk
->session_output_directory
= NULL
;
1951 if (chunk
->chunk_directory
) {
1952 /* Transferred ownership. */
1953 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1954 chunk
->chunk_directory
= NULL
;
1957 * The original chunk becomes invalid; the name and path attributes are
1958 * transferred to the new chunk instance.
1962 element
->chunk
.fd_tracker
= chunk
->fd_tracker
;
1963 element
->chunk
.in_registry_element
= true;
1968 struct lttng_trace_chunk
*
1969 lttng_trace_chunk_registry_publish_chunk(
1970 struct lttng_trace_chunk_registry
*registry
,
1971 uint64_t session_id
, struct lttng_trace_chunk
*chunk
)
1973 struct lttng_trace_chunk_registry_element
*element
;
1974 unsigned long element_hash
;
1976 pthread_mutex_lock(&chunk
->lock
);
1977 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
1979 pthread_mutex_unlock(&chunk
->lock
);
1984 * chunk is now invalid, the only valid operation is a 'put' from the
1988 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1992 struct cds_lfht_node
*published_node
;
1993 struct lttng_trace_chunk
*published_chunk
;
1994 struct lttng_trace_chunk_registry_element
*published_element
;
1996 published_node
= cds_lfht_add_unique(registry
->ht
,
1998 lttng_trace_chunk_registry_element_match
,
2000 &element
->trace_chunk_registry_ht_node
);
2001 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
2002 /* Successfully published the new element. */
2003 element
->registry
= registry
;
2004 /* Acquire a reference for the caller. */
2005 if (lttng_trace_chunk_get(&element
->chunk
)) {
2009 * Another thread concurrently unpublished the
2010 * trace chunk. This is currently unexpected.
2012 * Re-attempt to publish.
2014 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
2020 * An equivalent trace chunk was published before this trace
2021 * chunk. Attempt to acquire a reference to the one that was
2022 * already published and release the reference to the copy we
2023 * created if successful.
2025 published_element
= container_of(published_node
,
2026 typeof(*published_element
),
2027 trace_chunk_registry_ht_node
);
2028 published_chunk
= &published_element
->chunk
;
2029 if (lttng_trace_chunk_get(published_chunk
)) {
2030 lttng_trace_chunk_put(&element
->chunk
);
2031 element
= published_element
;
2035 * A reference to the previously published trace chunk could not
2036 * be acquired. Hence, retry to publish our copy of the trace
2042 return element
? &element
->chunk
: NULL
;
2046 * Note that the caller must be registered as an RCU thread.
2047 * However, it does not need to hold the RCU read lock. The RCU read lock is
2048 * acquired to perform the look-up in the registry's hash table and held until
2049 * after a reference to the "found" trace chunk is acquired.
2051 * IOW, holding a reference guarantees the existence of the object for the
2055 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
2056 const struct lttng_trace_chunk_registry
*registry
,
2057 uint64_t session_id
, uint64_t *chunk_id
)
2059 const struct lttng_trace_chunk_registry_element target_element
= {
2060 .chunk
.id
.is_set
= !!chunk_id
,
2061 .chunk
.id
.value
= chunk_id
? *chunk_id
: 0,
2062 .session_id
= session_id
,
2064 const unsigned long element_hash
=
2065 lttng_trace_chunk_registry_element_hash(
2067 struct cds_lfht_node
*published_node
;
2068 struct lttng_trace_chunk_registry_element
*published_element
;
2069 struct lttng_trace_chunk
*published_chunk
= NULL
;
2070 struct cds_lfht_iter iter
;
2073 cds_lfht_lookup(registry
->ht
,
2075 lttng_trace_chunk_registry_element_match
,
2078 published_node
= cds_lfht_iter_get_node(&iter
);
2079 if (!published_node
) {
2083 published_element
= container_of(published_node
,
2084 typeof(*published_element
),
2085 trace_chunk_registry_ht_node
);
2086 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
2087 published_chunk
= &published_element
->chunk
;
2091 return published_chunk
;
2094 struct lttng_trace_chunk
*
2095 lttng_trace_chunk_registry_find_chunk(
2096 const struct lttng_trace_chunk_registry
*registry
,
2097 uint64_t session_id
, uint64_t chunk_id
)
2099 return _lttng_trace_chunk_registry_find_chunk(registry
,
2100 session_id
, &chunk_id
);
2103 int lttng_trace_chunk_registry_chunk_exists(
2104 const struct lttng_trace_chunk_registry
*registry
,
2105 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
2108 const struct lttng_trace_chunk_registry_element target_element
= {
2109 .chunk
.id
.is_set
= true,
2110 .chunk
.id
.value
= chunk_id
,
2111 .session_id
= session_id
,
2113 const unsigned long element_hash
=
2114 lttng_trace_chunk_registry_element_hash(
2116 struct cds_lfht_node
*published_node
;
2117 struct cds_lfht_iter iter
;
2120 cds_lfht_lookup(registry
->ht
,
2122 lttng_trace_chunk_registry_element_match
,
2125 published_node
= cds_lfht_iter_get_node(&iter
);
2126 if (!published_node
) {
2127 *chunk_exists
= false;
2131 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
2137 struct lttng_trace_chunk
*
2138 lttng_trace_chunk_registry_find_anonymous_chunk(
2139 const struct lttng_trace_chunk_registry
*registry
,
2140 uint64_t session_id
)
2142 return _lttng_trace_chunk_registry_find_chunk(registry
,
2146 unsigned int lttng_trace_chunk_registry_put_each_chunk(
2147 const struct lttng_trace_chunk_registry
*registry
)
2149 struct cds_lfht_iter iter
;
2150 struct lttng_trace_chunk_registry_element
*chunk_element
;
2151 unsigned int trace_chunks_left
= 0;
2153 DBG("Releasing trace chunk registry to all trace chunks");
2155 cds_lfht_for_each_entry(registry
->ht
,
2156 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
2157 const char *chunk_id_str
= "none";
2158 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
2160 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
2161 if (chunk_element
->chunk
.id
.is_set
) {
2164 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
2166 chunk_element
->chunk
.id
.value
);
2167 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
2168 chunk_id_str
= "formatting error";
2170 chunk_id_str
= chunk_id_buf
;
2174 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
2175 "chunk_id = %s, name = \"%s\", status = %s",
2176 chunk_element
->session_id
,
2178 chunk_element
->chunk
.name
? : "none",
2179 chunk_element
->chunk
.close_command
.is_set
?
2181 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
2182 lttng_trace_chunk_put(&chunk_element
->chunk
);
2183 trace_chunks_left
++;
2186 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
2189 return trace_chunks_left
;