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
;
862 } else if (old_path
) {
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",
911 /* Unexpected !old_path && !path. */
912 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
917 new_path
= strdup(path
);
919 ERR("Failed to allocate new trace chunk path");
920 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
924 chunk
->path
= new_path
;
926 lttng_directory_handle_put(rename_directory
);
931 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path(
932 struct lttng_trace_chunk
*chunk
, const char *path
)
935 enum lttng_trace_chunk_status status
;
937 pthread_mutex_lock(&chunk
->lock
);
938 status
= lttng_trace_chunk_rename_path_no_lock(chunk
, path
);
939 pthread_mutex_unlock(&chunk
->lock
);
945 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
946 struct lttng_trace_chunk
*chunk
,
947 struct lttng_credentials
*credentials
)
949 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
951 pthread_mutex_lock(&chunk
->lock
);
952 if (chunk
->credentials
.is_set
) {
953 if (chunk
->credentials
.value
.use_current_user
) {
954 credentials
->uid
= geteuid();
955 credentials
->gid
= getegid();
957 *credentials
= chunk
->credentials
.value
.user
;
960 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
962 pthread_mutex_unlock(&chunk
->lock
);
967 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
968 struct lttng_trace_chunk
*chunk
,
969 const struct lttng_credentials
*user_credentials
)
971 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
972 const struct chunk_credentials credentials
= {
973 .user
= *user_credentials
,
974 .use_current_user
= false,
977 pthread_mutex_lock(&chunk
->lock
);
978 if (chunk
->credentials
.is_set
) {
979 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
982 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
984 pthread_mutex_unlock(&chunk
->lock
);
989 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
990 struct lttng_trace_chunk
*chunk
)
992 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
993 const struct chunk_credentials credentials
= {
994 .use_current_user
= true,
997 pthread_mutex_lock(&chunk
->lock
);
998 if (chunk
->credentials
.is_set
) {
999 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1002 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
1004 pthread_mutex_unlock(&chunk
->lock
);
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 const bool reference_acquired
=
1066 lttng_directory_handle_get(
1067 session_output_directory
);
1069 assert(reference_acquired
);
1070 chunk_directory_handle
= session_output_directory
;
1072 chunk
->chunk_directory
= chunk_directory_handle
;
1073 chunk_directory_handle
= NULL
;
1074 reference_acquired
= lttng_directory_handle_get(
1075 session_output_directory
);
1076 assert(reference_acquired
);
1077 chunk
->session_output_directory
= session_output_directory
;
1078 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
1080 pthread_mutex_unlock(&chunk
->lock
);
1085 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
1086 struct lttng_trace_chunk
*chunk
,
1087 struct lttng_directory_handle
*chunk_directory
)
1089 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1090 bool reference_acquired
;
1092 pthread_mutex_lock(&chunk
->lock
);
1093 if (chunk
->mode
.is_set
) {
1094 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1097 if (!chunk
->credentials
.is_set
) {
1098 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
1099 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1102 reference_acquired
= lttng_directory_handle_get(chunk_directory
);
1103 assert(reference_acquired
);
1104 chunk
->chunk_directory
= chunk_directory
;
1105 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
1107 pthread_mutex_unlock(&chunk
->lock
);
1112 enum lttng_trace_chunk_status
1113 lttng_trace_chunk_get_session_output_directory_handle(
1114 struct lttng_trace_chunk
*chunk
,
1115 struct lttng_directory_handle
**handle
)
1117 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1119 pthread_mutex_lock(&chunk
->lock
);
1120 if (!chunk
->session_output_directory
) {
1121 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1125 const bool reference_acquired
= lttng_directory_handle_get(
1126 chunk
->session_output_directory
);
1128 assert(reference_acquired
);
1129 *handle
= chunk
->session_output_directory
;
1132 pthread_mutex_unlock(&chunk
->lock
);
1137 enum lttng_trace_chunk_status
lttng_trace_chunk_borrow_chunk_directory_handle(
1138 struct lttng_trace_chunk
*chunk
,
1139 const struct lttng_directory_handle
**handle
)
1141 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1143 pthread_mutex_lock(&chunk
->lock
);
1144 if (!chunk
->chunk_directory
) {
1145 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1149 *handle
= chunk
->chunk_directory
;
1151 pthread_mutex_unlock(&chunk
->lock
);
1155 /* Add a top-level directory to the trace chunk if it was previously unknown. */
1157 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
1158 const char *new_path
)
1162 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
1163 &chunk
->top_level_directories
);
1164 const char *new_path_separator_pos
= strchr(new_path
, '/');
1165 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
1166 new_path_separator_pos
- new_path
: strlen(new_path
);
1168 for (i
= 0; i
< count
; i
++) {
1169 const char *path
= lttng_dynamic_pointer_array_get_pointer(
1170 &chunk
->top_level_directories
, i
);
1171 const ptrdiff_t path_top_level_len
= strlen(path
);
1173 if (path_top_level_len
!= new_path_top_level_len
) {
1176 if (!strncmp(path
, new_path
, path_top_level_len
)) {
1183 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
1185 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1186 new_path
, chunk
->name
? : "(unnamed)");
1188 PERROR("Failed to copy path");
1192 ret
= lttng_dynamic_pointer_array_add_pointer(
1193 &chunk
->top_level_directories
, copy
);
1195 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1205 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
1206 struct lttng_trace_chunk
*chunk
,
1210 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1212 DBG("Creating trace chunk subdirectory \"%s\"", path
);
1213 pthread_mutex_lock(&chunk
->lock
);
1214 if (!chunk
->credentials
.is_set
) {
1216 * Fatal error, credentials must be set before a
1217 * directory is created.
1219 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1221 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1224 if (!chunk
->mode
.is_set
||
1225 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
1226 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1228 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1231 if (!chunk
->chunk_directory
) {
1232 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1234 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1238 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1240 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1243 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
1244 chunk
->chunk_directory
, path
,
1246 chunk
->credentials
.value
.use_current_user
?
1247 NULL
: &chunk
->credentials
.value
.user
);
1249 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1251 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1254 ret
= add_top_level_directory_unique(chunk
, path
);
1256 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1260 pthread_mutex_unlock(&chunk
->lock
);
1265 * TODO: Implement O(1) lookup.
1268 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk
*chunk
,
1269 const char *path
, size_t *index
)
1273 count
= lttng_dynamic_pointer_array_get_count(&chunk
->files
);
1274 for (i
= 0; i
< count
; i
++) {
1275 const char *iter_path
=
1276 lttng_dynamic_pointer_array_get_pointer(
1278 if (!strcmp(iter_path
, path
)) {
1289 enum lttng_trace_chunk_status
lttng_trace_chunk_add_file(
1290 struct lttng_trace_chunk
*chunk
,
1295 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1297 if (lttng_trace_chunk_find_file(chunk
, path
, NULL
)) {
1298 return LTTNG_TRACE_CHUNK_STATUS_OK
;
1300 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1301 path
, chunk
->name
? : "(unnamed)");
1302 copy
= strdup(path
);
1304 PERROR("Failed to copy path");
1305 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1308 ret
= lttng_dynamic_pointer_array_add_pointer(
1309 &chunk
->files
, copy
);
1311 ERR("Allocation failure while adding file to a trace chunk");
1313 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1321 void lttng_trace_chunk_remove_file(
1322 struct lttng_trace_chunk
*chunk
,
1329 found
= lttng_trace_chunk_find_file(chunk
, path
, &index
);
1333 ret
= lttng_dynamic_pointer_array_remove_pointer(
1334 &chunk
->files
, index
);
1339 enum lttng_trace_chunk_status
_lttng_trace_chunk_open_fs_handle_locked(
1340 struct lttng_trace_chunk
*chunk
,
1341 const char *file_path
,
1344 struct fs_handle
**out_handle
,
1345 bool expect_no_file
)
1348 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1350 DBG("Opening trace chunk file \"%s\"", file_path
);
1351 if (!chunk
->credentials
.is_set
) {
1353 * Fatal error, credentials must be set before a
1356 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1358 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1361 if (!chunk
->chunk_directory
) {
1362 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1364 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1367 status
= lttng_trace_chunk_add_file(chunk
, file_path
);
1368 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1371 if (chunk
->fd_tracker
) {
1372 assert(chunk
->credentials
.value
.use_current_user
);
1373 *out_handle
= fd_tracker_open_fs_handle(chunk
->fd_tracker
,
1374 chunk
->chunk_directory
, file_path
, flags
, &mode
);
1375 ret
= *out_handle
? 0 : -1;
1377 ret
= lttng_directory_handle_open_file_as_user(
1378 chunk
->chunk_directory
, file_path
, flags
, mode
,
1379 chunk
->credentials
.value
.use_current_user
?
1381 &chunk
->credentials
.value
.user
);
1383 *out_handle
= fs_handle_untracked_create(
1384 chunk
->chunk_directory
, file_path
, ret
);
1386 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1392 if (errno
== ENOENT
&& expect_no_file
) {
1393 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
1395 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1396 file_path
, flags
, (int) mode
);
1397 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1399 lttng_trace_chunk_remove_file(chunk
, file_path
);
1407 enum lttng_trace_chunk_status
lttng_trace_chunk_open_fs_handle(
1408 struct lttng_trace_chunk
*chunk
,
1409 const char *file_path
,
1412 struct fs_handle
**out_handle
,
1413 bool expect_no_file
)
1415 enum lttng_trace_chunk_status status
;
1417 pthread_mutex_lock(&chunk
->lock
);
1418 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1419 flags
, mode
, out_handle
, expect_no_file
);
1420 pthread_mutex_unlock(&chunk
->lock
);
1425 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
1426 struct lttng_trace_chunk
*chunk
,
1427 const char *file_path
,
1431 bool expect_no_file
)
1433 enum lttng_trace_chunk_status status
;
1434 struct fs_handle
*fs_handle
;
1436 pthread_mutex_lock(&chunk
->lock
);
1438 * Using this method is never valid when an fd_tracker is being
1439 * used since the resulting file descriptor would not be tracked.
1441 assert(!chunk
->fd_tracker
);
1442 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1443 flags
, mode
, &fs_handle
, expect_no_file
);
1444 pthread_mutex_unlock(&chunk
->lock
);
1446 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
1447 *out_fd
= fs_handle_get_fd(fs_handle
);
1449 * Does not close the fd; we just "unbox" it from the fs_handle.
1451 fs_handle_untracked_destroy(container_of(
1452 fs_handle
, struct fs_handle_untracked
, parent
));
1459 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
1460 const char *file_path
)
1463 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1465 DBG("Unlinking trace chunk file \"%s\"", file_path
);
1466 pthread_mutex_lock(&chunk
->lock
);
1467 if (!chunk
->credentials
.is_set
) {
1469 * Fatal error, credentials must be set before a
1472 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1474 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1477 if (!chunk
->chunk_directory
) {
1478 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1480 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1483 ret
= lttng_directory_handle_unlink_file_as_user(
1484 chunk
->chunk_directory
, file_path
,
1485 chunk
->credentials
.value
.use_current_user
?
1486 NULL
: &chunk
->credentials
.value
.user
);
1488 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1491 lttng_trace_chunk_remove_file(chunk
, file_path
);
1493 pthread_mutex_unlock(&chunk
->lock
);
1498 int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk
*chunk
,
1502 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1504 DBG("Recursively removing trace chunk directory \"%s\"", path
);
1505 pthread_mutex_lock(&chunk
->lock
);
1506 if (!chunk
->credentials
.is_set
) {
1508 * Fatal error, credentials must be set before a
1509 * directory is removed.
1511 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1513 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1516 if (!chunk
->chunk_directory
) {
1517 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1519 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1522 ret
= lttng_directory_handle_remove_subdirectory_recursive_as_user(
1523 chunk
->chunk_directory
, path
,
1524 chunk
->credentials
.value
.use_current_user
?
1525 NULL
: &chunk
->credentials
.value
.user
,
1526 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
);
1528 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1532 pthread_mutex_unlock(&chunk
->lock
);
1537 int lttng_trace_chunk_move_to_completed_post_release(
1538 struct lttng_trace_chunk
*trace_chunk
)
1541 char *archived_chunk_name
= NULL
;
1542 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
1543 const time_t creation_timestamp
=
1544 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
1545 const time_t close_timestamp
=
1546 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
1547 struct lttng_directory_handle
*archived_chunks_directory
= NULL
;
1548 enum lttng_trace_chunk_status status
;
1550 if (!trace_chunk
->mode
.is_set
||
1551 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
1552 !trace_chunk
->session_output_directory
) {
1554 * This command doesn't need to run if the output is remote
1555 * or if the trace chunk is not owned by this process.
1560 assert(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
1561 assert(!trace_chunk
->name_overridden
);
1562 assert(trace_chunk
->path
);
1564 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
1566 if (!archived_chunk_name
) {
1567 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1572 ret
= lttng_directory_handle_create_subdirectory_as_user(
1573 trace_chunk
->session_output_directory
,
1574 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1576 !trace_chunk
->credentials
.value
.use_current_user
?
1577 &trace_chunk
->credentials
.value
.user
:
1580 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1581 "\" directory for archived trace chunks");
1585 archived_chunks_directory
= trace_chunk
->fd_tracker
?
1586 fd_tracker_create_directory_handle_from_handle(
1587 trace_chunk
->fd_tracker
,
1588 trace_chunk
->session_output_directory
,
1589 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
) :
1590 lttng_directory_handle_create_from_handle(
1591 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1592 trace_chunk
->session_output_directory
);
1593 if (!archived_chunks_directory
) {
1594 PERROR("Failed to get handle to archived trace chunks directory");
1600 * Make sure chunk is renamed to old directory if not already done by
1601 * the creation of the next chunk. This happens if a rotation is
1602 * performed while tracing is stopped.
1604 if (!trace_chunk
->path
|| strcmp(trace_chunk
->path
,
1605 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
)) {
1606 status
= lttng_trace_chunk_rename_path_no_lock(trace_chunk
,
1607 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1608 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1609 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1615 ret
= lttng_directory_handle_rename_as_user(
1616 trace_chunk
->session_output_directory
,
1618 archived_chunks_directory
,
1619 archived_chunk_name
,
1620 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1622 &trace_chunk
->credentials
.value
.user
);
1624 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1626 archived_chunk_name
);
1630 lttng_directory_handle_put(archived_chunks_directory
);
1631 free(archived_chunk_name
);
1636 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
)
1642 int lttng_trace_chunk_delete_post_release_user(
1643 struct lttng_trace_chunk
*trace_chunk
)
1647 DBG("Trace chunk \"delete\" close command post-release (User)");
1649 /* Unlink all files. */
1650 while (lttng_dynamic_pointer_array_get_count(&trace_chunk
->files
) != 0) {
1651 enum lttng_trace_chunk_status status
;
1655 path
= lttng_dynamic_pointer_array_get_pointer(
1656 &trace_chunk
->files
, 0);
1657 DBG("Unlink file: %s", path
);
1658 status
= lttng_trace_chunk_unlink_file(trace_chunk
, path
);
1659 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1660 ERR("Error unlinking file '%s' when deleting chunk", path
);
1670 int lttng_trace_chunk_delete_post_release_owner(
1671 struct lttng_trace_chunk
*trace_chunk
)
1673 enum lttng_trace_chunk_status status
;
1677 ret
= lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1682 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1684 assert(trace_chunk
->session_output_directory
);
1685 assert(trace_chunk
->chunk_directory
);
1687 /* Remove empty directories. */
1688 count
= lttng_dynamic_pointer_array_get_count(
1689 &trace_chunk
->top_level_directories
);
1691 for (i
= 0; i
< count
; i
++) {
1692 const char *top_level_name
=
1693 lttng_dynamic_pointer_array_get_pointer(
1694 &trace_chunk
->top_level_directories
, i
);
1696 status
= lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk
, top_level_name
);
1697 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1698 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1705 lttng_directory_handle_put(trace_chunk
->chunk_directory
);
1706 trace_chunk
->chunk_directory
= NULL
;
1708 if (trace_chunk
->path
&& trace_chunk
->path
[0] != '\0') {
1709 status
= lttng_directory_handle_remove_subdirectory(
1710 trace_chunk
->session_output_directory
,
1712 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1713 ERR("Error removing subdirectory '%s' file when deleting chunk",
1719 free(trace_chunk
->path
);
1720 trace_chunk
->path
= NULL
;
1726 * For local files, session and consumer daemons all run the delete hook. The
1727 * consumer daemons have the list of files to unlink, and technically the
1728 * session daemon is the owner of the chunk. Unlink all files owned by each
1732 int lttng_trace_chunk_delete_post_release(
1733 struct lttng_trace_chunk
*trace_chunk
)
1735 if (!trace_chunk
->chunk_directory
) {
1739 if (trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
) {
1740 return lttng_trace_chunk_delete_post_release_owner(trace_chunk
);
1742 return lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1747 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
1748 struct lttng_trace_chunk
*chunk
,
1749 enum lttng_trace_chunk_command_type
*command_type
)
1751 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1753 pthread_mutex_lock(&chunk
->lock
);
1754 if (chunk
->close_command
.is_set
) {
1755 *command_type
= chunk
->close_command
.value
;
1756 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1758 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1760 pthread_mutex_unlock(&chunk
->lock
);
1765 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
1766 struct lttng_trace_chunk
*chunk
,
1767 enum lttng_trace_chunk_command_type close_command
)
1769 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1771 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1772 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1773 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1777 pthread_mutex_lock(&chunk
->lock
);
1778 if (chunk
->close_command
.is_set
) {
1779 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1780 close_command_names
[chunk
->close_command
.value
],
1781 close_command_names
[close_command
]);
1783 DBG("Setting trace chunk close command to \"%s\"",
1784 close_command_names
[close_command
]);
1787 * Unset close command for no-op for backward compatibility with relayd
1790 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1791 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1793 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1795 pthread_mutex_unlock(&chunk
->lock
);
1801 const char *lttng_trace_chunk_command_type_get_name(
1802 enum lttng_trace_chunk_command_type command
)
1805 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1806 return "move to completed trace chunk folder";
1807 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1808 return "no operation";
1809 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1817 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1819 return urcu_ref_get_unless_zero(&chunk
->ref
);
1823 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1825 struct lttng_trace_chunk_registry_element
*element
=
1826 container_of(node
, typeof(*element
), rcu_node
);
1828 lttng_trace_chunk_fini(&element
->chunk
);
1833 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1835 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1838 if (chunk
->close_command
.is_set
) {
1839 if (close_command_post_release_funcs
[
1840 chunk
->close_command
.value
](chunk
)) {
1841 ERR("Trace chunk post-release command %s has failed.",
1842 close_command_names
[chunk
->close_command
.value
]);
1846 if (chunk
->in_registry_element
) {
1847 struct lttng_trace_chunk_registry_element
*element
;
1849 element
= container_of(chunk
, typeof(*element
), chunk
);
1850 if (element
->registry
) {
1852 cds_lfht_del(element
->registry
->ht
,
1853 &element
->trace_chunk_registry_ht_node
);
1855 call_rcu(&element
->rcu_node
,
1856 free_lttng_trace_chunk_registry_element
);
1858 /* Never published, can be free'd immediately. */
1859 free_lttng_trace_chunk_registry_element(
1860 &element
->rcu_node
);
1863 /* Not RCU-protected, free immediately. */
1864 lttng_trace_chunk_fini(chunk
);
1870 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1875 assert(chunk
->ref
.refcount
);
1876 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1880 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1882 struct lttng_trace_chunk_registry
*registry
;
1884 registry
= zmalloc(sizeof(*registry
));
1889 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1890 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1891 if (!registry
->ht
) {
1897 lttng_trace_chunk_registry_destroy(registry
);
1902 void lttng_trace_chunk_registry_destroy(
1903 struct lttng_trace_chunk_registry
*registry
)
1909 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1916 struct lttng_trace_chunk_registry_element
*
1917 lttng_trace_chunk_registry_element_create_from_chunk(
1918 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1920 struct lttng_trace_chunk_registry_element
*element
=
1921 zmalloc(sizeof(*element
));
1926 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1927 element
->session_id
= session_id
;
1929 element
->chunk
= *chunk
;
1930 lttng_trace_chunk_init(&element
->chunk
);
1931 if (chunk
->session_output_directory
) {
1932 /* Transferred ownership. */
1933 element
->chunk
.session_output_directory
=
1934 chunk
->session_output_directory
;
1935 chunk
->session_output_directory
= NULL
;
1937 if (chunk
->chunk_directory
) {
1938 /* Transferred ownership. */
1939 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1940 chunk
->chunk_directory
= NULL
;
1943 * The original chunk becomes invalid; the name and path attributes are
1944 * transferred to the new chunk instance.
1948 element
->chunk
.fd_tracker
= chunk
->fd_tracker
;
1949 element
->chunk
.in_registry_element
= true;
1955 struct lttng_trace_chunk
*
1956 lttng_trace_chunk_registry_publish_chunk(
1957 struct lttng_trace_chunk_registry
*registry
,
1958 uint64_t session_id
, struct lttng_trace_chunk
*chunk
)
1960 struct lttng_trace_chunk_registry_element
*element
;
1961 unsigned long element_hash
;
1963 pthread_mutex_lock(&chunk
->lock
);
1964 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
1966 pthread_mutex_unlock(&chunk
->lock
);
1971 * chunk is now invalid, the only valid operation is a 'put' from the
1975 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1979 struct cds_lfht_node
*published_node
;
1980 struct lttng_trace_chunk
*published_chunk
;
1981 struct lttng_trace_chunk_registry_element
*published_element
;
1983 published_node
= cds_lfht_add_unique(registry
->ht
,
1985 lttng_trace_chunk_registry_element_match
,
1987 &element
->trace_chunk_registry_ht_node
);
1988 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
1989 /* Successfully published the new element. */
1990 element
->registry
= registry
;
1991 /* Acquire a reference for the caller. */
1992 if (lttng_trace_chunk_get(&element
->chunk
)) {
1996 * Another thread concurrently unpublished the
1997 * trace chunk. This is currently unexpected.
1999 * Re-attempt to publish.
2001 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
2007 * An equivalent trace chunk was published before this trace
2008 * chunk. Attempt to acquire a reference to the one that was
2009 * already published and release the reference to the copy we
2010 * created if successful.
2012 published_element
= container_of(published_node
,
2013 typeof(*published_element
),
2014 trace_chunk_registry_ht_node
);
2015 published_chunk
= &published_element
->chunk
;
2016 if (lttng_trace_chunk_get(published_chunk
)) {
2017 lttng_trace_chunk_put(&element
->chunk
);
2018 element
= published_element
;
2022 * A reference to the previously published trace chunk could not
2023 * be acquired. Hence, retry to publish our copy of the trace
2029 return element
? &element
->chunk
: NULL
;
2033 * Note that the caller must be registered as an RCU thread.
2034 * However, it does not need to hold the RCU read lock. The RCU read lock is
2035 * acquired to perform the look-up in the registry's hash table and held until
2036 * after a reference to the "found" trace chunk is acquired.
2038 * IOW, holding a reference guarantees the existence of the object for the
2042 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
2043 const struct lttng_trace_chunk_registry
*registry
,
2044 uint64_t session_id
, uint64_t *chunk_id
)
2046 const struct lttng_trace_chunk_registry_element target_element
= {
2047 .chunk
.id
.is_set
= !!chunk_id
,
2048 .chunk
.id
.value
= chunk_id
? *chunk_id
: 0,
2049 .session_id
= session_id
,
2051 const unsigned long element_hash
=
2052 lttng_trace_chunk_registry_element_hash(
2054 struct cds_lfht_node
*published_node
;
2055 struct lttng_trace_chunk_registry_element
*published_element
;
2056 struct lttng_trace_chunk
*published_chunk
= NULL
;
2057 struct cds_lfht_iter iter
;
2060 cds_lfht_lookup(registry
->ht
,
2062 lttng_trace_chunk_registry_element_match
,
2065 published_node
= cds_lfht_iter_get_node(&iter
);
2066 if (!published_node
) {
2070 published_element
= container_of(published_node
,
2071 typeof(*published_element
),
2072 trace_chunk_registry_ht_node
);
2073 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
2074 published_chunk
= &published_element
->chunk
;
2078 return published_chunk
;
2082 struct lttng_trace_chunk
*
2083 lttng_trace_chunk_registry_find_chunk(
2084 const struct lttng_trace_chunk_registry
*registry
,
2085 uint64_t session_id
, uint64_t chunk_id
)
2087 return _lttng_trace_chunk_registry_find_chunk(registry
,
2088 session_id
, &chunk_id
);
2092 int lttng_trace_chunk_registry_chunk_exists(
2093 const struct lttng_trace_chunk_registry
*registry
,
2094 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
2097 const struct lttng_trace_chunk_registry_element target_element
= {
2098 .chunk
.id
.is_set
= true,
2099 .chunk
.id
.value
= chunk_id
,
2100 .session_id
= session_id
,
2102 const unsigned long element_hash
=
2103 lttng_trace_chunk_registry_element_hash(
2105 struct cds_lfht_node
*published_node
;
2106 struct cds_lfht_iter iter
;
2109 cds_lfht_lookup(registry
->ht
,
2111 lttng_trace_chunk_registry_element_match
,
2114 published_node
= cds_lfht_iter_get_node(&iter
);
2115 if (!published_node
) {
2116 *chunk_exists
= false;
2120 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
2127 struct lttng_trace_chunk
*
2128 lttng_trace_chunk_registry_find_anonymous_chunk(
2129 const struct lttng_trace_chunk_registry
*registry
,
2130 uint64_t session_id
)
2132 return _lttng_trace_chunk_registry_find_chunk(registry
,
2137 unsigned int lttng_trace_chunk_registry_put_each_chunk(
2138 const struct lttng_trace_chunk_registry
*registry
)
2140 struct cds_lfht_iter iter
;
2141 struct lttng_trace_chunk_registry_element
*chunk_element
;
2142 unsigned int trace_chunks_left
= 0;
2144 DBG("Releasing trace chunk registry to all trace chunks");
2146 cds_lfht_for_each_entry(registry
->ht
,
2147 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
2148 const char *chunk_id_str
= "none";
2149 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
2151 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
2152 if (chunk_element
->chunk
.id
.is_set
) {
2155 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
2157 chunk_element
->chunk
.id
.value
);
2158 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
2159 chunk_id_str
= "formatting error";
2161 chunk_id_str
= chunk_id_buf
;
2165 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
2166 "chunk_id = %s, name = \"%s\", status = %s",
2167 chunk_element
->session_id
,
2169 chunk_element
->chunk
.name
? : "none",
2170 chunk_element
->chunk
.close_command
.is_set
?
2172 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
2173 lttng_trace_chunk_put(&chunk_element
->chunk
);
2174 trace_chunks_left
++;
2177 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
2180 return trace_chunks_left
;