2 * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, version 2.1 only,
6 * as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 #include <common/compat/directory-handle.h>
19 #include <common/credentials.h>
20 #include <common/defaults.h>
21 #include <common/dynamic-array.h>
22 #include <common/error.h>
23 #include <common/fd-tracker/fd-tracker.h>
24 #include <common/hashtable/hashtable.h>
25 #include <common/hashtable/utils.h>
26 #include <common/optional.h>
27 #include <common/string-utils/format.h>
28 #include <common/time.h>
29 #include <common/trace-chunk-registry.h>
30 #include <common/trace-chunk.h>
31 #include <common/utils.h>
32 #include <lttng/constant.h>
38 #include <urcu/rculfhash.h>
42 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
43 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
45 #define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
46 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
48 enum trace_chunk_mode
{
49 TRACE_CHUNK_MODE_USER
,
50 TRACE_CHUNK_MODE_OWNER
,
54 * Callback to invoke on release of a trace chunk. Note that there is no
55 * need to 'lock' the trace chunk during the execution of these callbacks
56 * since only one thread may access a chunk during its destruction (the last
57 * to release its reference to the chunk).
59 typedef int (*chunk_command
)(struct lttng_trace_chunk
*trace_chunk
);
61 /* Move a completed trace chunk to the 'completed' trace archive folder. */
63 int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk
*trace_chunk
);
66 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
);
67 /* Unlink old chunk files. */
69 int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk
*trace_chunk
);
71 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
72 struct lttng_trace_chunk
*chunk
, const char *path
);
74 struct chunk_credentials
{
75 bool use_current_user
;
76 struct lttng_credentials user
;
80 * NOTE: Make sure to update:
81 * - lttng_trace_chunk_copy(),
82 * - lttng_trace_chunk_registry_element_create_from_chunk()
83 * if you modify this structure.
85 struct lttng_trace_chunk
{
88 LTTNG_OPTIONAL(enum trace_chunk_mode
) mode
;
90 * First-level directories created within the trace chunk.
91 * Elements are of type 'char *'.
93 * Only used by _owner_ mode chunks.
95 struct lttng_dynamic_pointer_array top_level_directories
;
97 * All files contained within the trace chunk.
98 * Array of paths (char *).
100 struct lttng_dynamic_pointer_array files
;
101 /* Is contained within an lttng_trace_chunk_registry_element? */
102 bool in_registry_element
;
103 bool name_overridden
;
106 /* An unset id means the chunk is anonymous. */
107 LTTNG_OPTIONAL(uint64_t) id
;
108 LTTNG_OPTIONAL(time_t) timestamp_creation
;
109 LTTNG_OPTIONAL(time_t) timestamp_close
;
110 LTTNG_OPTIONAL(struct chunk_credentials
) credentials
;
111 struct lttng_directory_handle
*session_output_directory
;
112 struct lttng_directory_handle
*chunk_directory
;
113 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type
) close_command
;
115 * fd_tracker instance through which file descriptors should be
118 * An fd_tracker always outlives any trace chunk; there is no
119 * need to perform any reference counting of that object.
121 struct fd_tracker
*fd_tracker
;
124 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
125 struct lttng_trace_chunk_registry_element
{
126 struct lttng_trace_chunk chunk
;
128 /* Weak and only set when added. */
129 struct lttng_trace_chunk_registry
*registry
;
130 struct cds_lfht_node trace_chunk_registry_ht_node
;
131 /* call_rcu delayed reclaim. */
132 struct rcu_head rcu_node
;
135 struct lttng_trace_chunk_registry
{
140 char *close_command_names
[] = {
141 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
142 "move to completed chunk folder",
143 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
145 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
150 chunk_command close_command_post_release_funcs
[] = {
151 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
152 lttng_trace_chunk_move_to_completed_post_release
,
153 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
154 lttng_trace_chunk_no_operation
,
155 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
156 lttng_trace_chunk_delete_post_release
,
160 bool lttng_trace_chunk_registry_element_equals(
161 const struct lttng_trace_chunk_registry_element
*a
,
162 const struct lttng_trace_chunk_registry_element
*b
)
164 if (a
->session_id
!= b
->session_id
) {
167 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
170 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
179 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
,
182 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
184 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
185 element_b
= caa_container_of(node
, typeof(*element_b
),
186 trace_chunk_registry_ht_node
);
187 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
191 unsigned long lttng_trace_chunk_registry_element_hash(
192 const struct lttng_trace_chunk_registry_element
*element
)
194 unsigned long hash
= hash_key_u64(&element
->session_id
,
197 if (element
->chunk
.id
.is_set
) {
198 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
205 char *generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
,
206 const time_t *close_timestamp
)
209 char *new_name
= NULL
;
210 char start_datetime
[ISO8601_STR_LEN
] = {};
211 /* Add 1 for a '-' prefix. */
212 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
214 ret
= time_to_iso8601_str(
216 start_datetime
, sizeof(start_datetime
));
218 ERR("Failed to format trace chunk start date time");
221 if (close_timestamp
) {
222 *end_datetime_suffix
= '-';
223 ret
= time_to_iso8601_str(
225 end_datetime_suffix
+ 1,
226 sizeof(end_datetime_suffix
) - 1);
228 ERR("Failed to format trace chunk end date time");
232 new_name
= zmalloc(GENERATED_CHUNK_NAME_LEN
);
234 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
237 ret
= snprintf(new_name
, GENERATED_CHUNK_NAME_LEN
, "%s%s-%" PRIu64
,
238 start_datetime
, end_datetime_suffix
, chunk_id
);
239 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
240 ERR("Failed to format trace chunk name");
251 void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
253 urcu_ref_init(&chunk
->ref
);
254 pthread_mutex_init(&chunk
->lock
, NULL
);
255 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
256 lttng_dynamic_pointer_array_init(&chunk
->files
, free
);
260 void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
262 if (chunk
->session_output_directory
) {
263 lttng_directory_handle_put(
264 chunk
->session_output_directory
);
265 chunk
->session_output_directory
= NULL
;
267 if (chunk
->chunk_directory
) {
268 lttng_directory_handle_put(chunk
->chunk_directory
);
269 chunk
->chunk_directory
= NULL
;
275 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
276 lttng_dynamic_pointer_array_reset(&chunk
->files
);
277 pthread_mutex_destroy(&chunk
->lock
);
281 struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
283 struct lttng_trace_chunk
*chunk
= NULL
;
285 chunk
= zmalloc(sizeof(*chunk
));
287 ERR("Failed to allocate trace chunk");
290 lttng_trace_chunk_init(chunk
);
296 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
298 DBG("Creating anonymous trace chunk");
299 return lttng_trace_chunk_allocate();
303 struct lttng_trace_chunk
*lttng_trace_chunk_create(
304 uint64_t chunk_id
, time_t chunk_creation_time
, const char *path
)
306 struct lttng_trace_chunk
*chunk
;
307 char chunk_creation_datetime_buf
[16] = {};
308 const char *chunk_creation_datetime_str
= "(formatting error)";
309 struct tm timeinfo_buf
, *timeinfo
;
311 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
315 /* Don't fail because of this; it is only used for logging. */
316 strftime_ret
= strftime(chunk_creation_datetime_buf
,
317 sizeof(chunk_creation_datetime_buf
),
318 "%Y%m%d-%H%M%S", timeinfo
);
320 chunk_creation_datetime_str
=
321 chunk_creation_datetime_buf
;
325 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
326 chunk_id
, chunk_creation_datetime_str
);
327 chunk
= lttng_trace_chunk_allocate();
332 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
333 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
335 chunk
->name
= generate_chunk_name(chunk_id
,
336 chunk_creation_time
, NULL
);
338 ERR("Failed to allocate trace chunk name storage");
343 chunk
->path
= strdup(path
);
349 chunk
->path
= strdup(chunk
->name
);
356 DBG("Chunk name set to \"%s\"", chunk
->name
? : "(none)");
360 lttng_trace_chunk_put(chunk
);
365 void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk
*chunk
,
366 struct fd_tracker
*fd_tracker
)
368 assert(!chunk
->session_output_directory
);
369 assert(!chunk
->chunk_directory
);
370 assert(lttng_dynamic_pointer_array_get_count(&chunk
->files
) == 0);
371 chunk
->fd_tracker
= fd_tracker
;
375 struct lttng_trace_chunk
*lttng_trace_chunk_copy(
376 struct lttng_trace_chunk
*source_chunk
)
378 struct lttng_trace_chunk
*new_chunk
= lttng_trace_chunk_allocate();
384 pthread_mutex_lock(&source_chunk
->lock
);
386 * A new chunk is always a user; it shall create no new trace
389 new_chunk
->mode
= (typeof(new_chunk
->mode
)) {
391 .value
= TRACE_CHUNK_MODE_USER
,
394 * top_level_directories is not copied as it is never used
395 * by _user_ mode chunks.
397 /* The new chunk is not part of a registry (yet, at least). */
398 new_chunk
->in_registry_element
= false;
399 new_chunk
->name_overridden
= source_chunk
->name_overridden
;
400 if (source_chunk
->name
) {
401 new_chunk
->name
= strdup(source_chunk
->name
);
402 if (!new_chunk
->name
) {
403 ERR("Failed to copy source trace chunk name in %s()",
408 if (source_chunk
->path
) {
409 new_chunk
->path
= strdup(source_chunk
->path
);
410 if (!new_chunk
->path
) {
411 ERR("Failed to copy source trace chunk path in %s()",
415 new_chunk
->id
= source_chunk
->id
;
416 new_chunk
->timestamp_creation
= source_chunk
->timestamp_creation
;
417 new_chunk
->timestamp_close
= source_chunk
->timestamp_close
;
418 new_chunk
->credentials
= source_chunk
->credentials
;
419 if (source_chunk
->session_output_directory
) {
420 const bool reference_acquired
= lttng_directory_handle_get(
421 source_chunk
->session_output_directory
);
423 assert(reference_acquired
);
424 new_chunk
->session_output_directory
=
425 source_chunk
->session_output_directory
;
427 if (source_chunk
->chunk_directory
) {
428 const bool reference_acquired
= lttng_directory_handle_get(
429 source_chunk
->chunk_directory
);
431 assert(reference_acquired
);
432 new_chunk
->chunk_directory
= source_chunk
->chunk_directory
;
434 new_chunk
->close_command
= source_chunk
->close_command
;
435 new_chunk
->fd_tracker
= source_chunk
->fd_tracker
;
436 pthread_mutex_unlock(&source_chunk
->lock
);
440 pthread_mutex_unlock(&source_chunk
->lock
);
441 lttng_trace_chunk_put(new_chunk
);
446 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(
447 struct lttng_trace_chunk
*chunk
, uint64_t *id
)
449 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
451 pthread_mutex_lock(&chunk
->lock
);
452 if (chunk
->id
.is_set
) {
453 *id
= chunk
->id
.value
;
455 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
457 pthread_mutex_unlock(&chunk
->lock
);
462 enum lttng_trace_chunk_status
lttng_trace_chunk_get_creation_timestamp(
463 struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
466 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
468 pthread_mutex_lock(&chunk
->lock
);
469 if (chunk
->timestamp_creation
.is_set
) {
470 *creation_ts
= chunk
->timestamp_creation
.value
;
472 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
474 pthread_mutex_unlock(&chunk
->lock
);
479 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_timestamp(
480 struct lttng_trace_chunk
*chunk
, time_t *close_ts
)
482 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
484 pthread_mutex_lock(&chunk
->lock
);
485 if (chunk
->timestamp_close
.is_set
) {
486 *close_ts
= chunk
->timestamp_close
.value
;
488 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
490 pthread_mutex_unlock(&chunk
->lock
);
495 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(
496 struct lttng_trace_chunk
*chunk
, time_t close_ts
)
498 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
500 pthread_mutex_lock(&chunk
->lock
);
501 if (!chunk
->timestamp_creation
.is_set
) {
502 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
503 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
506 if (chunk
->timestamp_creation
.value
> close_ts
) {
507 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
508 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
511 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
512 if (!chunk
->name_overridden
) {
514 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
515 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
518 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
522 pthread_mutex_unlock(&chunk
->lock
);
527 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(
528 struct lttng_trace_chunk
*chunk
, const char **name
,
529 bool *name_overridden
)
531 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
533 pthread_mutex_lock(&chunk
->lock
);
534 if (name_overridden
) {
535 *name_overridden
= chunk
->name_overridden
;
538 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
543 pthread_mutex_unlock(&chunk
->lock
);
548 bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk
*chunk
)
550 bool name_overridden
;
552 pthread_mutex_lock(&chunk
->lock
);
553 name_overridden
= chunk
->name_overridden
;
554 pthread_mutex_unlock(&chunk
->lock
);
555 return name_overridden
;
559 bool is_valid_chunk_name(const char *name
)
567 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
568 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
572 if (strchr(name
, '/') || strchr(name
, '.')) {
580 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
581 struct lttng_trace_chunk
*chunk
, const char *name
)
584 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
585 char *new_name
, *new_path
;
587 DBG("Override trace chunk name from %s to %s", chunk
->name
, name
);
588 if (!is_valid_chunk_name(name
)) {
589 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
591 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
595 pthread_mutex_lock(&chunk
->lock
);
596 if (!chunk
->id
.is_set
) {
597 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
599 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
603 new_name
= strdup(name
);
605 ERR("Failed to allocate new trace chunk name");
606 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
610 chunk
->name
= new_name
;
612 new_path
= strdup(name
);
614 ERR("Failed to allocate new trace chunk path");
615 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
619 chunk
->path
= new_path
;
621 chunk
->name_overridden
= true;
623 pthread_mutex_unlock(&chunk
->lock
);
629 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
630 struct lttng_trace_chunk
*chunk
, const char *path
)
633 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
634 struct lttng_directory_handle
*rename_directory
= NULL
;
635 char *new_path
, *old_path
;
638 if (chunk
->name_overridden
) {
639 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
643 old_path
= chunk
->path
;
644 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path
, path
);
646 if ((!old_path
&& !path
) ||
647 (old_path
&& path
&& !strcmp(old_path
, path
))) {
651 * Use chunk name as path if NULL path is specified.
657 /* Renaming from "" to "" is not accepted. */
658 if (path
[0] == '\0' && old_path
[0] == '\0') {
659 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
664 * If a rename is performed on a chunk for which the chunk_directory
665 * is not set (yet), or the session_output_directory is not set
666 * (interacting with a relay daemon), there is no rename to perform.
668 if (!chunk
->chunk_directory
||
669 !chunk
->session_output_directory
) {
673 if (old_path
[0] != '\0' && path
[0] != '\0') {
674 /* Rename chunk directory. */
675 ret
= lttng_directory_handle_rename_as_user(
676 chunk
->session_output_directory
,
678 chunk
->session_output_directory
,
680 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
682 &chunk
->credentials
.value
.user
);
684 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
686 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
689 rename_directory
= lttng_directory_handle_create_from_handle(
691 chunk
->session_output_directory
);
692 if (!rename_directory
) {
693 ERR("Failed to get handle to trace chunk rename directory");
694 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
698 /* Release old handle. */
699 lttng_directory_handle_put(chunk
->chunk_directory
);
701 * Transfer new handle reference to chunk as the current chunk
704 chunk
->chunk_directory
= rename_directory
;
705 rename_directory
= NULL
;
706 } else if (old_path
[0] == '\0') {
707 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
708 &chunk
->top_level_directories
);
710 ret
= lttng_directory_handle_create_subdirectory_as_user(
711 chunk
->session_output_directory
,
714 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
716 &chunk
->credentials
.value
.user
);
718 PERROR("Failed to create trace chunk rename directory \"%s\"",
720 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
724 rename_directory
= lttng_directory_handle_create_from_handle(
725 path
, chunk
->session_output_directory
);
726 if (!rename_directory
) {
727 ERR("Failed to get handle to trace chunk rename directory");
728 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
732 /* Move toplevel directories. */
733 for (i
= 0; i
< count
; i
++) {
734 const char *top_level_name
=
735 lttng_dynamic_pointer_array_get_pointer(
736 &chunk
->top_level_directories
, i
);
738 ret
= lttng_directory_handle_rename_as_user(
739 chunk
->chunk_directory
,
743 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
745 &chunk
->credentials
.value
.user
);
747 PERROR("Failed to move \"%s\" to trace chunk rename directory",
749 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
753 /* Release old handle. */
754 lttng_directory_handle_put(chunk
->chunk_directory
);
756 * Transfer new handle reference to chunk as the current chunk
759 chunk
->chunk_directory
= rename_directory
;
760 rename_directory
= NULL
;
762 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
763 &chunk
->top_level_directories
);
764 const bool reference_acquired
= lttng_directory_handle_get(
765 chunk
->session_output_directory
);
767 assert(reference_acquired
);
768 rename_directory
= chunk
->session_output_directory
;
770 /* Move toplevel directories. */
771 for (i
= 0; i
< count
; i
++) {
772 const char *top_level_name
=
773 lttng_dynamic_pointer_array_get_pointer(
774 &chunk
->top_level_directories
, i
);
776 ret
= lttng_directory_handle_rename_as_user(
777 chunk
->chunk_directory
,
781 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
783 &chunk
->credentials
.value
.user
);
785 PERROR("Failed to move \"%s\" to trace chunk rename directory",
787 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
791 /* Release old handle. */
792 lttng_directory_handle_put(chunk
->chunk_directory
);
794 * Transfer new handle reference to chunk as the current chunk
797 chunk
->chunk_directory
= rename_directory
;
798 rename_directory
= NULL
;
800 /* Remove old directory. */
801 status
= lttng_directory_handle_remove_subdirectory(
802 chunk
->session_output_directory
,
804 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
805 ERR("Error removing subdirectory '%s' file when deleting chunk",
814 new_path
= strdup(path
);
816 ERR("Failed to allocate new trace chunk path");
817 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
824 chunk
->path
= new_path
;
826 lttng_directory_handle_put(rename_directory
);
831 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path(
832 struct lttng_trace_chunk
*chunk
, const char *path
)
835 enum lttng_trace_chunk_status status
;
837 pthread_mutex_lock(&chunk
->lock
);
838 status
= lttng_trace_chunk_rename_path_no_lock(chunk
, path
);
839 pthread_mutex_unlock(&chunk
->lock
);
845 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
846 struct lttng_trace_chunk
*chunk
,
847 struct lttng_credentials
*credentials
)
849 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
851 pthread_mutex_lock(&chunk
->lock
);
852 if (chunk
->credentials
.is_set
) {
853 if (chunk
->credentials
.value
.use_current_user
) {
854 credentials
->uid
= geteuid();
855 credentials
->gid
= getegid();
857 *credentials
= chunk
->credentials
.value
.user
;
860 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
862 pthread_mutex_unlock(&chunk
->lock
);
867 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
868 struct lttng_trace_chunk
*chunk
,
869 const struct lttng_credentials
*user_credentials
)
871 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
872 const struct chunk_credentials credentials
= {
873 .user
= *user_credentials
,
874 .use_current_user
= false,
877 pthread_mutex_lock(&chunk
->lock
);
878 if (chunk
->credentials
.is_set
) {
879 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
882 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
884 pthread_mutex_unlock(&chunk
->lock
);
889 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
890 struct lttng_trace_chunk
*chunk
)
892 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
893 const struct chunk_credentials credentials
= {
894 .use_current_user
= true,
897 pthread_mutex_lock(&chunk
->lock
);
898 if (chunk
->credentials
.is_set
) {
899 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
902 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
904 pthread_mutex_unlock(&chunk
->lock
);
910 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
911 struct lttng_trace_chunk
*chunk
,
912 struct lttng_directory_handle
*session_output_directory
)
915 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
916 struct lttng_directory_handle
*chunk_directory_handle
= NULL
;
917 bool reference_acquired
;
919 pthread_mutex_lock(&chunk
->lock
);
920 if (chunk
->mode
.is_set
) {
921 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
924 if (!chunk
->credentials
.is_set
) {
926 * Fatal error, credentials must be set before a
927 * directory is created.
929 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
930 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
933 if (chunk
->path
[0] != '\0') {
934 ret
= lttng_directory_handle_create_subdirectory_as_user(
935 session_output_directory
,
938 !chunk
->credentials
.value
.use_current_user
?
939 &chunk
->credentials
.value
.user
: NULL
);
941 PERROR("Failed to create chunk output directory \"%s\"",
943 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
946 chunk_directory_handle
=
947 lttng_directory_handle_create_from_handle(
949 session_output_directory
);
950 if (!chunk_directory_handle
) {
951 /* The function already logs on all error paths. */
952 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
957 * A nameless chunk does not need its own output directory.
958 * The session's output directory will be used.
960 const bool reference_acquired
=
961 lttng_directory_handle_get(
962 session_output_directory
);
964 assert(reference_acquired
);
965 chunk_directory_handle
= session_output_directory
;
967 chunk
->chunk_directory
= chunk_directory_handle
;
968 chunk_directory_handle
= NULL
;
969 reference_acquired
= lttng_directory_handle_get(
970 session_output_directory
);
971 assert(reference_acquired
);
972 chunk
->session_output_directory
= session_output_directory
;
973 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
975 pthread_mutex_unlock(&chunk
->lock
);
980 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
981 struct lttng_trace_chunk
*chunk
,
982 struct lttng_directory_handle
*chunk_directory
)
984 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
985 bool reference_acquired
;
987 pthread_mutex_lock(&chunk
->lock
);
988 if (chunk
->mode
.is_set
) {
989 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
992 if (!chunk
->credentials
.is_set
) {
993 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
994 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
997 reference_acquired
= lttng_directory_handle_get(chunk_directory
);
998 assert(reference_acquired
);
999 chunk
->chunk_directory
= chunk_directory
;
1000 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
1002 pthread_mutex_unlock(&chunk
->lock
);
1007 enum lttng_trace_chunk_status
1008 lttng_trace_chunk_get_session_output_directory_handle(
1009 struct lttng_trace_chunk
*chunk
,
1010 struct lttng_directory_handle
**handle
)
1012 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1014 pthread_mutex_lock(&chunk
->lock
);
1015 if (!chunk
->session_output_directory
) {
1016 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1020 const bool reference_acquired
= lttng_directory_handle_get(
1021 chunk
->session_output_directory
);
1023 assert(reference_acquired
);
1024 *handle
= chunk
->session_output_directory
;
1027 pthread_mutex_unlock(&chunk
->lock
);
1032 enum lttng_trace_chunk_status
lttng_trace_chunk_borrow_chunk_directory_handle(
1033 struct lttng_trace_chunk
*chunk
,
1034 const struct lttng_directory_handle
**handle
)
1036 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1038 pthread_mutex_lock(&chunk
->lock
);
1039 if (!chunk
->chunk_directory
) {
1040 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1044 *handle
= chunk
->chunk_directory
;
1046 pthread_mutex_unlock(&chunk
->lock
);
1050 /* Add a top-level directory to the trace chunk if it was previously unknown. */
1052 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
1053 const char *new_path
)
1057 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
1058 &chunk
->top_level_directories
);
1059 const char *new_path_separator_pos
= strchr(new_path
, '/');
1060 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
1061 new_path_separator_pos
- new_path
: strlen(new_path
);
1063 for (i
= 0; i
< count
; i
++) {
1064 const char *path
= lttng_dynamic_pointer_array_get_pointer(
1065 &chunk
->top_level_directories
, i
);
1066 const ptrdiff_t path_top_level_len
= strlen(path
);
1068 if (path_top_level_len
!= new_path_top_level_len
) {
1071 if (!strncmp(path
, new_path
, path_top_level_len
)) {
1078 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
1080 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1081 new_path
, chunk
->name
? : "(unnamed)");
1083 PERROR("Failed to copy path");
1087 ret
= lttng_dynamic_pointer_array_add_pointer(
1088 &chunk
->top_level_directories
, copy
);
1090 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1100 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
1101 struct lttng_trace_chunk
*chunk
,
1105 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1107 DBG("Creating trace chunk subdirectory \"%s\"", path
);
1108 pthread_mutex_lock(&chunk
->lock
);
1109 if (!chunk
->credentials
.is_set
) {
1111 * Fatal error, credentials must be set before a
1112 * directory is created.
1114 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1116 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1119 if (!chunk
->mode
.is_set
||
1120 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
1121 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1123 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1126 if (!chunk
->chunk_directory
) {
1127 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1129 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1133 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1135 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1138 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
1139 chunk
->chunk_directory
, path
,
1141 chunk
->credentials
.value
.use_current_user
?
1142 NULL
: &chunk
->credentials
.value
.user
);
1144 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1146 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1149 ret
= add_top_level_directory_unique(chunk
, path
);
1151 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1155 pthread_mutex_unlock(&chunk
->lock
);
1160 * TODO: Implement O(1) lookup.
1163 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk
*chunk
,
1164 const char *path
, size_t *index
)
1168 count
= lttng_dynamic_pointer_array_get_count(&chunk
->files
);
1169 for (i
= 0; i
< count
; i
++) {
1170 const char *iter_path
=
1171 lttng_dynamic_pointer_array_get_pointer(
1173 if (!strcmp(iter_path
, path
)) {
1184 enum lttng_trace_chunk_status
lttng_trace_chunk_add_file(
1185 struct lttng_trace_chunk
*chunk
,
1190 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1192 if (lttng_trace_chunk_find_file(chunk
, path
, NULL
)) {
1193 return LTTNG_TRACE_CHUNK_STATUS_OK
;
1195 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1196 path
, chunk
->name
? : "(unnamed)");
1197 copy
= strdup(path
);
1199 PERROR("Failed to copy path");
1200 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1203 ret
= lttng_dynamic_pointer_array_add_pointer(
1204 &chunk
->files
, copy
);
1206 ERR("Allocation failure while adding file to a trace chunk");
1208 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1216 void lttng_trace_chunk_remove_file(
1217 struct lttng_trace_chunk
*chunk
,
1224 found
= lttng_trace_chunk_find_file(chunk
, path
, &index
);
1228 ret
= lttng_dynamic_pointer_array_remove_pointer(
1229 &chunk
->files
, index
);
1234 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
1235 struct lttng_trace_chunk
*chunk
, const char *file_path
,
1236 int flags
, mode_t mode
, int *out_fd
, bool expect_no_file
)
1239 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1241 DBG("Opening trace chunk file \"%s\"", file_path
);
1242 pthread_mutex_lock(&chunk
->lock
);
1243 if (!chunk
->credentials
.is_set
) {
1245 * Fatal error, credentials must be set before a
1248 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1250 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1253 if (!chunk
->chunk_directory
) {
1254 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1256 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1259 status
= lttng_trace_chunk_add_file(chunk
, file_path
);
1260 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1263 ret
= lttng_directory_handle_open_file_as_user(
1264 chunk
->chunk_directory
, file_path
, flags
, mode
,
1265 chunk
->credentials
.value
.use_current_user
?
1266 NULL
: &chunk
->credentials
.value
.user
);
1268 if (errno
== ENOENT
&& expect_no_file
) {
1269 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
1271 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1272 file_path
, flags
, (int) mode
);
1273 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1275 lttng_trace_chunk_remove_file(chunk
, file_path
);
1280 pthread_mutex_unlock(&chunk
->lock
);
1285 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
1286 const char *file_path
)
1289 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1291 DBG("Unlinking trace chunk file \"%s\"", file_path
);
1292 pthread_mutex_lock(&chunk
->lock
);
1293 if (!chunk
->credentials
.is_set
) {
1295 * Fatal error, credentials must be set before a
1298 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1300 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1303 if (!chunk
->chunk_directory
) {
1304 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1306 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1309 ret
= lttng_directory_handle_unlink_file_as_user(
1310 chunk
->chunk_directory
, file_path
,
1311 chunk
->credentials
.value
.use_current_user
?
1312 NULL
: &chunk
->credentials
.value
.user
);
1314 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1317 lttng_trace_chunk_remove_file(chunk
, file_path
);
1319 pthread_mutex_unlock(&chunk
->lock
);
1324 int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk
*chunk
,
1328 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1330 DBG("Recursively removing trace chunk directory \"%s\"", path
);
1331 pthread_mutex_lock(&chunk
->lock
);
1332 if (!chunk
->credentials
.is_set
) {
1334 * Fatal error, credentials must be set before a
1335 * directory is removed.
1337 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1339 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1342 if (!chunk
->chunk_directory
) {
1343 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1345 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1348 ret
= lttng_directory_handle_remove_subdirectory_recursive_as_user(
1349 chunk
->chunk_directory
, path
,
1350 chunk
->credentials
.value
.use_current_user
?
1351 NULL
: &chunk
->credentials
.value
.user
,
1352 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
);
1354 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1358 pthread_mutex_unlock(&chunk
->lock
);
1363 int lttng_trace_chunk_move_to_completed_post_release(
1364 struct lttng_trace_chunk
*trace_chunk
)
1367 char *archived_chunk_name
= NULL
;
1368 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
1369 const time_t creation_timestamp
=
1370 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
1371 const time_t close_timestamp
=
1372 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
1373 struct lttng_directory_handle
*archived_chunks_directory
= NULL
;
1374 enum lttng_trace_chunk_status status
;
1376 if (!trace_chunk
->mode
.is_set
||
1377 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
1378 !trace_chunk
->session_output_directory
) {
1380 * This command doesn't need to run if the output is remote
1381 * or if the trace chunk is not owned by this process.
1386 assert(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
1387 assert(!trace_chunk
->name_overridden
);
1388 assert(trace_chunk
->path
);
1390 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
1392 if (!archived_chunk_name
) {
1393 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1398 ret
= lttng_directory_handle_create_subdirectory_as_user(
1399 trace_chunk
->session_output_directory
,
1400 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1402 !trace_chunk
->credentials
.value
.use_current_user
?
1403 &trace_chunk
->credentials
.value
.user
:
1406 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1407 "\" directory for archived trace chunks");
1411 archived_chunks_directory
= lttng_directory_handle_create_from_handle(
1412 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1413 trace_chunk
->session_output_directory
);
1414 if (!archived_chunks_directory
) {
1415 PERROR("Failed to get handle to archived trace chunks directory");
1421 * Make sure chunk is renamed to old directory if not already done by
1422 * the creation of the next chunk. This happens if a rotation is
1423 * performed while tracing is stopped.
1425 if (!trace_chunk
->path
|| strcmp(trace_chunk
->path
,
1426 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
)) {
1427 status
= lttng_trace_chunk_rename_path_no_lock(trace_chunk
,
1428 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1429 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1430 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1436 ret
= lttng_directory_handle_rename_as_user(
1437 trace_chunk
->session_output_directory
,
1439 archived_chunks_directory
,
1440 archived_chunk_name
,
1441 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1443 &trace_chunk
->credentials
.value
.user
);
1445 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1447 archived_chunk_name
);
1451 lttng_directory_handle_put(archived_chunks_directory
);
1452 free(archived_chunk_name
);
1457 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
)
1463 int lttng_trace_chunk_delete_post_release_user(
1464 struct lttng_trace_chunk
*trace_chunk
)
1468 DBG("Trace chunk \"delete\" close command post-release (User)");
1470 /* Unlink all files. */
1471 while (lttng_dynamic_pointer_array_get_count(&trace_chunk
->files
) != 0) {
1472 enum lttng_trace_chunk_status status
;
1476 path
= lttng_dynamic_pointer_array_get_pointer(
1477 &trace_chunk
->files
, 0);
1478 DBG("Unlink file: %s", path
);
1479 status
= lttng_trace_chunk_unlink_file(trace_chunk
, path
);
1480 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1481 ERR("Error unlinking file '%s' when deleting chunk", path
);
1491 int lttng_trace_chunk_delete_post_release_owner(
1492 struct lttng_trace_chunk
*trace_chunk
)
1494 enum lttng_trace_chunk_status status
;
1498 ret
= lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1503 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1505 assert(trace_chunk
->session_output_directory
);
1506 assert(trace_chunk
->chunk_directory
);
1508 /* Remove empty directories. */
1509 count
= lttng_dynamic_pointer_array_get_count(
1510 &trace_chunk
->top_level_directories
);
1512 for (i
= 0; i
< count
; i
++) {
1513 const char *top_level_name
=
1514 lttng_dynamic_pointer_array_get_pointer(
1515 &trace_chunk
->top_level_directories
, i
);
1517 status
= lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk
, top_level_name
);
1518 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1519 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1526 lttng_directory_handle_put(trace_chunk
->chunk_directory
);
1527 trace_chunk
->chunk_directory
= NULL
;
1529 if (trace_chunk
->path
&& trace_chunk
->path
[0] != '\0') {
1530 status
= lttng_directory_handle_remove_subdirectory(
1531 trace_chunk
->session_output_directory
,
1533 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1534 ERR("Error removing subdirectory '%s' file when deleting chunk",
1540 free(trace_chunk
->path
);
1541 trace_chunk
->path
= NULL
;
1547 * For local files, session and consumer daemons all run the delete hook. The
1548 * consumer daemons have the list of files to unlink, and technically the
1549 * session daemon is the owner of the chunk. Unlink all files owned by each
1553 int lttng_trace_chunk_delete_post_release(
1554 struct lttng_trace_chunk
*trace_chunk
)
1556 if (!trace_chunk
->chunk_directory
) {
1560 if (trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
) {
1561 return lttng_trace_chunk_delete_post_release_owner(trace_chunk
);
1563 return lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1568 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
1569 struct lttng_trace_chunk
*chunk
,
1570 enum lttng_trace_chunk_command_type
*command_type
)
1572 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1574 pthread_mutex_lock(&chunk
->lock
);
1575 if (chunk
->close_command
.is_set
) {
1576 *command_type
= chunk
->close_command
.value
;
1577 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1579 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1581 pthread_mutex_unlock(&chunk
->lock
);
1586 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
1587 struct lttng_trace_chunk
*chunk
,
1588 enum lttng_trace_chunk_command_type close_command
)
1590 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1592 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1593 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1594 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1598 pthread_mutex_lock(&chunk
->lock
);
1599 if (chunk
->close_command
.is_set
) {
1600 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1601 close_command_names
[chunk
->close_command
.value
],
1602 close_command_names
[close_command
]);
1604 DBG("Setting trace chunk close command to \"%s\"",
1605 close_command_names
[close_command
]);
1608 * Unset close command for no-op for backward compatibility with relayd
1611 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1612 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1614 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1616 pthread_mutex_unlock(&chunk
->lock
);
1622 const char *lttng_trace_chunk_command_type_get_name(
1623 enum lttng_trace_chunk_command_type command
)
1626 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1627 return "move to completed trace chunk folder";
1628 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1629 return "no operation";
1630 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1638 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1640 return urcu_ref_get_unless_zero(&chunk
->ref
);
1644 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1646 struct lttng_trace_chunk_registry_element
*element
=
1647 container_of(node
, typeof(*element
), rcu_node
);
1649 lttng_trace_chunk_fini(&element
->chunk
);
1654 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1656 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1659 if (chunk
->close_command
.is_set
) {
1660 if (close_command_post_release_funcs
[
1661 chunk
->close_command
.value
](chunk
)) {
1662 ERR("Trace chunk post-release command %s has failed.",
1663 close_command_names
[chunk
->close_command
.value
]);
1667 if (chunk
->in_registry_element
) {
1668 struct lttng_trace_chunk_registry_element
*element
;
1670 element
= container_of(chunk
, typeof(*element
), chunk
);
1671 if (element
->registry
) {
1673 cds_lfht_del(element
->registry
->ht
,
1674 &element
->trace_chunk_registry_ht_node
);
1676 call_rcu(&element
->rcu_node
,
1677 free_lttng_trace_chunk_registry_element
);
1679 /* Never published, can be free'd immediately. */
1680 free_lttng_trace_chunk_registry_element(
1681 &element
->rcu_node
);
1684 /* Not RCU-protected, free immediately. */
1685 lttng_trace_chunk_fini(chunk
);
1691 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1696 assert(chunk
->ref
.refcount
);
1697 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1701 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1703 struct lttng_trace_chunk_registry
*registry
;
1705 registry
= zmalloc(sizeof(*registry
));
1710 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1711 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1712 if (!registry
->ht
) {
1718 lttng_trace_chunk_registry_destroy(registry
);
1723 void lttng_trace_chunk_registry_destroy(
1724 struct lttng_trace_chunk_registry
*registry
)
1730 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1737 struct lttng_trace_chunk_registry_element
*
1738 lttng_trace_chunk_registry_element_create_from_chunk(
1739 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1741 struct lttng_trace_chunk_registry_element
*element
=
1742 zmalloc(sizeof(*element
));
1747 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1748 element
->session_id
= session_id
;
1750 element
->chunk
= *chunk
;
1751 lttng_trace_chunk_init(&element
->chunk
);
1752 if (chunk
->session_output_directory
) {
1753 /* Transferred ownership. */
1754 element
->chunk
.session_output_directory
=
1755 chunk
->session_output_directory
;
1756 chunk
->session_output_directory
= NULL
;
1758 if (chunk
->chunk_directory
) {
1759 /* Transferred ownership. */
1760 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1761 chunk
->chunk_directory
= NULL
;
1764 * The original chunk becomes invalid; the name and path attributes are
1765 * transferred to the new chunk instance.
1769 element
->chunk
.fd_tracker
= chunk
->fd_tracker
;
1770 element
->chunk
.in_registry_element
= true;
1776 struct lttng_trace_chunk
*
1777 lttng_trace_chunk_registry_publish_chunk(
1778 struct lttng_trace_chunk_registry
*registry
,
1779 uint64_t session_id
, struct lttng_trace_chunk
*chunk
)
1781 struct lttng_trace_chunk_registry_element
*element
;
1782 unsigned long element_hash
;
1784 pthread_mutex_lock(&chunk
->lock
);
1785 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
1787 pthread_mutex_unlock(&chunk
->lock
);
1792 * chunk is now invalid, the only valid operation is a 'put' from the
1796 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1800 struct cds_lfht_node
*published_node
;
1801 struct lttng_trace_chunk
*published_chunk
;
1802 struct lttng_trace_chunk_registry_element
*published_element
;
1804 published_node
= cds_lfht_add_unique(registry
->ht
,
1806 lttng_trace_chunk_registry_element_match
,
1808 &element
->trace_chunk_registry_ht_node
);
1809 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
1810 /* Successfully published the new element. */
1811 element
->registry
= registry
;
1812 /* Acquire a reference for the caller. */
1813 if (lttng_trace_chunk_get(&element
->chunk
)) {
1817 * Another thread concurrently unpublished the
1818 * trace chunk. This is currently unexpected.
1820 * Re-attempt to publish.
1822 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1828 * An equivalent trace chunk was published before this trace
1829 * chunk. Attempt to acquire a reference to the one that was
1830 * already published and release the reference to the copy we
1831 * created if successful.
1833 published_element
= container_of(published_node
,
1834 typeof(*published_element
),
1835 trace_chunk_registry_ht_node
);
1836 published_chunk
= &published_element
->chunk
;
1837 if (lttng_trace_chunk_get(published_chunk
)) {
1838 lttng_trace_chunk_put(&element
->chunk
);
1839 element
= published_element
;
1843 * A reference to the previously published trace chunk could not
1844 * be acquired. Hence, retry to publish our copy of the trace
1850 return element
? &element
->chunk
: NULL
;
1854 * Note that the caller must be registered as an RCU thread.
1855 * However, it does not need to hold the RCU read lock. The RCU read lock is
1856 * acquired to perform the look-up in the registry's hash table and held until
1857 * after a reference to the "found" trace chunk is acquired.
1859 * IOW, holding a reference guarantees the existence of the object for the
1863 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
1864 const struct lttng_trace_chunk_registry
*registry
,
1865 uint64_t session_id
, uint64_t *chunk_id
)
1867 const struct lttng_trace_chunk_registry_element target_element
= {
1868 .chunk
.id
.is_set
= !!chunk_id
,
1869 .chunk
.id
.value
= chunk_id
? *chunk_id
: 0,
1870 .session_id
= session_id
,
1872 const unsigned long element_hash
=
1873 lttng_trace_chunk_registry_element_hash(
1875 struct cds_lfht_node
*published_node
;
1876 struct lttng_trace_chunk_registry_element
*published_element
;
1877 struct lttng_trace_chunk
*published_chunk
= NULL
;
1878 struct cds_lfht_iter iter
;
1881 cds_lfht_lookup(registry
->ht
,
1883 lttng_trace_chunk_registry_element_match
,
1886 published_node
= cds_lfht_iter_get_node(&iter
);
1887 if (!published_node
) {
1891 published_element
= container_of(published_node
,
1892 typeof(*published_element
),
1893 trace_chunk_registry_ht_node
);
1894 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
1895 published_chunk
= &published_element
->chunk
;
1899 return published_chunk
;
1903 struct lttng_trace_chunk
*
1904 lttng_trace_chunk_registry_find_chunk(
1905 const struct lttng_trace_chunk_registry
*registry
,
1906 uint64_t session_id
, uint64_t chunk_id
)
1908 return _lttng_trace_chunk_registry_find_chunk(registry
,
1909 session_id
, &chunk_id
);
1913 int lttng_trace_chunk_registry_chunk_exists(
1914 const struct lttng_trace_chunk_registry
*registry
,
1915 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
1918 const struct lttng_trace_chunk_registry_element target_element
= {
1919 .chunk
.id
.is_set
= true,
1920 .chunk
.id
.value
= chunk_id
,
1921 .session_id
= session_id
,
1923 const unsigned long element_hash
=
1924 lttng_trace_chunk_registry_element_hash(
1926 struct cds_lfht_node
*published_node
;
1927 struct cds_lfht_iter iter
;
1930 cds_lfht_lookup(registry
->ht
,
1932 lttng_trace_chunk_registry_element_match
,
1935 published_node
= cds_lfht_iter_get_node(&iter
);
1936 if (!published_node
) {
1937 *chunk_exists
= false;
1941 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
1948 struct lttng_trace_chunk
*
1949 lttng_trace_chunk_registry_find_anonymous_chunk(
1950 const struct lttng_trace_chunk_registry
*registry
,
1951 uint64_t session_id
)
1953 return _lttng_trace_chunk_registry_find_chunk(registry
,
1958 unsigned int lttng_trace_chunk_registry_put_each_chunk(
1959 const struct lttng_trace_chunk_registry
*registry
)
1961 struct cds_lfht_iter iter
;
1962 struct lttng_trace_chunk_registry_element
*chunk_element
;
1963 unsigned int trace_chunks_left
= 0;
1965 DBG("Releasing trace chunk registry to all trace chunks");
1967 cds_lfht_for_each_entry(registry
->ht
,
1968 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
1969 const char *chunk_id_str
= "none";
1970 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
1972 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
1973 if (chunk_element
->chunk
.id
.is_set
) {
1976 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
1978 chunk_element
->chunk
.id
.value
);
1979 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
1980 chunk_id_str
= "formatting error";
1982 chunk_id_str
= chunk_id_buf
;
1986 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
1987 "chunk_id = %s, name = \"%s\", status = %s",
1988 chunk_element
->session_id
,
1990 chunk_element
->chunk
.name
? : "none",
1991 chunk_element
->chunk
.close_command
.is_set
?
1993 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
1994 lttng_trace_chunk_put(&chunk_element
->chunk
);
1995 trace_chunks_left
++;
1998 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
2001 return trace_chunks_left
;