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.h>
16 #include <common/fs-handle-internal.h>
17 #include <common/hashtable/hashtable.h>
18 #include <common/hashtable/utils.h>
19 #include <common/optional.h>
20 #include <common/string-utils/format.h>
21 #include <common/time.h>
22 #include <common/trace-chunk-registry.h>
23 #include <common/trace-chunk.h>
24 #include <common/utils.h>
25 #include <lttng/constant.h>
31 #include <urcu/rculfhash.h>
35 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
36 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
38 #define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
39 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
41 enum trace_chunk_mode
{
42 TRACE_CHUNK_MODE_USER
,
43 TRACE_CHUNK_MODE_OWNER
,
47 * Callback to invoke on release of a trace chunk. Note that there is no
48 * need to 'lock' the trace chunk during the execution of these callbacks
49 * since only one thread may access a chunk during its destruction (the last
50 * to release its reference to the chunk).
52 typedef int (*chunk_command
)(struct lttng_trace_chunk
*trace_chunk
);
54 /* Move a completed trace chunk to the 'completed' trace archive folder. */
56 int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk
*trace_chunk
);
59 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
);
60 /* Unlink old chunk files. */
62 int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk
*trace_chunk
);
64 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
65 struct lttng_trace_chunk
*chunk
, const char *path
);
67 struct chunk_credentials
{
68 bool use_current_user
;
69 struct lttng_credentials user
;
73 * NOTE: Make sure to update:
74 * - lttng_trace_chunk_copy(),
75 * - lttng_trace_chunk_registry_element_create_from_chunk()
76 * if you modify this structure.
78 struct lttng_trace_chunk
{
81 LTTNG_OPTIONAL(enum trace_chunk_mode
) mode
;
83 * First-level directories created within the trace chunk.
84 * Elements are of type 'char *'.
86 * Only used by _owner_ mode chunks.
88 struct lttng_dynamic_pointer_array top_level_directories
;
90 * All files contained within the trace chunk.
91 * Array of paths (char *).
93 struct lttng_dynamic_pointer_array files
;
94 /* Is contained within an lttng_trace_chunk_registry_element? */
95 bool in_registry_element
;
99 /* An unset id means the chunk is anonymous. */
100 LTTNG_OPTIONAL(uint64_t) id
;
103 * The creation and close timestamps are NOT monotonic.
104 * They must not be used in context were monotonicity is required.
106 LTTNG_OPTIONAL(time_t) timestamp_creation
;
107 LTTNG_OPTIONAL(time_t) timestamp_close
;
109 LTTNG_OPTIONAL(struct chunk_credentials
) credentials
;
110 struct lttng_directory_handle
*session_output_directory
;
111 struct lttng_directory_handle
*chunk_directory
;
112 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type
) close_command
;
114 * fd_tracker instance through which file descriptors should be
117 * An fd_tracker always outlives any trace chunk; there is no
118 * need to perform any reference counting of that object.
120 struct fd_tracker
*fd_tracker
;
123 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
124 struct lttng_trace_chunk_registry_element
{
125 struct lttng_trace_chunk chunk
;
127 /* Weak and only set when added. */
128 struct lttng_trace_chunk_registry
*registry
;
129 struct cds_lfht_node trace_chunk_registry_ht_node
;
130 /* call_rcu delayed reclaim. */
131 struct rcu_head rcu_node
;
134 struct lttng_trace_chunk_registry
{
138 struct fs_handle_untracked
{
139 struct fs_handle parent
;
142 struct lttng_directory_handle
*directory_handle
;
148 int fs_handle_untracked_get_fd(struct fs_handle
*handle
);
150 void fs_handle_untracked_put_fd(struct fs_handle
*handle
);
152 int fs_handle_untracked_unlink(struct fs_handle
*handle
);
154 int fs_handle_untracked_close(struct fs_handle
*handle
);
157 char *close_command_names
[] = {
158 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
159 "move to completed chunk folder",
160 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
162 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
167 chunk_command close_command_post_release_funcs
[] = {
168 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
169 lttng_trace_chunk_move_to_completed_post_release
,
170 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
171 lttng_trace_chunk_no_operation
,
172 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
173 lttng_trace_chunk_delete_post_release
,
177 struct fs_handle
*fs_handle_untracked_create(
178 struct lttng_directory_handle
*directory_handle
,
182 struct fs_handle_untracked
*handle
= NULL
;
183 bool reference_acquired
;
184 char *path_copy
= strdup(path
);
186 LTTNG_ASSERT(fd
>= 0);
188 PERROR("Failed to copy file path while creating untracked filesystem handle");
192 handle
= zmalloc(sizeof(typeof(*handle
)));
194 PERROR("Failed to allocate untracked filesystem handle");
198 handle
->parent
= (typeof(handle
->parent
)) {
199 .get_fd
= fs_handle_untracked_get_fd
,
200 .put_fd
= fs_handle_untracked_put_fd
,
201 .unlink
= fs_handle_untracked_unlink
,
202 .close
= fs_handle_untracked_close
,
206 reference_acquired
= lttng_directory_handle_get(directory_handle
);
207 LTTNG_ASSERT(reference_acquired
);
208 handle
->location
.directory_handle
= directory_handle
;
209 /* Ownership is transferred. */
210 handle
->location
.path
= path_copy
;
214 return handle
? &handle
->parent
: NULL
;
218 int fs_handle_untracked_get_fd(struct fs_handle
*_handle
)
220 struct fs_handle_untracked
*handle
= container_of(
221 _handle
, struct fs_handle_untracked
, parent
);
227 void fs_handle_untracked_put_fd(struct fs_handle
*_handle
)
233 int fs_handle_untracked_unlink(struct fs_handle
*_handle
)
235 struct fs_handle_untracked
*handle
= container_of(
236 _handle
, struct fs_handle_untracked
, parent
);
238 return lttng_directory_handle_unlink_file(
239 handle
->location
.directory_handle
,
240 handle
->location
.path
);
244 void fs_handle_untracked_destroy(struct fs_handle_untracked
*handle
)
246 lttng_directory_handle_put(handle
->location
.directory_handle
);
247 free(handle
->location
.path
);
252 int fs_handle_untracked_close(struct fs_handle
*_handle
)
254 struct fs_handle_untracked
*handle
= container_of(
255 _handle
, struct fs_handle_untracked
, parent
);
256 int ret
= close(handle
->fd
);
258 fs_handle_untracked_destroy(handle
);
263 bool lttng_trace_chunk_registry_element_equals(
264 const struct lttng_trace_chunk_registry_element
*a
,
265 const struct lttng_trace_chunk_registry_element
*b
)
267 if (a
->session_id
!= b
->session_id
) {
270 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
273 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
282 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
,
285 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
287 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
288 element_b
= caa_container_of(node
, typeof(*element_b
),
289 trace_chunk_registry_ht_node
);
290 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
294 unsigned long lttng_trace_chunk_registry_element_hash(
295 const struct lttng_trace_chunk_registry_element
*element
)
297 unsigned long hash
= hash_key_u64(&element
->session_id
,
300 if (element
->chunk
.id
.is_set
) {
301 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
308 char *generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
,
309 const time_t *close_timestamp
)
312 char *new_name
= NULL
;
313 char start_datetime
[ISO8601_STR_LEN
] = {};
314 /* Add 1 for a '-' prefix. */
315 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
317 ret
= time_to_iso8601_str(
319 start_datetime
, sizeof(start_datetime
));
321 ERR("Failed to format trace chunk start date time");
324 if (close_timestamp
) {
325 *end_datetime_suffix
= '-';
326 ret
= time_to_iso8601_str(
328 end_datetime_suffix
+ 1,
329 sizeof(end_datetime_suffix
) - 1);
331 ERR("Failed to format trace chunk end date time");
335 new_name
= zmalloc(GENERATED_CHUNK_NAME_LEN
);
337 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
340 ret
= snprintf(new_name
, GENERATED_CHUNK_NAME_LEN
, "%s%s-%" PRIu64
,
341 start_datetime
, end_datetime_suffix
, chunk_id
);
342 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
343 ERR("Failed to format trace chunk name");
354 void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
356 urcu_ref_init(&chunk
->ref
);
357 pthread_mutex_init(&chunk
->lock
, NULL
);
358 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
359 lttng_dynamic_pointer_array_init(&chunk
->files
, free
);
363 void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
365 if (chunk
->session_output_directory
) {
366 lttng_directory_handle_put(
367 chunk
->session_output_directory
);
368 chunk
->session_output_directory
= NULL
;
370 if (chunk
->chunk_directory
) {
371 lttng_directory_handle_put(chunk
->chunk_directory
);
372 chunk
->chunk_directory
= NULL
;
378 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
379 lttng_dynamic_pointer_array_reset(&chunk
->files
);
380 pthread_mutex_destroy(&chunk
->lock
);
384 struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
386 struct lttng_trace_chunk
*chunk
= NULL
;
388 chunk
= zmalloc(sizeof(*chunk
));
390 ERR("Failed to allocate trace chunk");
393 lttng_trace_chunk_init(chunk
);
398 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
400 DBG("Creating anonymous trace chunk");
401 return lttng_trace_chunk_allocate();
404 struct lttng_trace_chunk
*lttng_trace_chunk_create(
405 uint64_t chunk_id
, time_t chunk_creation_time
, const char *path
)
407 struct lttng_trace_chunk
*chunk
;
408 char chunk_creation_datetime_buf
[16] = {};
409 const char *chunk_creation_datetime_str
= "(formatting error)";
410 struct tm timeinfo_buf
, *timeinfo
;
412 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
416 /* Don't fail because of this; it is only used for logging. */
417 strftime_ret
= strftime(chunk_creation_datetime_buf
,
418 sizeof(chunk_creation_datetime_buf
),
419 "%Y%m%d-%H%M%S", timeinfo
);
421 chunk_creation_datetime_str
=
422 chunk_creation_datetime_buf
;
426 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
427 chunk_id
, chunk_creation_datetime_str
);
428 chunk
= lttng_trace_chunk_allocate();
433 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
434 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
436 chunk
->name
= generate_chunk_name(chunk_id
,
437 chunk_creation_time
, NULL
);
439 ERR("Failed to allocate trace chunk name storage");
444 chunk
->path
= strdup(path
);
450 chunk
->path
= strdup(chunk
->name
);
457 DBG("Chunk name set to \"%s\"", chunk
->name
? : "(none)");
461 lttng_trace_chunk_put(chunk
);
465 void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk
*chunk
,
466 struct fd_tracker
*fd_tracker
)
468 LTTNG_ASSERT(!chunk
->session_output_directory
);
469 LTTNG_ASSERT(!chunk
->chunk_directory
);
470 LTTNG_ASSERT(lttng_dynamic_pointer_array_get_count(&chunk
->files
) == 0);
471 chunk
->fd_tracker
= fd_tracker
;
474 struct lttng_trace_chunk
*lttng_trace_chunk_copy(
475 struct lttng_trace_chunk
*source_chunk
)
477 struct lttng_trace_chunk
*new_chunk
= lttng_trace_chunk_allocate();
483 pthread_mutex_lock(&source_chunk
->lock
);
485 * A new chunk is always a user; it shall create no new trace
488 new_chunk
->mode
= (typeof(new_chunk
->mode
)) {
490 .value
= TRACE_CHUNK_MODE_USER
,
493 * top_level_directories is not copied as it is never used
494 * by _user_ mode chunks.
496 /* The new chunk is not part of a registry (yet, at least). */
497 new_chunk
->in_registry_element
= false;
498 new_chunk
->name_overridden
= source_chunk
->name_overridden
;
499 if (source_chunk
->name
) {
500 new_chunk
->name
= strdup(source_chunk
->name
);
501 if (!new_chunk
->name
) {
502 ERR("Failed to copy source trace chunk name in %s()",
507 if (source_chunk
->path
) {
508 new_chunk
->path
= strdup(source_chunk
->path
);
509 if (!new_chunk
->path
) {
510 ERR("Failed to copy source trace chunk path in %s()",
514 new_chunk
->id
= source_chunk
->id
;
515 new_chunk
->timestamp_creation
= source_chunk
->timestamp_creation
;
516 new_chunk
->timestamp_close
= source_chunk
->timestamp_close
;
517 new_chunk
->credentials
= source_chunk
->credentials
;
518 if (source_chunk
->session_output_directory
) {
519 const bool reference_acquired
= lttng_directory_handle_get(
520 source_chunk
->session_output_directory
);
522 LTTNG_ASSERT(reference_acquired
);
523 new_chunk
->session_output_directory
=
524 source_chunk
->session_output_directory
;
526 if (source_chunk
->chunk_directory
) {
527 const bool reference_acquired
= lttng_directory_handle_get(
528 source_chunk
->chunk_directory
);
530 LTTNG_ASSERT(reference_acquired
);
531 new_chunk
->chunk_directory
= source_chunk
->chunk_directory
;
533 new_chunk
->close_command
= source_chunk
->close_command
;
534 new_chunk
->fd_tracker
= source_chunk
->fd_tracker
;
535 pthread_mutex_unlock(&source_chunk
->lock
);
539 pthread_mutex_unlock(&source_chunk
->lock
);
540 lttng_trace_chunk_put(new_chunk
);
544 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(
545 struct lttng_trace_chunk
*chunk
, uint64_t *id
)
547 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
549 pthread_mutex_lock(&chunk
->lock
);
550 if (chunk
->id
.is_set
) {
551 *id
= chunk
->id
.value
;
553 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
555 pthread_mutex_unlock(&chunk
->lock
);
559 enum lttng_trace_chunk_status
lttng_trace_chunk_get_creation_timestamp(
560 struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
563 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
565 pthread_mutex_lock(&chunk
->lock
);
566 if (chunk
->timestamp_creation
.is_set
) {
567 *creation_ts
= chunk
->timestamp_creation
.value
;
569 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
571 pthread_mutex_unlock(&chunk
->lock
);
575 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_timestamp(
576 struct lttng_trace_chunk
*chunk
, time_t *close_ts
)
578 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
580 pthread_mutex_lock(&chunk
->lock
);
581 if (chunk
->timestamp_close
.is_set
) {
582 *close_ts
= chunk
->timestamp_close
.value
;
584 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
586 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
;
603 * Note: we do not enforce that the closing timestamp be greater or
604 * equal to the begin timestamp. These timestamps are used for
605 * generating the chunk name and should only be used in context where
606 * the monotonicity of time is not important. The source of those
607 * timestamps is NOT monotonic and represent the system calendar time,
608 * also know as the wall time.
610 if (chunk
->timestamp_creation
.value
> close_ts
) {
611 WARN("Set trace chunk close timestamp: close timestamp is before creation timestamp, begin : %ld, close : %ld",
612 chunk
->timestamp_creation
.value
, close_ts
);
615 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
616 if (!chunk
->name_overridden
) {
618 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
619 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
622 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
626 pthread_mutex_unlock(&chunk
->lock
);
630 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(
631 struct lttng_trace_chunk
*chunk
, const char **name
,
632 bool *name_overridden
)
634 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
636 pthread_mutex_lock(&chunk
->lock
);
637 if (name_overridden
) {
638 *name_overridden
= chunk
->name_overridden
;
641 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
646 pthread_mutex_unlock(&chunk
->lock
);
650 bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk
*chunk
)
652 bool name_overridden
;
654 pthread_mutex_lock(&chunk
->lock
);
655 name_overridden
= chunk
->name_overridden
;
656 pthread_mutex_unlock(&chunk
->lock
);
657 return name_overridden
;
661 bool is_valid_chunk_name(const char *name
)
669 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
670 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
674 if (strchr(name
, '/') || strchr(name
, '.')) {
681 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
682 struct lttng_trace_chunk
*chunk
, const char *name
)
685 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
686 char *new_name
, *new_path
;
688 DBG("Override trace chunk name from %s to %s", chunk
->name
, name
);
689 if (!is_valid_chunk_name(name
)) {
690 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
692 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
696 pthread_mutex_lock(&chunk
->lock
);
697 if (!chunk
->id
.is_set
) {
698 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
700 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
704 new_name
= strdup(name
);
706 ERR("Failed to allocate new trace chunk name");
707 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
711 chunk
->name
= new_name
;
713 new_path
= strdup(name
);
715 ERR("Failed to allocate new trace chunk path");
716 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
720 chunk
->path
= new_path
;
722 chunk
->name_overridden
= true;
724 pthread_mutex_unlock(&chunk
->lock
);
730 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
731 struct lttng_trace_chunk
*chunk
, const char *path
)
734 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
735 struct lttng_directory_handle
*rename_directory
= NULL
;
736 char *new_path
, *old_path
;
739 if (chunk
->name_overridden
) {
740 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
744 old_path
= chunk
->path
;
745 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path
, path
);
747 if ((!old_path
&& !path
) ||
748 (old_path
&& path
&& !strcmp(old_path
, path
))) {
752 * Use chunk name as path if NULL path is specified.
758 /* Renaming from "" to "" is not accepted. */
759 if (path
[0] == '\0' && old_path
[0] == '\0') {
760 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
765 * If a rename is performed on a chunk for which the chunk_directory
766 * is not set (yet), or the session_output_directory is not set
767 * (interacting with a relay daemon), there is no rename to perform.
769 if (!chunk
->chunk_directory
||
770 !chunk
->session_output_directory
) {
774 if (old_path
&& old_path
[0] != '\0' && path
[0] != '\0') {
775 /* Rename chunk directory. */
776 ret
= lttng_directory_handle_rename_as_user(
777 chunk
->session_output_directory
,
779 chunk
->session_output_directory
,
781 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
783 &chunk
->credentials
.value
.user
);
785 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
787 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
790 rename_directory
= chunk
->fd_tracker
?
791 fd_tracker_create_directory_handle_from_handle(
793 chunk
->session_output_directory
,
795 lttng_directory_handle_create_from_handle(
797 chunk
->session_output_directory
);
798 if (!rename_directory
) {
799 ERR("Failed to get handle to trace chunk rename directory");
800 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
804 /* Release old handle. */
805 lttng_directory_handle_put(chunk
->chunk_directory
);
807 * Transfer new handle reference to chunk as the current chunk
810 chunk
->chunk_directory
= rename_directory
;
811 rename_directory
= NULL
;
812 } else if (old_path
&& old_path
[0] == '\0') {
813 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
814 &chunk
->top_level_directories
);
816 ret
= lttng_directory_handle_create_subdirectory_as_user(
817 chunk
->session_output_directory
,
820 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
822 &chunk
->credentials
.value
.user
);
824 PERROR("Failed to create trace chunk rename directory \"%s\"",
826 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
830 rename_directory
= lttng_directory_handle_create_from_handle(
831 path
, chunk
->session_output_directory
);
832 if (!rename_directory
) {
833 ERR("Failed to get handle to trace chunk rename directory");
834 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
838 /* Move toplevel directories. */
839 for (i
= 0; i
< count
; i
++) {
840 const char *top_level_name
=
841 lttng_dynamic_pointer_array_get_pointer(
842 &chunk
->top_level_directories
, i
);
844 ret
= lttng_directory_handle_rename_as_user(
845 chunk
->chunk_directory
,
849 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
851 &chunk
->credentials
.value
.user
);
853 PERROR("Failed to move \"%s\" to trace chunk rename directory",
855 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
859 /* Release old handle. */
860 lttng_directory_handle_put(chunk
->chunk_directory
);
862 * Transfer new handle reference to chunk as the current chunk
865 chunk
->chunk_directory
= rename_directory
;
866 rename_directory
= NULL
;
867 } else if (old_path
) {
868 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
869 &chunk
->top_level_directories
);
870 const bool reference_acquired
= lttng_directory_handle_get(
871 chunk
->session_output_directory
);
873 LTTNG_ASSERT(reference_acquired
);
874 rename_directory
= chunk
->session_output_directory
;
876 /* Move toplevel directories. */
877 for (i
= 0; i
< count
; i
++) {
878 const char *top_level_name
=
879 lttng_dynamic_pointer_array_get_pointer(
880 &chunk
->top_level_directories
, i
);
882 ret
= lttng_directory_handle_rename_as_user(
883 chunk
->chunk_directory
,
887 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
889 &chunk
->credentials
.value
.user
);
891 PERROR("Failed to move \"%s\" to trace chunk rename directory",
893 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
897 /* Release old handle. */
898 lttng_directory_handle_put(chunk
->chunk_directory
);
900 * Transfer new handle reference to chunk as the current chunk
903 chunk
->chunk_directory
= rename_directory
;
904 rename_directory
= NULL
;
906 /* Remove old directory. */
907 status
= lttng_directory_handle_remove_subdirectory(
908 chunk
->session_output_directory
,
910 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
911 ERR("Error removing subdirectory '%s' file when deleting chunk",
916 /* Unexpected !old_path && !path. */
917 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
922 new_path
= strdup(path
);
924 ERR("Failed to allocate new trace chunk path");
925 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
929 chunk
->path
= new_path
;
931 lttng_directory_handle_put(rename_directory
);
935 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path(
936 struct lttng_trace_chunk
*chunk
, const char *path
)
939 enum lttng_trace_chunk_status status
;
941 pthread_mutex_lock(&chunk
->lock
);
942 status
= lttng_trace_chunk_rename_path_no_lock(chunk
, path
);
943 pthread_mutex_unlock(&chunk
->lock
);
948 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
949 struct lttng_trace_chunk
*chunk
,
950 struct lttng_credentials
*credentials
)
952 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
954 pthread_mutex_lock(&chunk
->lock
);
955 if (chunk
->credentials
.is_set
) {
956 if (chunk
->credentials
.value
.use_current_user
) {
957 LTTNG_OPTIONAL_SET(&credentials
->uid
, geteuid());
958 LTTNG_OPTIONAL_SET(&credentials
->gid
, getegid());
960 *credentials
= chunk
->credentials
.value
.user
;
963 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
965 pthread_mutex_unlock(&chunk
->lock
);
969 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
970 struct lttng_trace_chunk
*chunk
,
971 const struct lttng_credentials
*user_credentials
)
973 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
974 const struct chunk_credentials credentials
= {
975 .user
= *user_credentials
,
976 .use_current_user
= false,
979 pthread_mutex_lock(&chunk
->lock
);
980 if (chunk
->credentials
.is_set
) {
981 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
984 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
986 pthread_mutex_unlock(&chunk
->lock
);
990 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
991 struct lttng_trace_chunk
*chunk
)
993 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
994 const struct chunk_credentials credentials
= {
995 .use_current_user
= true,
998 pthread_mutex_lock(&chunk
->lock
);
999 if (chunk
->credentials
.is_set
) {
1000 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1003 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
1005 pthread_mutex_unlock(&chunk
->lock
);
1010 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
1011 struct lttng_trace_chunk
*chunk
,
1012 struct lttng_directory_handle
*session_output_directory
)
1015 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1016 struct lttng_directory_handle
*chunk_directory_handle
= NULL
;
1017 bool reference_acquired
;
1019 pthread_mutex_lock(&chunk
->lock
);
1020 if (chunk
->mode
.is_set
) {
1021 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1024 if (!chunk
->credentials
.is_set
) {
1026 * Fatal error, credentials must be set before a
1027 * directory is created.
1029 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
1030 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1033 if (chunk
->path
&& chunk
->path
[0] != '\0') {
1034 ret
= lttng_directory_handle_create_subdirectory_as_user(
1035 session_output_directory
,
1038 !chunk
->credentials
.value
.use_current_user
?
1039 &chunk
->credentials
.value
.user
: NULL
);
1041 PERROR("Failed to create chunk output directory \"%s\"",
1043 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1046 chunk_directory_handle
=
1048 fd_tracker_create_directory_handle_from_handle(
1050 session_output_directory
,
1052 lttng_directory_handle_create_from_handle(
1054 session_output_directory
);
1055 if (!chunk_directory_handle
) {
1056 /* The function already logs on all error paths. */
1057 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1062 * A nameless chunk does not need its own output directory.
1063 * The session's output directory will be used.
1065 reference_acquired
= lttng_directory_handle_get(
1066 session_output_directory
);
1068 LTTNG_ASSERT(reference_acquired
);
1069 chunk_directory_handle
= session_output_directory
;
1071 chunk
->chunk_directory
= chunk_directory_handle
;
1072 chunk_directory_handle
= NULL
;
1073 reference_acquired
= lttng_directory_handle_get(
1074 session_output_directory
);
1075 LTTNG_ASSERT(reference_acquired
);
1076 chunk
->session_output_directory
= session_output_directory
;
1077 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
1079 pthread_mutex_unlock(&chunk
->lock
);
1083 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
1084 struct lttng_trace_chunk
*chunk
,
1085 struct lttng_directory_handle
*chunk_directory
)
1087 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1088 bool reference_acquired
;
1090 pthread_mutex_lock(&chunk
->lock
);
1091 if (chunk
->mode
.is_set
) {
1092 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1095 if (!chunk
->credentials
.is_set
) {
1096 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
1097 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1100 reference_acquired
= lttng_directory_handle_get(chunk_directory
);
1101 LTTNG_ASSERT(reference_acquired
);
1102 chunk
->chunk_directory
= chunk_directory
;
1103 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
1105 pthread_mutex_unlock(&chunk
->lock
);
1109 enum lttng_trace_chunk_status
1110 lttng_trace_chunk_get_session_output_directory_handle(
1111 struct lttng_trace_chunk
*chunk
,
1112 struct lttng_directory_handle
**handle
)
1114 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1116 pthread_mutex_lock(&chunk
->lock
);
1117 if (!chunk
->session_output_directory
) {
1118 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1122 const bool reference_acquired
= lttng_directory_handle_get(
1123 chunk
->session_output_directory
);
1125 LTTNG_ASSERT(reference_acquired
);
1126 *handle
= chunk
->session_output_directory
;
1129 pthread_mutex_unlock(&chunk
->lock
);
1133 enum lttng_trace_chunk_status
lttng_trace_chunk_borrow_chunk_directory_handle(
1134 struct lttng_trace_chunk
*chunk
,
1135 const struct lttng_directory_handle
**handle
)
1137 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1139 pthread_mutex_lock(&chunk
->lock
);
1140 if (!chunk
->chunk_directory
) {
1141 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1145 *handle
= chunk
->chunk_directory
;
1147 pthread_mutex_unlock(&chunk
->lock
);
1151 /* Add a top-level directory to the trace chunk if it was previously unknown. */
1153 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
1154 const char *new_path
)
1158 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
1159 &chunk
->top_level_directories
);
1160 const char *new_path_separator_pos
= strchr(new_path
, '/');
1161 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
1162 new_path_separator_pos
- new_path
: strlen(new_path
);
1164 for (i
= 0; i
< count
; i
++) {
1165 const char *path
= lttng_dynamic_pointer_array_get_pointer(
1166 &chunk
->top_level_directories
, i
);
1167 const ptrdiff_t path_top_level_len
= strlen(path
);
1169 if (path_top_level_len
!= new_path_top_level_len
) {
1172 if (!strncmp(path
, new_path
, path_top_level_len
)) {
1179 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
1181 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1182 new_path
, chunk
->name
? : "(unnamed)");
1184 PERROR("Failed to copy path");
1188 ret
= lttng_dynamic_pointer_array_add_pointer(
1189 &chunk
->top_level_directories
, copy
);
1191 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1200 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
1201 struct lttng_trace_chunk
*chunk
,
1205 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1207 DBG("Creating trace chunk subdirectory \"%s\"", path
);
1208 pthread_mutex_lock(&chunk
->lock
);
1209 if (!chunk
->credentials
.is_set
) {
1211 * Fatal error, credentials must be set before a
1212 * directory is created.
1214 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1216 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1219 if (!chunk
->mode
.is_set
||
1220 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
1221 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1223 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1226 if (!chunk
->chunk_directory
) {
1227 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1229 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1233 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1235 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1238 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
1239 chunk
->chunk_directory
, path
,
1241 chunk
->credentials
.value
.use_current_user
?
1242 NULL
: &chunk
->credentials
.value
.user
);
1244 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1246 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1249 ret
= add_top_level_directory_unique(chunk
, path
);
1251 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1255 pthread_mutex_unlock(&chunk
->lock
);
1260 * TODO: Implement O(1) lookup.
1263 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk
*chunk
,
1264 const char *path
, size_t *index
)
1268 count
= lttng_dynamic_pointer_array_get_count(&chunk
->files
);
1269 for (i
= 0; i
< count
; i
++) {
1270 const char *iter_path
=
1271 lttng_dynamic_pointer_array_get_pointer(
1273 if (!strcmp(iter_path
, path
)) {
1284 enum lttng_trace_chunk_status
lttng_trace_chunk_add_file(
1285 struct lttng_trace_chunk
*chunk
,
1290 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1292 if (lttng_trace_chunk_find_file(chunk
, path
, NULL
)) {
1293 return LTTNG_TRACE_CHUNK_STATUS_OK
;
1295 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1296 path
, chunk
->name
? : "(unnamed)");
1297 copy
= strdup(path
);
1299 PERROR("Failed to copy path");
1300 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1303 ret
= lttng_dynamic_pointer_array_add_pointer(
1304 &chunk
->files
, copy
);
1306 ERR("Allocation failure while adding file to a trace chunk");
1308 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1316 void lttng_trace_chunk_remove_file(
1317 struct lttng_trace_chunk
*chunk
,
1324 found
= lttng_trace_chunk_find_file(chunk
, path
, &index
);
1328 ret
= lttng_dynamic_pointer_array_remove_pointer(
1329 &chunk
->files
, index
);
1334 enum lttng_trace_chunk_status
_lttng_trace_chunk_open_fs_handle_locked(
1335 struct lttng_trace_chunk
*chunk
,
1336 const char *file_path
,
1339 struct fs_handle
**out_handle
,
1340 bool expect_no_file
)
1343 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1345 DBG("Opening trace chunk file \"%s\"", file_path
);
1346 if (!chunk
->credentials
.is_set
) {
1348 * Fatal error, credentials must be set before a
1351 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1353 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1356 if (!chunk
->chunk_directory
) {
1357 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1359 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1362 status
= lttng_trace_chunk_add_file(chunk
, file_path
);
1363 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1366 if (chunk
->fd_tracker
) {
1367 LTTNG_ASSERT(chunk
->credentials
.value
.use_current_user
);
1368 *out_handle
= fd_tracker_open_fs_handle(chunk
->fd_tracker
,
1369 chunk
->chunk_directory
, file_path
, flags
, &mode
);
1370 ret
= *out_handle
? 0 : -1;
1372 ret
= lttng_directory_handle_open_file_as_user(
1373 chunk
->chunk_directory
, file_path
, flags
, mode
,
1374 chunk
->credentials
.value
.use_current_user
?
1376 &chunk
->credentials
.value
.user
);
1378 *out_handle
= fs_handle_untracked_create(
1379 chunk
->chunk_directory
, file_path
, ret
);
1381 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1387 if (errno
== ENOENT
&& expect_no_file
) {
1388 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
1390 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1391 file_path
, flags
, (int) mode
);
1392 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1394 lttng_trace_chunk_remove_file(chunk
, file_path
);
1401 enum lttng_trace_chunk_status
lttng_trace_chunk_open_fs_handle(
1402 struct lttng_trace_chunk
*chunk
,
1403 const char *file_path
,
1406 struct fs_handle
**out_handle
,
1407 bool expect_no_file
)
1409 enum lttng_trace_chunk_status status
;
1411 pthread_mutex_lock(&chunk
->lock
);
1412 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1413 flags
, mode
, out_handle
, expect_no_file
);
1414 pthread_mutex_unlock(&chunk
->lock
);
1418 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
1419 struct lttng_trace_chunk
*chunk
,
1420 const char *file_path
,
1424 bool expect_no_file
)
1426 enum lttng_trace_chunk_status status
;
1427 struct fs_handle
*fs_handle
;
1429 pthread_mutex_lock(&chunk
->lock
);
1431 * Using this method is never valid when an fd_tracker is being
1432 * used since the resulting file descriptor would not be tracked.
1434 LTTNG_ASSERT(!chunk
->fd_tracker
);
1435 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1436 flags
, mode
, &fs_handle
, expect_no_file
);
1437 pthread_mutex_unlock(&chunk
->lock
);
1439 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
1440 *out_fd
= fs_handle_get_fd(fs_handle
);
1442 * Does not close the fd; we just "unbox" it from the fs_handle.
1444 fs_handle_untracked_destroy(container_of(
1445 fs_handle
, struct fs_handle_untracked
, parent
));
1451 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
1452 const char *file_path
)
1455 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1457 DBG("Unlinking trace chunk file \"%s\"", file_path
);
1458 pthread_mutex_lock(&chunk
->lock
);
1459 if (!chunk
->credentials
.is_set
) {
1461 * Fatal error, credentials must be set before a
1464 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1466 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1469 if (!chunk
->chunk_directory
) {
1470 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1472 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1475 ret
= lttng_directory_handle_unlink_file_as_user(
1476 chunk
->chunk_directory
, file_path
,
1477 chunk
->credentials
.value
.use_current_user
?
1478 NULL
: &chunk
->credentials
.value
.user
);
1480 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1483 lttng_trace_chunk_remove_file(chunk
, file_path
);
1485 pthread_mutex_unlock(&chunk
->lock
);
1490 int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk
*chunk
,
1494 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1496 DBG("Recursively removing trace chunk directory \"%s\"", path
);
1497 pthread_mutex_lock(&chunk
->lock
);
1498 if (!chunk
->credentials
.is_set
) {
1500 * Fatal error, credentials must be set before a
1501 * directory is removed.
1503 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1505 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1508 if (!chunk
->chunk_directory
) {
1509 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1511 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1514 ret
= lttng_directory_handle_remove_subdirectory_recursive_as_user(
1515 chunk
->chunk_directory
, path
,
1516 chunk
->credentials
.value
.use_current_user
?
1517 NULL
: &chunk
->credentials
.value
.user
,
1518 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
);
1520 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1524 pthread_mutex_unlock(&chunk
->lock
);
1529 int lttng_trace_chunk_move_to_completed_post_release(
1530 struct lttng_trace_chunk
*trace_chunk
)
1533 char *archived_chunk_name
= NULL
;
1534 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
1535 const time_t creation_timestamp
=
1536 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
1537 const time_t close_timestamp
=
1538 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
1539 struct lttng_directory_handle
*archived_chunks_directory
= NULL
;
1540 enum lttng_trace_chunk_status status
;
1542 if (!trace_chunk
->mode
.is_set
||
1543 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
1544 !trace_chunk
->session_output_directory
) {
1546 * This command doesn't need to run if the output is remote
1547 * or if the trace chunk is not owned by this process.
1552 LTTNG_ASSERT(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
1553 LTTNG_ASSERT(!trace_chunk
->name_overridden
);
1554 LTTNG_ASSERT(trace_chunk
->path
);
1556 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
1558 if (!archived_chunk_name
) {
1559 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1564 ret
= lttng_directory_handle_create_subdirectory_as_user(
1565 trace_chunk
->session_output_directory
,
1566 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1568 !trace_chunk
->credentials
.value
.use_current_user
?
1569 &trace_chunk
->credentials
.value
.user
:
1572 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1573 "\" directory for archived trace chunks");
1577 archived_chunks_directory
= trace_chunk
->fd_tracker
?
1578 fd_tracker_create_directory_handle_from_handle(
1579 trace_chunk
->fd_tracker
,
1580 trace_chunk
->session_output_directory
,
1581 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
) :
1582 lttng_directory_handle_create_from_handle(
1583 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1584 trace_chunk
->session_output_directory
);
1585 if (!archived_chunks_directory
) {
1586 PERROR("Failed to get handle to archived trace chunks directory");
1592 * Make sure chunk is renamed to old directory if not already done by
1593 * the creation of the next chunk. This happens if a rotation is
1594 * performed while tracing is stopped.
1596 if (!trace_chunk
->path
|| strcmp(trace_chunk
->path
,
1597 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
)) {
1598 status
= lttng_trace_chunk_rename_path_no_lock(trace_chunk
,
1599 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1600 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1601 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1607 ret
= lttng_directory_handle_rename_as_user(
1608 trace_chunk
->session_output_directory
,
1610 archived_chunks_directory
,
1611 archived_chunk_name
,
1612 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1614 &trace_chunk
->credentials
.value
.user
);
1616 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1618 archived_chunk_name
);
1622 lttng_directory_handle_put(archived_chunks_directory
);
1623 free(archived_chunk_name
);
1628 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
)
1634 int lttng_trace_chunk_delete_post_release_user(
1635 struct lttng_trace_chunk
*trace_chunk
)
1639 DBG("Trace chunk \"delete\" close command post-release (User)");
1641 /* Unlink all files. */
1642 while (lttng_dynamic_pointer_array_get_count(&trace_chunk
->files
) != 0) {
1643 enum lttng_trace_chunk_status status
;
1647 path
= lttng_dynamic_pointer_array_get_pointer(
1648 &trace_chunk
->files
, 0);
1649 DBG("Unlink file: %s", path
);
1650 status
= lttng_trace_chunk_unlink_file(trace_chunk
, path
);
1651 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1652 ERR("Error unlinking file '%s' when deleting chunk", path
);
1662 int lttng_trace_chunk_delete_post_release_owner(
1663 struct lttng_trace_chunk
*trace_chunk
)
1665 enum lttng_trace_chunk_status status
;
1669 ret
= lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1674 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1676 LTTNG_ASSERT(trace_chunk
->session_output_directory
);
1677 LTTNG_ASSERT(trace_chunk
->chunk_directory
);
1679 /* Remove empty directories. */
1680 count
= lttng_dynamic_pointer_array_get_count(
1681 &trace_chunk
->top_level_directories
);
1683 for (i
= 0; i
< count
; i
++) {
1684 const char *top_level_name
=
1685 lttng_dynamic_pointer_array_get_pointer(
1686 &trace_chunk
->top_level_directories
, i
);
1688 status
= lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk
, top_level_name
);
1689 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1690 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1697 lttng_directory_handle_put(trace_chunk
->chunk_directory
);
1698 trace_chunk
->chunk_directory
= NULL
;
1700 if (trace_chunk
->path
&& trace_chunk
->path
[0] != '\0') {
1701 status
= lttng_directory_handle_remove_subdirectory(
1702 trace_chunk
->session_output_directory
,
1704 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1705 ERR("Error removing subdirectory '%s' file when deleting chunk",
1711 free(trace_chunk
->path
);
1712 trace_chunk
->path
= NULL
;
1718 * For local files, session and consumer daemons all run the delete hook. The
1719 * consumer daemons have the list of files to unlink, and technically the
1720 * session daemon is the owner of the chunk. Unlink all files owned by each
1724 int lttng_trace_chunk_delete_post_release(
1725 struct lttng_trace_chunk
*trace_chunk
)
1727 if (!trace_chunk
->chunk_directory
) {
1731 if (trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
) {
1732 return lttng_trace_chunk_delete_post_release_owner(trace_chunk
);
1734 return lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1738 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
1739 struct lttng_trace_chunk
*chunk
,
1740 enum lttng_trace_chunk_command_type
*command_type
)
1742 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1744 pthread_mutex_lock(&chunk
->lock
);
1745 if (chunk
->close_command
.is_set
) {
1746 *command_type
= chunk
->close_command
.value
;
1747 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1749 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1751 pthread_mutex_unlock(&chunk
->lock
);
1755 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
1756 struct lttng_trace_chunk
*chunk
,
1757 enum lttng_trace_chunk_command_type close_command
)
1759 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1761 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1762 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1763 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1767 pthread_mutex_lock(&chunk
->lock
);
1768 if (chunk
->close_command
.is_set
) {
1769 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1770 close_command_names
[chunk
->close_command
.value
],
1771 close_command_names
[close_command
]);
1773 DBG("Setting trace chunk close command to \"%s\"",
1774 close_command_names
[close_command
]);
1777 * Unset close command for no-op for backward compatibility with relayd
1780 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1781 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1783 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1785 pthread_mutex_unlock(&chunk
->lock
);
1790 const char *lttng_trace_chunk_command_type_get_name(
1791 enum lttng_trace_chunk_command_type command
)
1794 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1795 return "move to completed trace chunk folder";
1796 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1797 return "no operation";
1798 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1805 bool lttng_trace_chunk_ids_equal(const struct lttng_trace_chunk
*chunk_a
,
1806 const struct lttng_trace_chunk
*chunk_b
)
1810 if (chunk_a
== chunk_b
) {
1815 if (!!chunk_a
^ !!chunk_b
) {
1819 if (chunk_a
->id
.is_set
^ chunk_a
->id
.is_set
) {
1820 /* One id is set and not the other, thus they are not equal. */
1824 if (!chunk_a
->id
.is_set
) {
1825 /* Both ids are unset. */
1828 equal
= chunk_a
->id
.value
== chunk_b
->id
.value
;
1835 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1837 return urcu_ref_get_unless_zero(&chunk
->ref
);
1841 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1843 struct lttng_trace_chunk_registry_element
*element
=
1844 container_of(node
, typeof(*element
), rcu_node
);
1846 lttng_trace_chunk_fini(&element
->chunk
);
1851 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1853 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1856 if (chunk
->close_command
.is_set
) {
1857 if (close_command_post_release_funcs
[
1858 chunk
->close_command
.value
](chunk
)) {
1859 ERR("Trace chunk post-release command %s has failed.",
1860 close_command_names
[chunk
->close_command
.value
]);
1864 if (chunk
->in_registry_element
) {
1865 struct lttng_trace_chunk_registry_element
*element
;
1867 element
= container_of(chunk
, typeof(*element
), chunk
);
1868 if (element
->registry
) {
1870 cds_lfht_del(element
->registry
->ht
,
1871 &element
->trace_chunk_registry_ht_node
);
1873 call_rcu(&element
->rcu_node
,
1874 free_lttng_trace_chunk_registry_element
);
1876 /* Never published, can be free'd immediately. */
1877 free_lttng_trace_chunk_registry_element(
1878 &element
->rcu_node
);
1881 /* Not RCU-protected, free immediately. */
1882 lttng_trace_chunk_fini(chunk
);
1887 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1892 LTTNG_ASSERT(chunk
->ref
.refcount
);
1893 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1896 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1898 struct lttng_trace_chunk_registry
*registry
;
1900 registry
= zmalloc(sizeof(*registry
));
1905 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1906 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1907 if (!registry
->ht
) {
1913 lttng_trace_chunk_registry_destroy(registry
);
1917 void lttng_trace_chunk_registry_destroy(
1918 struct lttng_trace_chunk_registry
*registry
)
1924 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1931 struct lttng_trace_chunk_registry_element
*
1932 lttng_trace_chunk_registry_element_create_from_chunk(
1933 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1935 struct lttng_trace_chunk_registry_element
*element
=
1936 zmalloc(sizeof(*element
));
1941 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1942 element
->session_id
= session_id
;
1944 element
->chunk
= *chunk
;
1945 lttng_trace_chunk_init(&element
->chunk
);
1946 if (chunk
->session_output_directory
) {
1947 /* Transferred ownership. */
1948 element
->chunk
.session_output_directory
=
1949 chunk
->session_output_directory
;
1950 chunk
->session_output_directory
= NULL
;
1952 if (chunk
->chunk_directory
) {
1953 /* Transferred ownership. */
1954 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1955 chunk
->chunk_directory
= NULL
;
1958 * The original chunk becomes invalid; the name and path attributes are
1959 * transferred to the new chunk instance.
1963 element
->chunk
.fd_tracker
= chunk
->fd_tracker
;
1964 element
->chunk
.in_registry_element
= true;
1969 struct lttng_trace_chunk
*
1970 lttng_trace_chunk_registry_publish_chunk(
1971 struct lttng_trace_chunk_registry
*registry
,
1972 uint64_t session_id
, struct lttng_trace_chunk
*chunk
)
1974 struct lttng_trace_chunk_registry_element
*element
;
1975 unsigned long element_hash
;
1977 pthread_mutex_lock(&chunk
->lock
);
1978 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
1980 pthread_mutex_unlock(&chunk
->lock
);
1985 * chunk is now invalid, the only valid operation is a 'put' from the
1989 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1993 struct cds_lfht_node
*published_node
;
1994 struct lttng_trace_chunk
*published_chunk
;
1995 struct lttng_trace_chunk_registry_element
*published_element
;
1997 published_node
= cds_lfht_add_unique(registry
->ht
,
1999 lttng_trace_chunk_registry_element_match
,
2001 &element
->trace_chunk_registry_ht_node
);
2002 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
2003 /* Successfully published the new element. */
2004 element
->registry
= registry
;
2005 /* Acquire a reference for the caller. */
2006 if (lttng_trace_chunk_get(&element
->chunk
)) {
2010 * Another thread concurrently unpublished the
2011 * trace chunk. This is currently unexpected.
2013 * Re-attempt to publish.
2015 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
2021 * An equivalent trace chunk was published before this trace
2022 * chunk. Attempt to acquire a reference to the one that was
2023 * already published and release the reference to the copy we
2024 * created if successful.
2026 published_element
= container_of(published_node
,
2027 typeof(*published_element
),
2028 trace_chunk_registry_ht_node
);
2029 published_chunk
= &published_element
->chunk
;
2030 if (lttng_trace_chunk_get(published_chunk
)) {
2031 lttng_trace_chunk_put(&element
->chunk
);
2032 element
= published_element
;
2036 * A reference to the previously published trace chunk could not
2037 * be acquired. Hence, retry to publish our copy of the trace
2043 return element
? &element
->chunk
: NULL
;
2047 * Note that the caller must be registered as an RCU thread.
2048 * However, it does not need to hold the RCU read lock. The RCU read lock is
2049 * acquired to perform the look-up in the registry's hash table and held until
2050 * after a reference to the "found" trace chunk is acquired.
2052 * IOW, holding a reference guarantees the existence of the object for the
2056 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
2057 const struct lttng_trace_chunk_registry
*registry
,
2058 uint64_t session_id
, uint64_t *chunk_id
)
2060 const struct lttng_trace_chunk_registry_element target_element
= {
2061 .chunk
.id
.is_set
= !!chunk_id
,
2062 .chunk
.id
.value
= chunk_id
? *chunk_id
: 0,
2063 .session_id
= session_id
,
2065 const unsigned long element_hash
=
2066 lttng_trace_chunk_registry_element_hash(
2068 struct cds_lfht_node
*published_node
;
2069 struct lttng_trace_chunk_registry_element
*published_element
;
2070 struct lttng_trace_chunk
*published_chunk
= NULL
;
2071 struct cds_lfht_iter iter
;
2074 cds_lfht_lookup(registry
->ht
,
2076 lttng_trace_chunk_registry_element_match
,
2079 published_node
= cds_lfht_iter_get_node(&iter
);
2080 if (!published_node
) {
2084 published_element
= container_of(published_node
,
2085 typeof(*published_element
),
2086 trace_chunk_registry_ht_node
);
2087 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
2088 published_chunk
= &published_element
->chunk
;
2092 return published_chunk
;
2095 struct lttng_trace_chunk
*
2096 lttng_trace_chunk_registry_find_chunk(
2097 const struct lttng_trace_chunk_registry
*registry
,
2098 uint64_t session_id
, uint64_t chunk_id
)
2100 return _lttng_trace_chunk_registry_find_chunk(registry
,
2101 session_id
, &chunk_id
);
2104 int lttng_trace_chunk_registry_chunk_exists(
2105 const struct lttng_trace_chunk_registry
*registry
,
2106 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
2109 const struct lttng_trace_chunk_registry_element target_element
= {
2110 .chunk
.id
.is_set
= true,
2111 .chunk
.id
.value
= chunk_id
,
2112 .session_id
= session_id
,
2114 const unsigned long element_hash
=
2115 lttng_trace_chunk_registry_element_hash(
2117 struct cds_lfht_node
*published_node
;
2118 struct cds_lfht_iter iter
;
2121 cds_lfht_lookup(registry
->ht
,
2123 lttng_trace_chunk_registry_element_match
,
2126 published_node
= cds_lfht_iter_get_node(&iter
);
2127 if (!published_node
) {
2128 *chunk_exists
= false;
2132 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
2138 struct lttng_trace_chunk
*
2139 lttng_trace_chunk_registry_find_anonymous_chunk(
2140 const struct lttng_trace_chunk_registry
*registry
,
2141 uint64_t session_id
)
2143 return _lttng_trace_chunk_registry_find_chunk(registry
,
2147 unsigned int lttng_trace_chunk_registry_put_each_chunk(
2148 const struct lttng_trace_chunk_registry
*registry
)
2150 struct cds_lfht_iter iter
;
2151 struct lttng_trace_chunk_registry_element
*chunk_element
;
2152 unsigned int trace_chunks_left
= 0;
2154 DBG("Releasing trace chunk registry to all trace chunks");
2156 cds_lfht_for_each_entry(registry
->ht
,
2157 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
2158 const char *chunk_id_str
= "none";
2159 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
2161 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
2162 if (chunk_element
->chunk
.id
.is_set
) {
2165 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
2167 chunk_element
->chunk
.id
.value
);
2168 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
2169 chunk_id_str
= "formatting error";
2171 chunk_id_str
= chunk_id_buf
;
2175 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
2176 "chunk_id = %s, name = \"%s\", status = %s",
2177 chunk_element
->session_id
,
2179 chunk_element
->chunk
.name
? : "none",
2180 chunk_element
->chunk
.close_command
.is_set
?
2182 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
2183 lttng_trace_chunk_put(&chunk_element
->chunk
);
2184 trace_chunks_left
++;
2187 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
2190 return trace_chunks_left
;