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
;
100 LTTNG_OPTIONAL(time_t) timestamp_creation
;
101 LTTNG_OPTIONAL(time_t) timestamp_close
;
102 LTTNG_OPTIONAL(struct chunk_credentials
) credentials
;
103 struct lttng_directory_handle
*session_output_directory
;
104 struct lttng_directory_handle
*chunk_directory
;
105 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type
) close_command
;
107 * fd_tracker instance through which file descriptors should be
110 * An fd_tracker always outlives any trace chunk; there is no
111 * need to perform any reference counting of that object.
113 struct fd_tracker
*fd_tracker
;
116 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
117 struct lttng_trace_chunk_registry_element
{
118 struct lttng_trace_chunk chunk
;
120 /* Weak and only set when added. */
121 struct lttng_trace_chunk_registry
*registry
;
122 struct cds_lfht_node trace_chunk_registry_ht_node
;
123 /* call_rcu delayed reclaim. */
124 struct rcu_head rcu_node
;
127 struct lttng_trace_chunk_registry
{
131 struct fs_handle_untracked
{
132 struct fs_handle parent
;
135 struct lttng_directory_handle
*directory_handle
;
141 int fs_handle_untracked_get_fd(struct fs_handle
*handle
);
143 void fs_handle_untracked_put_fd(struct fs_handle
*handle
);
145 int fs_handle_untracked_unlink(struct fs_handle
*handle
);
147 int fs_handle_untracked_close(struct fs_handle
*handle
);
150 char *close_command_names
[] = {
151 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
152 "move to completed chunk folder",
153 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
155 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
160 chunk_command close_command_post_release_funcs
[] = {
161 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
162 lttng_trace_chunk_move_to_completed_post_release
,
163 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
164 lttng_trace_chunk_no_operation
,
165 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
166 lttng_trace_chunk_delete_post_release
,
170 struct fs_handle
*fs_handle_untracked_create(
171 struct lttng_directory_handle
*directory_handle
,
175 struct fs_handle_untracked
*handle
= NULL
;
176 bool reference_acquired
;
177 char *path_copy
= strdup(path
);
181 PERROR("Failed to copy file path while creating untracked filesystem handle");
185 handle
= zmalloc(sizeof(typeof(*handle
)));
187 PERROR("Failed to allocate untracked filesystem handle");
191 handle
->parent
= (typeof(handle
->parent
)) {
192 .get_fd
= fs_handle_untracked_get_fd
,
193 .put_fd
= fs_handle_untracked_put_fd
,
194 .unlink
= fs_handle_untracked_unlink
,
195 .close
= fs_handle_untracked_close
,
199 reference_acquired
= lttng_directory_handle_get(directory_handle
);
200 assert(reference_acquired
);
201 handle
->location
.directory_handle
= directory_handle
;
202 /* Ownership is transferred. */
203 handle
->location
.path
= path_copy
;
207 return handle
? &handle
->parent
: NULL
;
211 int fs_handle_untracked_get_fd(struct fs_handle
*_handle
)
213 struct fs_handle_untracked
*handle
= container_of(
214 _handle
, struct fs_handle_untracked
, parent
);
220 void fs_handle_untracked_put_fd(struct fs_handle
*_handle
)
226 int fs_handle_untracked_unlink(struct fs_handle
*_handle
)
228 struct fs_handle_untracked
*handle
= container_of(
229 _handle
, struct fs_handle_untracked
, parent
);
231 return lttng_directory_handle_unlink_file(
232 handle
->location
.directory_handle
,
233 handle
->location
.path
);
237 void fs_handle_untracked_destroy(struct fs_handle_untracked
*handle
)
239 lttng_directory_handle_put(handle
->location
.directory_handle
);
240 free(handle
->location
.path
);
245 int fs_handle_untracked_close(struct fs_handle
*_handle
)
247 struct fs_handle_untracked
*handle
= container_of(
248 _handle
, struct fs_handle_untracked
, parent
);
249 int ret
= close(handle
->fd
);
251 fs_handle_untracked_destroy(handle
);
256 bool lttng_trace_chunk_registry_element_equals(
257 const struct lttng_trace_chunk_registry_element
*a
,
258 const struct lttng_trace_chunk_registry_element
*b
)
260 if (a
->session_id
!= b
->session_id
) {
263 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
266 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
275 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
,
278 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
280 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
281 element_b
= caa_container_of(node
, typeof(*element_b
),
282 trace_chunk_registry_ht_node
);
283 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
287 unsigned long lttng_trace_chunk_registry_element_hash(
288 const struct lttng_trace_chunk_registry_element
*element
)
290 unsigned long hash
= hash_key_u64(&element
->session_id
,
293 if (element
->chunk
.id
.is_set
) {
294 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
301 char *generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
,
302 const time_t *close_timestamp
)
305 char *new_name
= NULL
;
306 char start_datetime
[ISO8601_STR_LEN
] = {};
307 /* Add 1 for a '-' prefix. */
308 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
310 ret
= time_to_iso8601_str(
312 start_datetime
, sizeof(start_datetime
));
314 ERR("Failed to format trace chunk start date time");
317 if (close_timestamp
) {
318 *end_datetime_suffix
= '-';
319 ret
= time_to_iso8601_str(
321 end_datetime_suffix
+ 1,
322 sizeof(end_datetime_suffix
) - 1);
324 ERR("Failed to format trace chunk end date time");
328 new_name
= zmalloc(GENERATED_CHUNK_NAME_LEN
);
330 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
333 ret
= snprintf(new_name
, GENERATED_CHUNK_NAME_LEN
, "%s%s-%" PRIu64
,
334 start_datetime
, end_datetime_suffix
, chunk_id
);
335 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
336 ERR("Failed to format trace chunk name");
347 void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
349 urcu_ref_init(&chunk
->ref
);
350 pthread_mutex_init(&chunk
->lock
, NULL
);
351 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
352 lttng_dynamic_pointer_array_init(&chunk
->files
, free
);
356 void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
358 if (chunk
->session_output_directory
) {
359 lttng_directory_handle_put(
360 chunk
->session_output_directory
);
361 chunk
->session_output_directory
= NULL
;
363 if (chunk
->chunk_directory
) {
364 lttng_directory_handle_put(chunk
->chunk_directory
);
365 chunk
->chunk_directory
= NULL
;
371 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
372 lttng_dynamic_pointer_array_reset(&chunk
->files
);
373 pthread_mutex_destroy(&chunk
->lock
);
377 struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
379 struct lttng_trace_chunk
*chunk
= NULL
;
381 chunk
= zmalloc(sizeof(*chunk
));
383 ERR("Failed to allocate trace chunk");
386 lttng_trace_chunk_init(chunk
);
392 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
394 DBG("Creating anonymous trace chunk");
395 return lttng_trace_chunk_allocate();
399 struct lttng_trace_chunk
*lttng_trace_chunk_create(
400 uint64_t chunk_id
, time_t chunk_creation_time
, const char *path
)
402 struct lttng_trace_chunk
*chunk
;
403 char chunk_creation_datetime_buf
[16] = {};
404 const char *chunk_creation_datetime_str
= "(formatting error)";
405 struct tm timeinfo_buf
, *timeinfo
;
407 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
411 /* Don't fail because of this; it is only used for logging. */
412 strftime_ret
= strftime(chunk_creation_datetime_buf
,
413 sizeof(chunk_creation_datetime_buf
),
414 "%Y%m%d-%H%M%S", timeinfo
);
416 chunk_creation_datetime_str
=
417 chunk_creation_datetime_buf
;
421 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
422 chunk_id
, chunk_creation_datetime_str
);
423 chunk
= lttng_trace_chunk_allocate();
428 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
429 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
431 chunk
->name
= generate_chunk_name(chunk_id
,
432 chunk_creation_time
, NULL
);
434 ERR("Failed to allocate trace chunk name storage");
439 chunk
->path
= strdup(path
);
445 chunk
->path
= strdup(chunk
->name
);
452 DBG("Chunk name set to \"%s\"", chunk
->name
? : "(none)");
456 lttng_trace_chunk_put(chunk
);
461 void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk
*chunk
,
462 struct fd_tracker
*fd_tracker
)
464 assert(!chunk
->session_output_directory
);
465 assert(!chunk
->chunk_directory
);
466 assert(lttng_dynamic_pointer_array_get_count(&chunk
->files
) == 0);
467 chunk
->fd_tracker
= fd_tracker
;
471 struct lttng_trace_chunk
*lttng_trace_chunk_copy(
472 struct lttng_trace_chunk
*source_chunk
)
474 struct lttng_trace_chunk
*new_chunk
= lttng_trace_chunk_allocate();
480 pthread_mutex_lock(&source_chunk
->lock
);
482 * A new chunk is always a user; it shall create no new trace
485 new_chunk
->mode
= (typeof(new_chunk
->mode
)) {
487 .value
= TRACE_CHUNK_MODE_USER
,
490 * top_level_directories is not copied as it is never used
491 * by _user_ mode chunks.
493 /* The new chunk is not part of a registry (yet, at least). */
494 new_chunk
->in_registry_element
= false;
495 new_chunk
->name_overridden
= source_chunk
->name_overridden
;
496 if (source_chunk
->name
) {
497 new_chunk
->name
= strdup(source_chunk
->name
);
498 if (!new_chunk
->name
) {
499 ERR("Failed to copy source trace chunk name in %s()",
504 if (source_chunk
->path
) {
505 new_chunk
->path
= strdup(source_chunk
->path
);
506 if (!new_chunk
->path
) {
507 ERR("Failed to copy source trace chunk path in %s()",
511 new_chunk
->id
= source_chunk
->id
;
512 new_chunk
->timestamp_creation
= source_chunk
->timestamp_creation
;
513 new_chunk
->timestamp_close
= source_chunk
->timestamp_close
;
514 new_chunk
->credentials
= source_chunk
->credentials
;
515 if (source_chunk
->session_output_directory
) {
516 const bool reference_acquired
= lttng_directory_handle_get(
517 source_chunk
->session_output_directory
);
519 assert(reference_acquired
);
520 new_chunk
->session_output_directory
=
521 source_chunk
->session_output_directory
;
523 if (source_chunk
->chunk_directory
) {
524 const bool reference_acquired
= lttng_directory_handle_get(
525 source_chunk
->chunk_directory
);
527 assert(reference_acquired
);
528 new_chunk
->chunk_directory
= source_chunk
->chunk_directory
;
530 new_chunk
->close_command
= source_chunk
->close_command
;
531 new_chunk
->fd_tracker
= source_chunk
->fd_tracker
;
532 pthread_mutex_unlock(&source_chunk
->lock
);
536 pthread_mutex_unlock(&source_chunk
->lock
);
537 lttng_trace_chunk_put(new_chunk
);
542 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(
543 struct lttng_trace_chunk
*chunk
, uint64_t *id
)
545 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
547 pthread_mutex_lock(&chunk
->lock
);
548 if (chunk
->id
.is_set
) {
549 *id
= chunk
->id
.value
;
551 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
553 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
);
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
);
591 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(
592 struct lttng_trace_chunk
*chunk
, time_t close_ts
)
594 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
596 pthread_mutex_lock(&chunk
->lock
);
597 if (!chunk
->timestamp_creation
.is_set
) {
598 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
599 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
602 if (chunk
->timestamp_creation
.value
> close_ts
) {
603 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
604 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
607 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
608 if (!chunk
->name_overridden
) {
610 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
611 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
614 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
618 pthread_mutex_unlock(&chunk
->lock
);
623 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(
624 struct lttng_trace_chunk
*chunk
, const char **name
,
625 bool *name_overridden
)
627 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
629 pthread_mutex_lock(&chunk
->lock
);
630 if (name_overridden
) {
631 *name_overridden
= chunk
->name_overridden
;
634 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
639 pthread_mutex_unlock(&chunk
->lock
);
644 bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk
*chunk
)
646 bool name_overridden
;
648 pthread_mutex_lock(&chunk
->lock
);
649 name_overridden
= chunk
->name_overridden
;
650 pthread_mutex_unlock(&chunk
->lock
);
651 return name_overridden
;
655 bool is_valid_chunk_name(const char *name
)
663 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
664 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
668 if (strchr(name
, '/') || strchr(name
, '.')) {
676 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
677 struct lttng_trace_chunk
*chunk
, const char *name
)
680 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
681 char *new_name
, *new_path
;
683 DBG("Override trace chunk name from %s to %s", chunk
->name
, name
);
684 if (!is_valid_chunk_name(name
)) {
685 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
687 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
691 pthread_mutex_lock(&chunk
->lock
);
692 if (!chunk
->id
.is_set
) {
693 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
695 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
699 new_name
= strdup(name
);
701 ERR("Failed to allocate new trace chunk name");
702 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
706 chunk
->name
= new_name
;
708 new_path
= strdup(name
);
710 ERR("Failed to allocate new trace chunk path");
711 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
715 chunk
->path
= new_path
;
717 chunk
->name_overridden
= true;
719 pthread_mutex_unlock(&chunk
->lock
);
725 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
726 struct lttng_trace_chunk
*chunk
, const char *path
)
729 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
730 struct lttng_directory_handle
*rename_directory
= NULL
;
731 char *new_path
, *old_path
;
734 if (chunk
->name_overridden
) {
735 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
739 old_path
= chunk
->path
;
740 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path
, path
);
742 if ((!old_path
&& !path
) ||
743 (old_path
&& path
&& !strcmp(old_path
, path
))) {
747 * Use chunk name as path if NULL path is specified.
753 /* Renaming from "" to "" is not accepted. */
754 if (path
[0] == '\0' && old_path
[0] == '\0') {
755 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
760 * If a rename is performed on a chunk for which the chunk_directory
761 * is not set (yet), or the session_output_directory is not set
762 * (interacting with a relay daemon), there is no rename to perform.
764 if (!chunk
->chunk_directory
||
765 !chunk
->session_output_directory
) {
769 if (old_path
&& old_path
[0] != '\0' && path
[0] != '\0') {
770 /* Rename chunk directory. */
771 ret
= lttng_directory_handle_rename_as_user(
772 chunk
->session_output_directory
,
774 chunk
->session_output_directory
,
776 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
778 &chunk
->credentials
.value
.user
);
780 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
782 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
785 rename_directory
= chunk
->fd_tracker
?
786 fd_tracker_create_directory_handle_from_handle(
788 chunk
->session_output_directory
,
790 lttng_directory_handle_create_from_handle(
792 chunk
->session_output_directory
);
793 if (!rename_directory
) {
794 ERR("Failed to get handle to trace chunk rename directory");
795 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
799 /* Release old handle. */
800 lttng_directory_handle_put(chunk
->chunk_directory
);
802 * Transfer new handle reference to chunk as the current chunk
805 chunk
->chunk_directory
= rename_directory
;
806 rename_directory
= NULL
;
807 } else if (old_path
&& old_path
[0] == '\0') {
808 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
809 &chunk
->top_level_directories
);
811 ret
= lttng_directory_handle_create_subdirectory_as_user(
812 chunk
->session_output_directory
,
815 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
817 &chunk
->credentials
.value
.user
);
819 PERROR("Failed to create trace chunk rename directory \"%s\"",
821 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
825 rename_directory
= lttng_directory_handle_create_from_handle(
826 path
, chunk
->session_output_directory
);
827 if (!rename_directory
) {
828 ERR("Failed to get handle to trace chunk rename directory");
829 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
833 /* Move toplevel directories. */
834 for (i
= 0; i
< count
; i
++) {
835 const char *top_level_name
=
836 lttng_dynamic_pointer_array_get_pointer(
837 &chunk
->top_level_directories
, i
);
839 ret
= lttng_directory_handle_rename_as_user(
840 chunk
->chunk_directory
,
844 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
846 &chunk
->credentials
.value
.user
);
848 PERROR("Failed to move \"%s\" to trace chunk rename directory",
850 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
854 /* Release old handle. */
855 lttng_directory_handle_put(chunk
->chunk_directory
);
857 * Transfer new handle reference to chunk as the current chunk
860 chunk
->chunk_directory
= rename_directory
;
861 rename_directory
= NULL
;
863 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
864 &chunk
->top_level_directories
);
865 const bool reference_acquired
= lttng_directory_handle_get(
866 chunk
->session_output_directory
);
868 assert(reference_acquired
);
869 rename_directory
= chunk
->session_output_directory
;
871 /* Move toplevel directories. */
872 for (i
= 0; i
< count
; i
++) {
873 const char *top_level_name
=
874 lttng_dynamic_pointer_array_get_pointer(
875 &chunk
->top_level_directories
, i
);
877 ret
= lttng_directory_handle_rename_as_user(
878 chunk
->chunk_directory
,
882 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
884 &chunk
->credentials
.value
.user
);
886 PERROR("Failed to move \"%s\" to trace chunk rename directory",
888 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
892 /* Release old handle. */
893 lttng_directory_handle_put(chunk
->chunk_directory
);
895 * Transfer new handle reference to chunk as the current chunk
898 chunk
->chunk_directory
= rename_directory
;
899 rename_directory
= NULL
;
901 /* Remove old directory. */
902 status
= lttng_directory_handle_remove_subdirectory(
903 chunk
->session_output_directory
,
905 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
906 ERR("Error removing subdirectory '%s' file when deleting chunk",
914 new_path
= strdup(path
);
916 ERR("Failed to allocate new trace chunk path");
917 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
921 chunk
->path
= new_path
;
923 lttng_directory_handle_put(rename_directory
);
928 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path(
929 struct lttng_trace_chunk
*chunk
, const char *path
)
932 enum lttng_trace_chunk_status status
;
934 pthread_mutex_lock(&chunk
->lock
);
935 status
= lttng_trace_chunk_rename_path_no_lock(chunk
, path
);
936 pthread_mutex_unlock(&chunk
->lock
);
942 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
943 struct lttng_trace_chunk
*chunk
,
944 struct lttng_credentials
*credentials
)
946 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
948 pthread_mutex_lock(&chunk
->lock
);
949 if (chunk
->credentials
.is_set
) {
950 if (chunk
->credentials
.value
.use_current_user
) {
951 credentials
->uid
= geteuid();
952 credentials
->gid
= getegid();
954 *credentials
= chunk
->credentials
.value
.user
;
957 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
959 pthread_mutex_unlock(&chunk
->lock
);
964 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
965 struct lttng_trace_chunk
*chunk
,
966 const struct lttng_credentials
*user_credentials
)
968 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
969 const struct chunk_credentials credentials
= {
970 .user
= *user_credentials
,
971 .use_current_user
= false,
974 pthread_mutex_lock(&chunk
->lock
);
975 if (chunk
->credentials
.is_set
) {
976 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
979 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
981 pthread_mutex_unlock(&chunk
->lock
);
986 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
987 struct lttng_trace_chunk
*chunk
)
989 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
990 const struct chunk_credentials credentials
= {
991 .use_current_user
= true,
994 pthread_mutex_lock(&chunk
->lock
);
995 if (chunk
->credentials
.is_set
) {
996 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
999 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
1001 pthread_mutex_unlock(&chunk
->lock
);
1007 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
1008 struct lttng_trace_chunk
*chunk
,
1009 struct lttng_directory_handle
*session_output_directory
)
1012 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1013 struct lttng_directory_handle
*chunk_directory_handle
= NULL
;
1014 bool reference_acquired
;
1016 pthread_mutex_lock(&chunk
->lock
);
1017 if (chunk
->mode
.is_set
) {
1018 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1021 if (!chunk
->credentials
.is_set
) {
1023 * Fatal error, credentials must be set before a
1024 * directory is created.
1026 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
1027 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1030 if (chunk
->path
[0] != '\0') {
1031 ret
= lttng_directory_handle_create_subdirectory_as_user(
1032 session_output_directory
,
1035 !chunk
->credentials
.value
.use_current_user
?
1036 &chunk
->credentials
.value
.user
: NULL
);
1038 PERROR("Failed to create chunk output directory \"%s\"",
1040 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1043 chunk_directory_handle
=
1045 fd_tracker_create_directory_handle_from_handle(
1047 session_output_directory
,
1049 lttng_directory_handle_create_from_handle(
1051 session_output_directory
);
1052 if (!chunk_directory_handle
) {
1053 /* The function already logs on all error paths. */
1054 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1059 * A nameless chunk does not need its own output directory.
1060 * The session's output directory will be used.
1062 const bool reference_acquired
=
1063 lttng_directory_handle_get(
1064 session_output_directory
);
1066 assert(reference_acquired
);
1067 chunk_directory_handle
= session_output_directory
;
1069 chunk
->chunk_directory
= chunk_directory_handle
;
1070 chunk_directory_handle
= NULL
;
1071 reference_acquired
= lttng_directory_handle_get(
1072 session_output_directory
);
1073 assert(reference_acquired
);
1074 chunk
->session_output_directory
= session_output_directory
;
1075 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
1077 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 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
);
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 assert(reference_acquired
);
1126 *handle
= chunk
->session_output_directory
;
1129 pthread_mutex_unlock(&chunk
->lock
);
1134 enum lttng_trace_chunk_status
lttng_trace_chunk_borrow_chunk_directory_handle(
1135 struct lttng_trace_chunk
*chunk
,
1136 const struct lttng_directory_handle
**handle
)
1138 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1140 pthread_mutex_lock(&chunk
->lock
);
1141 if (!chunk
->chunk_directory
) {
1142 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1146 *handle
= chunk
->chunk_directory
;
1148 pthread_mutex_unlock(&chunk
->lock
);
1152 /* Add a top-level directory to the trace chunk if it was previously unknown. */
1154 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
1155 const char *new_path
)
1159 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
1160 &chunk
->top_level_directories
);
1161 const char *new_path_separator_pos
= strchr(new_path
, '/');
1162 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
1163 new_path_separator_pos
- new_path
: strlen(new_path
);
1165 for (i
= 0; i
< count
; i
++) {
1166 const char *path
= lttng_dynamic_pointer_array_get_pointer(
1167 &chunk
->top_level_directories
, i
);
1168 const ptrdiff_t path_top_level_len
= strlen(path
);
1170 if (path_top_level_len
!= new_path_top_level_len
) {
1173 if (!strncmp(path
, new_path
, path_top_level_len
)) {
1180 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
1182 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1183 new_path
, chunk
->name
? : "(unnamed)");
1185 PERROR("Failed to copy path");
1189 ret
= lttng_dynamic_pointer_array_add_pointer(
1190 &chunk
->top_level_directories
, copy
);
1192 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1202 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
1203 struct lttng_trace_chunk
*chunk
,
1207 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1209 DBG("Creating trace chunk subdirectory \"%s\"", path
);
1210 pthread_mutex_lock(&chunk
->lock
);
1211 if (!chunk
->credentials
.is_set
) {
1213 * Fatal error, credentials must be set before a
1214 * directory is created.
1216 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1218 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1221 if (!chunk
->mode
.is_set
||
1222 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
1223 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1225 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1228 if (!chunk
->chunk_directory
) {
1229 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1231 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1235 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1237 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1240 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
1241 chunk
->chunk_directory
, path
,
1243 chunk
->credentials
.value
.use_current_user
?
1244 NULL
: &chunk
->credentials
.value
.user
);
1246 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1248 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1251 ret
= add_top_level_directory_unique(chunk
, path
);
1253 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1257 pthread_mutex_unlock(&chunk
->lock
);
1262 * TODO: Implement O(1) lookup.
1265 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk
*chunk
,
1266 const char *path
, size_t *index
)
1270 count
= lttng_dynamic_pointer_array_get_count(&chunk
->files
);
1271 for (i
= 0; i
< count
; i
++) {
1272 const char *iter_path
=
1273 lttng_dynamic_pointer_array_get_pointer(
1275 if (!strcmp(iter_path
, path
)) {
1286 enum lttng_trace_chunk_status
lttng_trace_chunk_add_file(
1287 struct lttng_trace_chunk
*chunk
,
1292 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1294 if (lttng_trace_chunk_find_file(chunk
, path
, NULL
)) {
1295 return LTTNG_TRACE_CHUNK_STATUS_OK
;
1297 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1298 path
, chunk
->name
? : "(unnamed)");
1299 copy
= strdup(path
);
1301 PERROR("Failed to copy path");
1302 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1305 ret
= lttng_dynamic_pointer_array_add_pointer(
1306 &chunk
->files
, copy
);
1308 ERR("Allocation failure while adding file to a trace chunk");
1310 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1318 void lttng_trace_chunk_remove_file(
1319 struct lttng_trace_chunk
*chunk
,
1326 found
= lttng_trace_chunk_find_file(chunk
, path
, &index
);
1330 ret
= lttng_dynamic_pointer_array_remove_pointer(
1331 &chunk
->files
, index
);
1336 enum lttng_trace_chunk_status
_lttng_trace_chunk_open_fs_handle_locked(
1337 struct lttng_trace_chunk
*chunk
,
1338 const char *file_path
,
1341 struct fs_handle
**out_handle
,
1342 bool expect_no_file
)
1345 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1347 DBG("Opening trace chunk file \"%s\"", file_path
);
1348 if (!chunk
->credentials
.is_set
) {
1350 * Fatal error, credentials must be set before a
1353 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1355 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1358 if (!chunk
->chunk_directory
) {
1359 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1361 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1364 status
= lttng_trace_chunk_add_file(chunk
, file_path
);
1365 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1368 if (chunk
->fd_tracker
) {
1369 assert(chunk
->credentials
.value
.use_current_user
);
1370 *out_handle
= fd_tracker_open_fs_handle(chunk
->fd_tracker
,
1371 chunk
->chunk_directory
, file_path
, flags
, &mode
);
1372 ret
= *out_handle
? 0 : -1;
1374 ret
= lttng_directory_handle_open_file_as_user(
1375 chunk
->chunk_directory
, file_path
, flags
, mode
,
1376 chunk
->credentials
.value
.use_current_user
?
1378 &chunk
->credentials
.value
.user
);
1380 *out_handle
= fs_handle_untracked_create(
1381 chunk
->chunk_directory
, file_path
, ret
);
1383 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1389 if (errno
== ENOENT
&& expect_no_file
) {
1390 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
1392 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1393 file_path
, flags
, (int) mode
);
1394 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1396 lttng_trace_chunk_remove_file(chunk
, file_path
);
1404 enum lttng_trace_chunk_status
lttng_trace_chunk_open_fs_handle(
1405 struct lttng_trace_chunk
*chunk
,
1406 const char *file_path
,
1409 struct fs_handle
**out_handle
,
1410 bool expect_no_file
)
1412 enum lttng_trace_chunk_status status
;
1414 pthread_mutex_lock(&chunk
->lock
);
1415 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1416 flags
, mode
, out_handle
, expect_no_file
);
1417 pthread_mutex_unlock(&chunk
->lock
);
1422 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
1423 struct lttng_trace_chunk
*chunk
,
1424 const char *file_path
,
1428 bool expect_no_file
)
1430 enum lttng_trace_chunk_status status
;
1431 struct fs_handle
*fs_handle
;
1433 pthread_mutex_lock(&chunk
->lock
);
1435 * Using this method is never valid when an fd_tracker is being
1436 * used since the resulting file descriptor would not be tracked.
1438 assert(!chunk
->fd_tracker
);
1439 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1440 flags
, mode
, &fs_handle
, expect_no_file
);
1441 pthread_mutex_unlock(&chunk
->lock
);
1443 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
1444 *out_fd
= fs_handle_get_fd(fs_handle
);
1446 * Does not close the fd; we just "unbox" it from the fs_handle.
1448 fs_handle_untracked_destroy(container_of(
1449 fs_handle
, struct fs_handle_untracked
, parent
));
1456 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
1457 const char *file_path
)
1460 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1462 DBG("Unlinking trace chunk file \"%s\"", file_path
);
1463 pthread_mutex_lock(&chunk
->lock
);
1464 if (!chunk
->credentials
.is_set
) {
1466 * Fatal error, credentials must be set before a
1469 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1471 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1474 if (!chunk
->chunk_directory
) {
1475 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1477 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1480 ret
= lttng_directory_handle_unlink_file_as_user(
1481 chunk
->chunk_directory
, file_path
,
1482 chunk
->credentials
.value
.use_current_user
?
1483 NULL
: &chunk
->credentials
.value
.user
);
1485 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1488 lttng_trace_chunk_remove_file(chunk
, file_path
);
1490 pthread_mutex_unlock(&chunk
->lock
);
1495 int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk
*chunk
,
1499 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1501 DBG("Recursively removing trace chunk directory \"%s\"", path
);
1502 pthread_mutex_lock(&chunk
->lock
);
1503 if (!chunk
->credentials
.is_set
) {
1505 * Fatal error, credentials must be set before a
1506 * directory is removed.
1508 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1510 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1513 if (!chunk
->chunk_directory
) {
1514 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1516 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1519 ret
= lttng_directory_handle_remove_subdirectory_recursive_as_user(
1520 chunk
->chunk_directory
, path
,
1521 chunk
->credentials
.value
.use_current_user
?
1522 NULL
: &chunk
->credentials
.value
.user
,
1523 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
);
1525 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1529 pthread_mutex_unlock(&chunk
->lock
);
1534 int lttng_trace_chunk_move_to_completed_post_release(
1535 struct lttng_trace_chunk
*trace_chunk
)
1538 char *archived_chunk_name
= NULL
;
1539 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
1540 const time_t creation_timestamp
=
1541 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
1542 const time_t close_timestamp
=
1543 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
1544 struct lttng_directory_handle
*archived_chunks_directory
= NULL
;
1545 enum lttng_trace_chunk_status status
;
1547 if (!trace_chunk
->mode
.is_set
||
1548 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
1549 !trace_chunk
->session_output_directory
) {
1551 * This command doesn't need to run if the output is remote
1552 * or if the trace chunk is not owned by this process.
1557 assert(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
1558 assert(!trace_chunk
->name_overridden
);
1559 assert(trace_chunk
->path
);
1561 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
1563 if (!archived_chunk_name
) {
1564 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1569 ret
= lttng_directory_handle_create_subdirectory_as_user(
1570 trace_chunk
->session_output_directory
,
1571 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1573 !trace_chunk
->credentials
.value
.use_current_user
?
1574 &trace_chunk
->credentials
.value
.user
:
1577 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1578 "\" directory for archived trace chunks");
1582 archived_chunks_directory
= trace_chunk
->fd_tracker
?
1583 fd_tracker_create_directory_handle_from_handle(
1584 trace_chunk
->fd_tracker
,
1585 trace_chunk
->session_output_directory
,
1586 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
) :
1587 lttng_directory_handle_create_from_handle(
1588 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1589 trace_chunk
->session_output_directory
);
1590 if (!archived_chunks_directory
) {
1591 PERROR("Failed to get handle to archived trace chunks directory");
1597 * Make sure chunk is renamed to old directory if not already done by
1598 * the creation of the next chunk. This happens if a rotation is
1599 * performed while tracing is stopped.
1601 if (!trace_chunk
->path
|| strcmp(trace_chunk
->path
,
1602 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
)) {
1603 status
= lttng_trace_chunk_rename_path_no_lock(trace_chunk
,
1604 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1605 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1606 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1612 ret
= lttng_directory_handle_rename_as_user(
1613 trace_chunk
->session_output_directory
,
1615 archived_chunks_directory
,
1616 archived_chunk_name
,
1617 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1619 &trace_chunk
->credentials
.value
.user
);
1621 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1623 archived_chunk_name
);
1627 lttng_directory_handle_put(archived_chunks_directory
);
1628 free(archived_chunk_name
);
1633 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
)
1639 int lttng_trace_chunk_delete_post_release_user(
1640 struct lttng_trace_chunk
*trace_chunk
)
1644 DBG("Trace chunk \"delete\" close command post-release (User)");
1646 /* Unlink all files. */
1647 while (lttng_dynamic_pointer_array_get_count(&trace_chunk
->files
) != 0) {
1648 enum lttng_trace_chunk_status status
;
1652 path
= lttng_dynamic_pointer_array_get_pointer(
1653 &trace_chunk
->files
, 0);
1654 DBG("Unlink file: %s", path
);
1655 status
= lttng_trace_chunk_unlink_file(trace_chunk
, path
);
1656 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1657 ERR("Error unlinking file '%s' when deleting chunk", path
);
1667 int lttng_trace_chunk_delete_post_release_owner(
1668 struct lttng_trace_chunk
*trace_chunk
)
1670 enum lttng_trace_chunk_status status
;
1674 ret
= lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1679 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1681 assert(trace_chunk
->session_output_directory
);
1682 assert(trace_chunk
->chunk_directory
);
1684 /* Remove empty directories. */
1685 count
= lttng_dynamic_pointer_array_get_count(
1686 &trace_chunk
->top_level_directories
);
1688 for (i
= 0; i
< count
; i
++) {
1689 const char *top_level_name
=
1690 lttng_dynamic_pointer_array_get_pointer(
1691 &trace_chunk
->top_level_directories
, i
);
1693 status
= lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk
, top_level_name
);
1694 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1695 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1702 lttng_directory_handle_put(trace_chunk
->chunk_directory
);
1703 trace_chunk
->chunk_directory
= NULL
;
1705 if (trace_chunk
->path
&& trace_chunk
->path
[0] != '\0') {
1706 status
= lttng_directory_handle_remove_subdirectory(
1707 trace_chunk
->session_output_directory
,
1709 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1710 ERR("Error removing subdirectory '%s' file when deleting chunk",
1716 free(trace_chunk
->path
);
1717 trace_chunk
->path
= NULL
;
1723 * For local files, session and consumer daemons all run the delete hook. The
1724 * consumer daemons have the list of files to unlink, and technically the
1725 * session daemon is the owner of the chunk. Unlink all files owned by each
1729 int lttng_trace_chunk_delete_post_release(
1730 struct lttng_trace_chunk
*trace_chunk
)
1732 if (!trace_chunk
->chunk_directory
) {
1736 if (trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
) {
1737 return lttng_trace_chunk_delete_post_release_owner(trace_chunk
);
1739 return lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1744 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
1745 struct lttng_trace_chunk
*chunk
,
1746 enum lttng_trace_chunk_command_type
*command_type
)
1748 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1750 pthread_mutex_lock(&chunk
->lock
);
1751 if (chunk
->close_command
.is_set
) {
1752 *command_type
= chunk
->close_command
.value
;
1753 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1755 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1757 pthread_mutex_unlock(&chunk
->lock
);
1762 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
1763 struct lttng_trace_chunk
*chunk
,
1764 enum lttng_trace_chunk_command_type close_command
)
1766 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1768 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1769 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1770 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1774 pthread_mutex_lock(&chunk
->lock
);
1775 if (chunk
->close_command
.is_set
) {
1776 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1777 close_command_names
[chunk
->close_command
.value
],
1778 close_command_names
[close_command
]);
1780 DBG("Setting trace chunk close command to \"%s\"",
1781 close_command_names
[close_command
]);
1784 * Unset close command for no-op for backward compatibility with relayd
1787 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1788 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1790 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1792 pthread_mutex_unlock(&chunk
->lock
);
1798 const char *lttng_trace_chunk_command_type_get_name(
1799 enum lttng_trace_chunk_command_type command
)
1802 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1803 return "move to completed trace chunk folder";
1804 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1805 return "no operation";
1806 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1814 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1816 return urcu_ref_get_unless_zero(&chunk
->ref
);
1820 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1822 struct lttng_trace_chunk_registry_element
*element
=
1823 container_of(node
, typeof(*element
), rcu_node
);
1825 lttng_trace_chunk_fini(&element
->chunk
);
1830 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1832 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1835 if (chunk
->close_command
.is_set
) {
1836 if (close_command_post_release_funcs
[
1837 chunk
->close_command
.value
](chunk
)) {
1838 ERR("Trace chunk post-release command %s has failed.",
1839 close_command_names
[chunk
->close_command
.value
]);
1843 if (chunk
->in_registry_element
) {
1844 struct lttng_trace_chunk_registry_element
*element
;
1846 element
= container_of(chunk
, typeof(*element
), chunk
);
1847 if (element
->registry
) {
1849 cds_lfht_del(element
->registry
->ht
,
1850 &element
->trace_chunk_registry_ht_node
);
1852 call_rcu(&element
->rcu_node
,
1853 free_lttng_trace_chunk_registry_element
);
1855 /* Never published, can be free'd immediately. */
1856 free_lttng_trace_chunk_registry_element(
1857 &element
->rcu_node
);
1860 /* Not RCU-protected, free immediately. */
1861 lttng_trace_chunk_fini(chunk
);
1867 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1872 assert(chunk
->ref
.refcount
);
1873 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1877 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1879 struct lttng_trace_chunk_registry
*registry
;
1881 registry
= zmalloc(sizeof(*registry
));
1886 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1887 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1888 if (!registry
->ht
) {
1894 lttng_trace_chunk_registry_destroy(registry
);
1899 void lttng_trace_chunk_registry_destroy(
1900 struct lttng_trace_chunk_registry
*registry
)
1906 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1913 struct lttng_trace_chunk_registry_element
*
1914 lttng_trace_chunk_registry_element_create_from_chunk(
1915 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1917 struct lttng_trace_chunk_registry_element
*element
=
1918 zmalloc(sizeof(*element
));
1923 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1924 element
->session_id
= session_id
;
1926 element
->chunk
= *chunk
;
1927 lttng_trace_chunk_init(&element
->chunk
);
1928 if (chunk
->session_output_directory
) {
1929 /* Transferred ownership. */
1930 element
->chunk
.session_output_directory
=
1931 chunk
->session_output_directory
;
1932 chunk
->session_output_directory
= NULL
;
1934 if (chunk
->chunk_directory
) {
1935 /* Transferred ownership. */
1936 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1937 chunk
->chunk_directory
= NULL
;
1940 * The original chunk becomes invalid; the name and path attributes are
1941 * transferred to the new chunk instance.
1945 element
->chunk
.fd_tracker
= chunk
->fd_tracker
;
1946 element
->chunk
.in_registry_element
= true;
1952 struct lttng_trace_chunk
*
1953 lttng_trace_chunk_registry_publish_chunk(
1954 struct lttng_trace_chunk_registry
*registry
,
1955 uint64_t session_id
, struct lttng_trace_chunk
*chunk
)
1957 struct lttng_trace_chunk_registry_element
*element
;
1958 unsigned long element_hash
;
1960 pthread_mutex_lock(&chunk
->lock
);
1961 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
1963 pthread_mutex_unlock(&chunk
->lock
);
1968 * chunk is now invalid, the only valid operation is a 'put' from the
1972 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1976 struct cds_lfht_node
*published_node
;
1977 struct lttng_trace_chunk
*published_chunk
;
1978 struct lttng_trace_chunk_registry_element
*published_element
;
1980 published_node
= cds_lfht_add_unique(registry
->ht
,
1982 lttng_trace_chunk_registry_element_match
,
1984 &element
->trace_chunk_registry_ht_node
);
1985 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
1986 /* Successfully published the new element. */
1987 element
->registry
= registry
;
1988 /* Acquire a reference for the caller. */
1989 if (lttng_trace_chunk_get(&element
->chunk
)) {
1993 * Another thread concurrently unpublished the
1994 * trace chunk. This is currently unexpected.
1996 * Re-attempt to publish.
1998 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
2004 * An equivalent trace chunk was published before this trace
2005 * chunk. Attempt to acquire a reference to the one that was
2006 * already published and release the reference to the copy we
2007 * created if successful.
2009 published_element
= container_of(published_node
,
2010 typeof(*published_element
),
2011 trace_chunk_registry_ht_node
);
2012 published_chunk
= &published_element
->chunk
;
2013 if (lttng_trace_chunk_get(published_chunk
)) {
2014 lttng_trace_chunk_put(&element
->chunk
);
2015 element
= published_element
;
2019 * A reference to the previously published trace chunk could not
2020 * be acquired. Hence, retry to publish our copy of the trace
2026 return element
? &element
->chunk
: NULL
;
2030 * Note that the caller must be registered as an RCU thread.
2031 * However, it does not need to hold the RCU read lock. The RCU read lock is
2032 * acquired to perform the look-up in the registry's hash table and held until
2033 * after a reference to the "found" trace chunk is acquired.
2035 * IOW, holding a reference guarantees the existence of the object for the
2039 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
2040 const struct lttng_trace_chunk_registry
*registry
,
2041 uint64_t session_id
, uint64_t *chunk_id
)
2043 const struct lttng_trace_chunk_registry_element target_element
= {
2044 .chunk
.id
.is_set
= !!chunk_id
,
2045 .chunk
.id
.value
= chunk_id
? *chunk_id
: 0,
2046 .session_id
= session_id
,
2048 const unsigned long element_hash
=
2049 lttng_trace_chunk_registry_element_hash(
2051 struct cds_lfht_node
*published_node
;
2052 struct lttng_trace_chunk_registry_element
*published_element
;
2053 struct lttng_trace_chunk
*published_chunk
= NULL
;
2054 struct cds_lfht_iter iter
;
2057 cds_lfht_lookup(registry
->ht
,
2059 lttng_trace_chunk_registry_element_match
,
2062 published_node
= cds_lfht_iter_get_node(&iter
);
2063 if (!published_node
) {
2067 published_element
= container_of(published_node
,
2068 typeof(*published_element
),
2069 trace_chunk_registry_ht_node
);
2070 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
2071 published_chunk
= &published_element
->chunk
;
2075 return published_chunk
;
2079 struct lttng_trace_chunk
*
2080 lttng_trace_chunk_registry_find_chunk(
2081 const struct lttng_trace_chunk_registry
*registry
,
2082 uint64_t session_id
, uint64_t chunk_id
)
2084 return _lttng_trace_chunk_registry_find_chunk(registry
,
2085 session_id
, &chunk_id
);
2089 int lttng_trace_chunk_registry_chunk_exists(
2090 const struct lttng_trace_chunk_registry
*registry
,
2091 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
2094 const struct lttng_trace_chunk_registry_element target_element
= {
2095 .chunk
.id
.is_set
= true,
2096 .chunk
.id
.value
= chunk_id
,
2097 .session_id
= session_id
,
2099 const unsigned long element_hash
=
2100 lttng_trace_chunk_registry_element_hash(
2102 struct cds_lfht_node
*published_node
;
2103 struct cds_lfht_iter iter
;
2106 cds_lfht_lookup(registry
->ht
,
2108 lttng_trace_chunk_registry_element_match
,
2111 published_node
= cds_lfht_iter_get_node(&iter
);
2112 if (!published_node
) {
2113 *chunk_exists
= false;
2117 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
2124 struct lttng_trace_chunk
*
2125 lttng_trace_chunk_registry_find_anonymous_chunk(
2126 const struct lttng_trace_chunk_registry
*registry
,
2127 uint64_t session_id
)
2129 return _lttng_trace_chunk_registry_find_chunk(registry
,
2134 unsigned int lttng_trace_chunk_registry_put_each_chunk(
2135 const struct lttng_trace_chunk_registry
*registry
)
2137 struct cds_lfht_iter iter
;
2138 struct lttng_trace_chunk_registry_element
*chunk_element
;
2139 unsigned int trace_chunks_left
= 0;
2141 DBG("Releasing trace chunk registry to all trace chunks");
2143 cds_lfht_for_each_entry(registry
->ht
,
2144 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
2145 const char *chunk_id_str
= "none";
2146 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
2148 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
2149 if (chunk_element
->chunk
.id
.is_set
) {
2152 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
2154 chunk_element
->chunk
.id
.value
);
2155 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
2156 chunk_id_str
= "formatting error";
2158 chunk_id_str
= chunk_id_buf
;
2162 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
2163 "chunk_id = %s, name = \"%s\", status = %s",
2164 chunk_element
->session_id
,
2166 chunk_element
->chunk
.name
? : "none",
2167 chunk_element
->chunk
.close_command
.is_set
?
2169 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
2170 lttng_trace_chunk_put(&chunk_element
->chunk
);
2171 trace_chunks_left
++;
2174 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
2177 return trace_chunks_left
;