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 <lttng/constant.h>
19 #include <common/string-utils/format.h>
20 #include <common/trace-chunk.h>
21 #include <common/trace-chunk-registry.h>
22 #include <common/hashtable/utils.h>
23 #include <common/hashtable/hashtable.h>
24 #include <common/error.h>
25 #include <common/utils.h>
26 #include <common/time.h>
27 #include <common/optional.h>
28 #include <common/compat/directory-handle.h>
29 #include <common/credentials.h>
30 #include <common/defaults.h>
31 #include <common/dynamic-array.h>
34 #include <urcu/rculfhash.h>
41 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
42 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
44 #define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
45 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
47 enum trace_chunk_mode
{
48 TRACE_CHUNK_MODE_USER
,
49 TRACE_CHUNK_MODE_OWNER
,
53 * Callback to invoke on release of a trace chunk. Note that there is no
54 * need to 'lock' the trace chunk during the execution of these callbacks
55 * since only one thread may access a chunk during its destruction (the last
56 * to release its reference to the chunk).
58 typedef int (*chunk_command
)(struct lttng_trace_chunk
*trace_chunk
);
60 /* Move a completed trace chunk to the 'completed' trace archive folder. */
62 int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk
*trace_chunk
);
64 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
65 struct lttng_trace_chunk
*chunk
, const char *path
);
67 struct chunk_credentials
{
68 bool use_current_user
;
69 struct lttng_credentials user
;
73 * NOTE: Make sure to update:
74 * - lttng_trace_chunk_copy(),
75 * - lttng_trace_chunk_registry_element_create_from_chunk()
76 * if you modify this structure.
78 struct lttng_trace_chunk
{
81 LTTNG_OPTIONAL(enum trace_chunk_mode
) mode
;
83 * First-level directories created within the trace chunk.
84 * Elements are of type 'char *'.
86 * Only used by _owner_ mode chunks.
88 struct lttng_dynamic_pointer_array top_level_directories
;
90 * All files contained within the trace chunk.
91 * Array of paths (char *).
93 struct lttng_dynamic_pointer_array files
;
94 /* Is contained within an lttng_trace_chunk_registry_element? */
95 bool in_registry_element
;
99 /* An unset id means the chunk is anonymous. */
100 LTTNG_OPTIONAL(uint64_t) id
;
101 LTTNG_OPTIONAL(time_t) timestamp_creation
;
102 LTTNG_OPTIONAL(time_t) timestamp_close
;
103 LTTNG_OPTIONAL(struct chunk_credentials
) credentials
;
104 struct lttng_directory_handle
*session_output_directory
;
105 struct lttng_directory_handle
*chunk_directory
;
106 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type
) close_command
;
109 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
110 struct lttng_trace_chunk_registry_element
{
111 struct lttng_trace_chunk chunk
;
113 /* Weak and only set when added. */
114 struct lttng_trace_chunk_registry
*registry
;
115 struct cds_lfht_node trace_chunk_registry_ht_node
;
116 /* call_rcu delayed reclaim. */
117 struct rcu_head rcu_node
;
120 struct lttng_trace_chunk_registry
{
125 char *close_command_names
[] = {
126 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
127 "move to completed chunk folder",
128 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
130 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
135 chunk_command close_command_post_release_funcs
[] = {
136 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
137 lttng_trace_chunk_move_to_completed_post_release
,
141 bool lttng_trace_chunk_registry_element_equals(
142 const struct lttng_trace_chunk_registry_element
*a
,
143 const struct lttng_trace_chunk_registry_element
*b
)
145 if (a
->session_id
!= b
->session_id
) {
148 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
151 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
160 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
,
163 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
165 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
166 element_b
= caa_container_of(node
, typeof(*element_b
),
167 trace_chunk_registry_ht_node
);
168 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
172 unsigned long lttng_trace_chunk_registry_element_hash(
173 const struct lttng_trace_chunk_registry_element
*element
)
175 unsigned long hash
= hash_key_u64(&element
->session_id
,
178 if (element
->chunk
.id
.is_set
) {
179 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
186 char *generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
,
187 const time_t *close_timestamp
)
190 char *new_name
= NULL
;
191 char start_datetime
[ISO8601_STR_LEN
] = {};
192 /* Add 1 for a '-' prefix. */
193 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
195 ret
= time_to_iso8601_str(
197 start_datetime
, sizeof(start_datetime
));
199 ERR("Failed to format trace chunk start date time");
202 if (close_timestamp
) {
203 *end_datetime_suffix
= '-';
204 ret
= time_to_iso8601_str(
206 end_datetime_suffix
+ 1,
207 sizeof(end_datetime_suffix
) - 1);
209 ERR("Failed to format trace chunk end date time");
213 new_name
= zmalloc(GENERATED_CHUNK_NAME_LEN
);
215 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
218 ret
= snprintf(new_name
, GENERATED_CHUNK_NAME_LEN
, "%s%s-%" PRIu64
,
219 start_datetime
, end_datetime_suffix
, chunk_id
);
220 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
221 ERR("Failed to format trace chunk name");
232 void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
234 urcu_ref_init(&chunk
->ref
);
235 pthread_mutex_init(&chunk
->lock
, NULL
);
236 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
237 lttng_dynamic_pointer_array_init(&chunk
->files
, free
);
241 void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
243 if (chunk
->session_output_directory
) {
244 lttng_directory_handle_put(
245 chunk
->session_output_directory
);
246 chunk
->session_output_directory
= NULL
;
248 if (chunk
->chunk_directory
) {
249 lttng_directory_handle_put(chunk
->chunk_directory
);
250 chunk
->chunk_directory
= NULL
;
256 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
257 lttng_dynamic_pointer_array_reset(&chunk
->files
);
258 pthread_mutex_destroy(&chunk
->lock
);
262 struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
264 struct lttng_trace_chunk
*chunk
= NULL
;
266 chunk
= zmalloc(sizeof(*chunk
));
268 ERR("Failed to allocate trace chunk");
271 lttng_trace_chunk_init(chunk
);
277 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
279 DBG("Creating anonymous trace chunk");
280 return lttng_trace_chunk_allocate();
284 struct lttng_trace_chunk
*lttng_trace_chunk_create(
285 uint64_t chunk_id
, time_t chunk_creation_time
, const char *path
)
287 struct lttng_trace_chunk
*chunk
;
288 char chunk_creation_datetime_buf
[16] = {};
289 const char *chunk_creation_datetime_str
= "(formatting error)";
290 struct tm timeinfo_buf
, *timeinfo
;
292 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
296 /* Don't fail because of this; it is only used for logging. */
297 strftime_ret
= strftime(chunk_creation_datetime_buf
,
298 sizeof(chunk_creation_datetime_buf
),
299 "%Y%m%d-%H%M%S", timeinfo
);
301 chunk_creation_datetime_str
=
302 chunk_creation_datetime_buf
;
306 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
307 chunk_id
, chunk_creation_datetime_str
);
308 chunk
= lttng_trace_chunk_allocate();
313 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
314 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
316 chunk
->name
= generate_chunk_name(chunk_id
,
317 chunk_creation_time
, NULL
);
319 ERR("Failed to allocate trace chunk name storage");
324 chunk
->path
= strdup(path
);
330 chunk
->path
= strdup(chunk
->name
);
337 DBG("Chunk name set to \"%s\"", chunk
->name
? : "(none)");
341 lttng_trace_chunk_put(chunk
);
346 struct lttng_trace_chunk
*lttng_trace_chunk_copy(
347 struct lttng_trace_chunk
*source_chunk
)
349 struct lttng_trace_chunk
*new_chunk
= lttng_trace_chunk_allocate();
355 pthread_mutex_lock(&source_chunk
->lock
);
357 * A new chunk is always a user; it shall create no new trace
360 new_chunk
->mode
= (typeof(new_chunk
->mode
)) {
362 .value
= TRACE_CHUNK_MODE_USER
,
365 * top_level_directories is not copied as it is never used
366 * by _user_ mode chunks.
368 /* The new chunk is not part of a registry (yet, at least). */
369 new_chunk
->in_registry_element
= false;
370 new_chunk
->name_overridden
= source_chunk
->name_overridden
;
371 if (source_chunk
->name
) {
372 new_chunk
->name
= strdup(source_chunk
->name
);
373 if (!new_chunk
->name
) {
374 ERR("Failed to copy source trace chunk name in %s()",
379 if (source_chunk
->path
) {
380 new_chunk
->path
= strdup(source_chunk
->path
);
381 if (!new_chunk
->path
) {
382 ERR("Failed to copy source trace chunk path in %s()",
386 new_chunk
->id
= source_chunk
->id
;
387 new_chunk
->timestamp_creation
= source_chunk
->timestamp_creation
;
388 new_chunk
->timestamp_close
= source_chunk
->timestamp_close
;
389 new_chunk
->credentials
= source_chunk
->credentials
;
390 if (source_chunk
->session_output_directory
) {
391 const bool reference_acquired
= lttng_directory_handle_get(
392 source_chunk
->session_output_directory
);
394 assert(reference_acquired
);
395 new_chunk
->session_output_directory
=
396 source_chunk
->session_output_directory
;
398 if (source_chunk
->chunk_directory
) {
399 const bool reference_acquired
= lttng_directory_handle_get(
400 source_chunk
->chunk_directory
);
402 assert(reference_acquired
);
403 new_chunk
->chunk_directory
= source_chunk
->chunk_directory
;
405 new_chunk
->close_command
= source_chunk
->close_command
;
406 pthread_mutex_unlock(&source_chunk
->lock
);
410 pthread_mutex_unlock(&source_chunk
->lock
);
411 lttng_trace_chunk_put(new_chunk
);
416 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(
417 struct lttng_trace_chunk
*chunk
, uint64_t *id
)
419 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
421 pthread_mutex_lock(&chunk
->lock
);
422 if (chunk
->id
.is_set
) {
423 *id
= chunk
->id
.value
;
425 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
427 pthread_mutex_unlock(&chunk
->lock
);
432 enum lttng_trace_chunk_status
lttng_trace_chunk_get_creation_timestamp(
433 struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
436 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
438 pthread_mutex_lock(&chunk
->lock
);
439 if (chunk
->timestamp_creation
.is_set
) {
440 *creation_ts
= chunk
->timestamp_creation
.value
;
442 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
444 pthread_mutex_unlock(&chunk
->lock
);
449 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_timestamp(
450 struct lttng_trace_chunk
*chunk
, time_t *close_ts
)
452 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
454 pthread_mutex_lock(&chunk
->lock
);
455 if (chunk
->timestamp_close
.is_set
) {
456 *close_ts
= chunk
->timestamp_close
.value
;
458 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
460 pthread_mutex_unlock(&chunk
->lock
);
465 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(
466 struct lttng_trace_chunk
*chunk
, time_t close_ts
)
468 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
470 pthread_mutex_lock(&chunk
->lock
);
471 if (!chunk
->timestamp_creation
.is_set
) {
472 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
473 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
476 if (chunk
->timestamp_creation
.value
> close_ts
) {
477 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
478 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
481 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
482 if (!chunk
->name_overridden
) {
484 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
485 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
488 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
492 pthread_mutex_unlock(&chunk
->lock
);
497 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(
498 struct lttng_trace_chunk
*chunk
, const char **name
,
499 bool *name_overridden
)
501 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
503 pthread_mutex_lock(&chunk
->lock
);
504 if (name_overridden
) {
505 *name_overridden
= chunk
->name_overridden
;
508 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
513 pthread_mutex_unlock(&chunk
->lock
);
518 bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk
*chunk
)
520 bool name_overridden
;
522 pthread_mutex_lock(&chunk
->lock
);
523 name_overridden
= chunk
->name_overridden
;
524 pthread_mutex_unlock(&chunk
->lock
);
525 return name_overridden
;
529 bool is_valid_chunk_name(const char *name
)
537 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
538 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
542 if (strchr(name
, '/') || strchr(name
, '.')) {
550 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
551 struct lttng_trace_chunk
*chunk
, const char *name
)
554 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
555 char *new_name
, *new_path
;
557 DBG("Override trace chunk name from %s to %s", chunk
->name
, name
);
558 if (!is_valid_chunk_name(name
)) {
559 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
561 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
565 pthread_mutex_lock(&chunk
->lock
);
566 if (!chunk
->id
.is_set
) {
567 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
569 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
573 new_name
= strdup(name
);
575 ERR("Failed to allocate new trace chunk name");
576 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
580 chunk
->name
= new_name
;
582 new_path
= strdup(name
);
584 ERR("Failed to allocate new trace chunk path");
585 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
589 chunk
->path
= new_path
;
591 chunk
->name_overridden
= true;
593 pthread_mutex_unlock(&chunk
->lock
);
599 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
600 struct lttng_trace_chunk
*chunk
, const char *path
)
603 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
604 struct lttng_directory_handle
*rename_directory
= NULL
;
605 char *new_path
, *old_path
;
608 if (chunk
->name_overridden
) {
609 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
613 old_path
= chunk
->path
;
614 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path
, path
);
616 if ((!old_path
&& !path
) ||
617 (old_path
&& path
&& !strcmp(old_path
, path
))) {
621 * Use chunk name as path if NULL path is specified.
627 /* Renaming from "" to "" is not accepted. */
628 if (path
[0] == '\0' && old_path
[0] == '\0') {
629 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
634 * If a rename is performed on a chunk for which the chunk_directory
635 * is not set (yet), or the session_output_directory is not set
636 * (interacting with a relay daemon), there is no rename to perform.
638 if (!chunk
->chunk_directory
||
639 !chunk
->session_output_directory
) {
643 if (old_path
[0] != '\0' && path
[0] != '\0') {
644 /* Rename chunk directory. */
645 ret
= lttng_directory_handle_rename_as_user(
646 chunk
->session_output_directory
,
648 chunk
->session_output_directory
,
650 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
652 &chunk
->credentials
.value
.user
);
654 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
656 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
659 rename_directory
= lttng_directory_handle_create_from_handle(
661 chunk
->session_output_directory
);
662 if (!rename_directory
) {
663 ERR("Failed to get handle to trace chunk rename directory");
664 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
668 /* Release old handle. */
669 lttng_directory_handle_put(chunk
->chunk_directory
);
671 * Transfer new handle reference to chunk as the current chunk
674 chunk
->chunk_directory
= rename_directory
;
675 rename_directory
= NULL
;
676 } else if (old_path
[0] == '\0') {
677 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
678 &chunk
->top_level_directories
);
680 ret
= lttng_directory_handle_create_subdirectory_as_user(
681 chunk
->session_output_directory
,
684 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
686 &chunk
->credentials
.value
.user
);
688 PERROR("Failed to create trace chunk rename directory \"%s\"",
690 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
694 rename_directory
= lttng_directory_handle_create_from_handle(
695 path
, chunk
->session_output_directory
);
696 if (!rename_directory
) {
697 ERR("Failed to get handle to trace chunk rename directory");
698 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
702 /* Move toplevel directories. */
703 for (i
= 0; i
< count
; i
++) {
704 const char *top_level_name
=
705 lttng_dynamic_pointer_array_get_pointer(
706 &chunk
->top_level_directories
, i
);
708 ret
= lttng_directory_handle_rename_as_user(
709 chunk
->chunk_directory
,
713 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
715 &chunk
->credentials
.value
.user
);
717 PERROR("Failed to move \"%s\" to trace chunk rename directory",
719 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
723 /* Release old handle. */
724 lttng_directory_handle_put(chunk
->chunk_directory
);
726 * Transfer new handle reference to chunk as the current chunk
729 chunk
->chunk_directory
= rename_directory
;
730 rename_directory
= NULL
;
732 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
733 &chunk
->top_level_directories
);
734 const bool reference_acquired
= lttng_directory_handle_get(
735 chunk
->session_output_directory
);
737 assert(reference_acquired
);
738 rename_directory
= chunk
->session_output_directory
;
740 /* Move toplevel directories. */
741 for (i
= 0; i
< count
; i
++) {
742 const char *top_level_name
=
743 lttng_dynamic_pointer_array_get_pointer(
744 &chunk
->top_level_directories
, i
);
746 ret
= lttng_directory_handle_rename_as_user(
747 chunk
->chunk_directory
,
751 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
753 &chunk
->credentials
.value
.user
);
755 PERROR("Failed to move \"%s\" to trace chunk rename directory",
757 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
761 /* Release old handle. */
762 lttng_directory_handle_put(chunk
->chunk_directory
);
764 * Transfer new handle reference to chunk as the current chunk
767 chunk
->chunk_directory
= rename_directory
;
768 rename_directory
= NULL
;
770 /* Remove old directory. */
771 status
= lttng_directory_handle_remove_subdirectory(
772 chunk
->session_output_directory
,
774 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
775 ERR("Error removing subdirectory '%s' file when deleting chunk",
784 new_path
= strdup(path
);
786 ERR("Failed to allocate new trace chunk path");
787 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
794 chunk
->path
= new_path
;
796 lttng_directory_handle_put(rename_directory
);
801 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path(
802 struct lttng_trace_chunk
*chunk
, const char *path
)
805 enum lttng_trace_chunk_status status
;
807 pthread_mutex_lock(&chunk
->lock
);
808 status
= lttng_trace_chunk_rename_path_no_lock(chunk
, path
);
809 pthread_mutex_unlock(&chunk
->lock
);
815 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
816 struct lttng_trace_chunk
*chunk
,
817 struct lttng_credentials
*credentials
)
819 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
821 pthread_mutex_lock(&chunk
->lock
);
822 if (chunk
->credentials
.is_set
) {
823 if (chunk
->credentials
.value
.use_current_user
) {
824 credentials
->uid
= geteuid();
825 credentials
->gid
= getegid();
827 *credentials
= chunk
->credentials
.value
.user
;
830 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
832 pthread_mutex_unlock(&chunk
->lock
);
837 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
838 struct lttng_trace_chunk
*chunk
,
839 const struct lttng_credentials
*user_credentials
)
841 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
842 const struct chunk_credentials credentials
= {
843 .user
= *user_credentials
,
844 .use_current_user
= false,
847 pthread_mutex_lock(&chunk
->lock
);
848 if (chunk
->credentials
.is_set
) {
849 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
852 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
854 pthread_mutex_unlock(&chunk
->lock
);
859 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
860 struct lttng_trace_chunk
*chunk
)
862 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
863 const struct chunk_credentials credentials
= {
864 .use_current_user
= true,
867 pthread_mutex_lock(&chunk
->lock
);
868 if (chunk
->credentials
.is_set
) {
869 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
872 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
874 pthread_mutex_unlock(&chunk
->lock
);
880 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
881 struct lttng_trace_chunk
*chunk
,
882 struct lttng_directory_handle
*session_output_directory
)
885 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
886 struct lttng_directory_handle
*chunk_directory_handle
= NULL
;
887 bool reference_acquired
;
889 pthread_mutex_lock(&chunk
->lock
);
890 if (chunk
->mode
.is_set
) {
891 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
894 if (!chunk
->credentials
.is_set
) {
896 * Fatal error, credentials must be set before a
897 * directory is created.
899 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
900 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
903 if (chunk
->path
[0] != '\0') {
904 ret
= lttng_directory_handle_create_subdirectory_as_user(
905 session_output_directory
,
908 !chunk
->credentials
.value
.use_current_user
?
909 &chunk
->credentials
.value
.user
: NULL
);
911 PERROR("Failed to create chunk output directory \"%s\"",
913 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
916 chunk_directory_handle
=
917 lttng_directory_handle_create_from_handle(
919 session_output_directory
);
920 if (!chunk_directory_handle
) {
921 /* The function already logs on all error paths. */
922 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
927 * A nameless chunk does not need its own output directory.
928 * The session's output directory will be used.
930 const bool reference_acquired
=
931 lttng_directory_handle_get(
932 session_output_directory
);
934 assert(reference_acquired
);
935 chunk_directory_handle
= session_output_directory
;
937 chunk
->chunk_directory
= chunk_directory_handle
;
938 chunk_directory_handle
= NULL
;
939 reference_acquired
= lttng_directory_handle_get(
940 session_output_directory
);
941 assert(reference_acquired
);
942 chunk
->session_output_directory
= session_output_directory
;
943 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
945 pthread_mutex_unlock(&chunk
->lock
);
950 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
951 struct lttng_trace_chunk
*chunk
,
952 struct lttng_directory_handle
*chunk_directory
)
954 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
955 bool reference_acquired
;
957 pthread_mutex_lock(&chunk
->lock
);
958 if (chunk
->mode
.is_set
) {
959 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
962 if (!chunk
->credentials
.is_set
) {
963 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
964 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
967 reference_acquired
= lttng_directory_handle_get(chunk_directory
);
968 assert(reference_acquired
);
969 chunk
->chunk_directory
= chunk_directory
;
970 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
972 pthread_mutex_unlock(&chunk
->lock
);
977 enum lttng_trace_chunk_status
lttng_trace_chunk_borrow_chunk_directory_handle(
978 struct lttng_trace_chunk
*chunk
,
979 const struct lttng_directory_handle
**handle
)
981 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
983 pthread_mutex_lock(&chunk
->lock
);
984 if (!chunk
->chunk_directory
) {
985 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
989 *handle
= chunk
->chunk_directory
;
991 pthread_mutex_unlock(&chunk
->lock
);
995 /* Add a top-level directory to the trace chunk if it was previously unknown. */
997 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
998 const char *new_path
)
1002 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
1003 &chunk
->top_level_directories
);
1004 const char *new_path_separator_pos
= strchr(new_path
, '/');
1005 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
1006 new_path_separator_pos
- new_path
: strlen(new_path
);
1008 for (i
= 0; i
< count
; i
++) {
1009 const char *path
= lttng_dynamic_pointer_array_get_pointer(
1010 &chunk
->top_level_directories
, i
);
1011 const ptrdiff_t path_top_level_len
= strlen(path
);
1013 if (path_top_level_len
!= new_path_top_level_len
) {
1016 if (!strncmp(path
, new_path
, path_top_level_len
)) {
1023 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
1025 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1026 new_path
, chunk
->name
? : "(unnamed)");
1028 PERROR("Failed to copy path");
1032 ret
= lttng_dynamic_pointer_array_add_pointer(
1033 &chunk
->top_level_directories
, copy
);
1035 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1045 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
1046 struct lttng_trace_chunk
*chunk
,
1050 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1052 DBG("Creating trace chunk subdirectory \"%s\"", path
);
1053 pthread_mutex_lock(&chunk
->lock
);
1054 if (!chunk
->credentials
.is_set
) {
1056 * Fatal error, credentials must be set before a
1057 * directory is created.
1059 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1061 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1064 if (!chunk
->mode
.is_set
||
1065 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
1066 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1068 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1071 if (!chunk
->chunk_directory
) {
1072 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1074 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1078 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1080 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1083 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
1084 chunk
->chunk_directory
, path
,
1086 chunk
->credentials
.value
.use_current_user
?
1087 NULL
: &chunk
->credentials
.value
.user
);
1089 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1091 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1094 ret
= add_top_level_directory_unique(chunk
, path
);
1096 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1100 pthread_mutex_unlock(&chunk
->lock
);
1105 * TODO: Implement O(1) lookup.
1108 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk
*chunk
,
1109 const char *path
, size_t *index
)
1113 count
= lttng_dynamic_pointer_array_get_count(&chunk
->files
);
1114 for (i
= 0; i
< count
; i
++) {
1115 const char *iter_path
=
1116 lttng_dynamic_pointer_array_get_pointer(
1118 if (!strcmp(iter_path
, path
)) {
1129 enum lttng_trace_chunk_status
lttng_trace_chunk_add_file(
1130 struct lttng_trace_chunk
*chunk
,
1135 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1137 if (lttng_trace_chunk_find_file(chunk
, path
, NULL
)) {
1138 return LTTNG_TRACE_CHUNK_STATUS_OK
;
1140 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1141 path
, chunk
->name
? : "(unnamed)");
1142 copy
= strdup(path
);
1144 PERROR("Failed to copy path");
1145 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1148 ret
= lttng_dynamic_pointer_array_add_pointer(
1149 &chunk
->files
, copy
);
1151 ERR("Allocation failure while adding file to a trace chunk");
1153 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1161 void lttng_trace_chunk_remove_file(
1162 struct lttng_trace_chunk
*chunk
,
1169 found
= lttng_trace_chunk_find_file(chunk
, path
, &index
);
1173 ret
= lttng_dynamic_pointer_array_remove_pointer(
1174 &chunk
->files
, index
);
1179 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
1180 struct lttng_trace_chunk
*chunk
, const char *file_path
,
1181 int flags
, mode_t mode
, int *out_fd
, bool expect_no_file
)
1184 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1186 DBG("Opening trace chunk file \"%s\"", file_path
);
1187 pthread_mutex_lock(&chunk
->lock
);
1188 if (!chunk
->credentials
.is_set
) {
1190 * Fatal error, credentials must be set before a
1193 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1195 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1198 if (!chunk
->chunk_directory
) {
1199 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1201 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1204 status
= lttng_trace_chunk_add_file(chunk
, file_path
);
1205 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1208 ret
= lttng_directory_handle_open_file_as_user(
1209 chunk
->chunk_directory
, file_path
, flags
, mode
,
1210 chunk
->credentials
.value
.use_current_user
?
1211 NULL
: &chunk
->credentials
.value
.user
);
1213 if (errno
== ENOENT
&& expect_no_file
) {
1214 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
1216 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1217 file_path
, flags
, (int) mode
);
1218 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1220 lttng_trace_chunk_remove_file(chunk
, file_path
);
1225 pthread_mutex_unlock(&chunk
->lock
);
1230 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
1231 const char *file_path
)
1234 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1236 DBG("Unlinking trace chunk file \"%s\"", file_path
);
1237 pthread_mutex_lock(&chunk
->lock
);
1238 if (!chunk
->credentials
.is_set
) {
1240 * Fatal error, credentials must be set before a
1243 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1245 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1248 if (!chunk
->chunk_directory
) {
1249 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1251 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1254 ret
= lttng_directory_handle_unlink_file_as_user(
1255 chunk
->chunk_directory
, file_path
,
1256 chunk
->credentials
.value
.use_current_user
?
1257 NULL
: &chunk
->credentials
.value
.user
);
1259 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1262 lttng_trace_chunk_remove_file(chunk
, file_path
);
1264 pthread_mutex_unlock(&chunk
->lock
);
1269 int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk
*chunk
,
1273 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1275 DBG("Recursively removing trace chunk directory \"%s\"", path
);
1276 pthread_mutex_lock(&chunk
->lock
);
1277 if (!chunk
->credentials
.is_set
) {
1279 * Fatal error, credentials must be set before a
1280 * directory is removed.
1282 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1284 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1287 if (!chunk
->chunk_directory
) {
1288 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1290 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1293 ret
= lttng_directory_handle_remove_subdirectory_recursive_as_user(
1294 chunk
->chunk_directory
, path
,
1295 chunk
->credentials
.value
.use_current_user
?
1296 NULL
: &chunk
->credentials
.value
.user
,
1297 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
);
1299 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1303 pthread_mutex_unlock(&chunk
->lock
);
1308 int lttng_trace_chunk_move_to_completed_post_release(
1309 struct lttng_trace_chunk
*trace_chunk
)
1312 char *archived_chunk_name
= NULL
;
1313 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
1314 const time_t creation_timestamp
=
1315 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
1316 const time_t close_timestamp
=
1317 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
1318 struct lttng_directory_handle
*archived_chunks_directory
= NULL
;
1319 enum lttng_trace_chunk_status status
;
1321 if (!trace_chunk
->mode
.is_set
||
1322 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
1323 !trace_chunk
->session_output_directory
) {
1325 * This command doesn't need to run if the output is remote
1326 * or if the trace chunk is not owned by this process.
1331 assert(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
1332 assert(!trace_chunk
->name_overridden
);
1333 assert(trace_chunk
->path
);
1335 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
1337 if (!archived_chunk_name
) {
1338 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1343 ret
= lttng_directory_handle_create_subdirectory_as_user(
1344 trace_chunk
->session_output_directory
,
1345 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1347 !trace_chunk
->credentials
.value
.use_current_user
?
1348 &trace_chunk
->credentials
.value
.user
:
1351 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1352 "\" directory for archived trace chunks");
1356 archived_chunks_directory
= lttng_directory_handle_create_from_handle(
1357 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1358 trace_chunk
->session_output_directory
);
1359 if (!archived_chunks_directory
) {
1360 PERROR("Failed to get handle to archived trace chunks directory");
1366 * Make sure chunk is renamed to old directory if not already done by
1367 * the creation of the next chunk. This happens if a rotation is
1368 * performed while tracing is stopped.
1370 if (!trace_chunk
->path
|| strcmp(trace_chunk
->path
,
1371 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
)) {
1372 status
= lttng_trace_chunk_rename_path_no_lock(trace_chunk
,
1373 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1374 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1375 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1381 ret
= lttng_directory_handle_rename_as_user(
1382 trace_chunk
->session_output_directory
,
1384 archived_chunks_directory
,
1385 archived_chunk_name
,
1386 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1388 &trace_chunk
->credentials
.value
.user
);
1390 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1392 archived_chunk_name
);
1396 lttng_directory_handle_put(archived_chunks_directory
);
1397 free(archived_chunk_name
);
1402 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
1403 struct lttng_trace_chunk
*chunk
,
1404 enum lttng_trace_chunk_command_type
*command_type
)
1406 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1408 pthread_mutex_lock(&chunk
->lock
);
1409 if (chunk
->close_command
.is_set
) {
1410 *command_type
= chunk
->close_command
.value
;
1411 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1413 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1415 pthread_mutex_unlock(&chunk
->lock
);
1420 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
1421 struct lttng_trace_chunk
*chunk
,
1422 enum lttng_trace_chunk_command_type close_command
)
1424 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1426 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1427 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1428 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1432 pthread_mutex_lock(&chunk
->lock
);
1433 if (chunk
->close_command
.is_set
) {
1434 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1435 close_command_names
[chunk
->close_command
.value
],
1436 close_command_names
[close_command
]);
1438 DBG("Setting trace chunk close command to \"%s\"",
1439 close_command_names
[close_command
]);
1442 * Unset close command for no-op for backward compatibility with relayd
1445 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1446 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1448 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1450 pthread_mutex_unlock(&chunk
->lock
);
1456 const char *lttng_trace_chunk_command_type_get_name(
1457 enum lttng_trace_chunk_command_type command
)
1460 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1461 return "move to completed trace chunk folder";
1462 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1463 return "no operation";
1464 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1472 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1474 return urcu_ref_get_unless_zero(&chunk
->ref
);
1478 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1480 struct lttng_trace_chunk_registry_element
*element
=
1481 container_of(node
, typeof(*element
), rcu_node
);
1483 lttng_trace_chunk_fini(&element
->chunk
);
1488 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1490 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1493 if (chunk
->close_command
.is_set
) {
1494 if (close_command_post_release_funcs
[
1495 chunk
->close_command
.value
](chunk
)) {
1496 ERR("Trace chunk post-release command %s has failed.",
1497 close_command_names
[chunk
->close_command
.value
]);
1501 if (chunk
->in_registry_element
) {
1502 struct lttng_trace_chunk_registry_element
*element
;
1504 element
= container_of(chunk
, typeof(*element
), chunk
);
1505 if (element
->registry
) {
1507 cds_lfht_del(element
->registry
->ht
,
1508 &element
->trace_chunk_registry_ht_node
);
1510 call_rcu(&element
->rcu_node
,
1511 free_lttng_trace_chunk_registry_element
);
1513 /* Never published, can be free'd immediately. */
1514 free_lttng_trace_chunk_registry_element(
1515 &element
->rcu_node
);
1518 /* Not RCU-protected, free immediately. */
1519 lttng_trace_chunk_fini(chunk
);
1525 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1530 assert(chunk
->ref
.refcount
);
1531 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1535 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1537 struct lttng_trace_chunk_registry
*registry
;
1539 registry
= zmalloc(sizeof(*registry
));
1544 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1545 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1546 if (!registry
->ht
) {
1552 lttng_trace_chunk_registry_destroy(registry
);
1557 void lttng_trace_chunk_registry_destroy(
1558 struct lttng_trace_chunk_registry
*registry
)
1564 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1571 struct lttng_trace_chunk_registry_element
*
1572 lttng_trace_chunk_registry_element_create_from_chunk(
1573 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1575 struct lttng_trace_chunk_registry_element
*element
=
1576 zmalloc(sizeof(*element
));
1581 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1582 element
->session_id
= session_id
;
1584 element
->chunk
= *chunk
;
1585 lttng_trace_chunk_init(&element
->chunk
);
1586 if (chunk
->session_output_directory
) {
1587 /* Transferred ownership. */
1588 element
->chunk
.session_output_directory
=
1589 chunk
->session_output_directory
;
1590 chunk
->session_output_directory
= NULL
;
1592 if (chunk
->chunk_directory
) {
1593 /* Transferred ownership. */
1594 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1595 chunk
->chunk_directory
= NULL
;
1598 * The original chunk becomes invalid; the name and path attributes are
1599 * transferred to the new chunk instance.
1603 element
->chunk
.in_registry_element
= true;
1609 struct lttng_trace_chunk
*
1610 lttng_trace_chunk_registry_publish_chunk(
1611 struct lttng_trace_chunk_registry
*registry
,
1612 uint64_t session_id
, struct lttng_trace_chunk
*chunk
)
1614 struct lttng_trace_chunk_registry_element
*element
;
1615 unsigned long element_hash
;
1617 pthread_mutex_lock(&chunk
->lock
);
1618 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
1620 pthread_mutex_unlock(&chunk
->lock
);
1625 * chunk is now invalid, the only valid operation is a 'put' from the
1629 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1633 struct cds_lfht_node
*published_node
;
1634 struct lttng_trace_chunk
*published_chunk
;
1635 struct lttng_trace_chunk_registry_element
*published_element
;
1637 published_node
= cds_lfht_add_unique(registry
->ht
,
1639 lttng_trace_chunk_registry_element_match
,
1641 &element
->trace_chunk_registry_ht_node
);
1642 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
1643 /* Successfully published the new element. */
1644 element
->registry
= registry
;
1645 /* Acquire a reference for the caller. */
1646 if (lttng_trace_chunk_get(&element
->chunk
)) {
1650 * Another thread concurrently unpublished the
1651 * trace chunk. This is currently unexpected.
1653 * Re-attempt to publish.
1655 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1661 * An equivalent trace chunk was published before this trace
1662 * chunk. Attempt to acquire a reference to the one that was
1663 * already published and release the reference to the copy we
1664 * created if successful.
1666 published_element
= container_of(published_node
,
1667 typeof(*published_element
),
1668 trace_chunk_registry_ht_node
);
1669 published_chunk
= &published_element
->chunk
;
1670 if (lttng_trace_chunk_get(published_chunk
)) {
1671 lttng_trace_chunk_put(&element
->chunk
);
1672 element
= published_element
;
1676 * A reference to the previously published trace chunk could not
1677 * be acquired. Hence, retry to publish our copy of the trace
1683 return element
? &element
->chunk
: NULL
;
1687 * Note that the caller must be registered as an RCU thread.
1688 * However, it does not need to hold the RCU read lock. The RCU read lock is
1689 * acquired to perform the look-up in the registry's hash table and held until
1690 * after a reference to the "found" trace chunk is acquired.
1692 * IOW, holding a reference guarantees the existence of the object for the
1696 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
1697 const struct lttng_trace_chunk_registry
*registry
,
1698 uint64_t session_id
, uint64_t *chunk_id
)
1700 const struct lttng_trace_chunk_registry_element target_element
= {
1701 .chunk
.id
.is_set
= !!chunk_id
,
1702 .chunk
.id
.value
= chunk_id
? *chunk_id
: 0,
1703 .session_id
= session_id
,
1705 const unsigned long element_hash
=
1706 lttng_trace_chunk_registry_element_hash(
1708 struct cds_lfht_node
*published_node
;
1709 struct lttng_trace_chunk_registry_element
*published_element
;
1710 struct lttng_trace_chunk
*published_chunk
= NULL
;
1711 struct cds_lfht_iter iter
;
1714 cds_lfht_lookup(registry
->ht
,
1716 lttng_trace_chunk_registry_element_match
,
1719 published_node
= cds_lfht_iter_get_node(&iter
);
1720 if (!published_node
) {
1724 published_element
= container_of(published_node
,
1725 typeof(*published_element
),
1726 trace_chunk_registry_ht_node
);
1727 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
1728 published_chunk
= &published_element
->chunk
;
1732 return published_chunk
;
1736 struct lttng_trace_chunk
*
1737 lttng_trace_chunk_registry_find_chunk(
1738 const struct lttng_trace_chunk_registry
*registry
,
1739 uint64_t session_id
, uint64_t chunk_id
)
1741 return _lttng_trace_chunk_registry_find_chunk(registry
,
1742 session_id
, &chunk_id
);
1746 int lttng_trace_chunk_registry_chunk_exists(
1747 const struct lttng_trace_chunk_registry
*registry
,
1748 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
1751 const struct lttng_trace_chunk_registry_element target_element
= {
1752 .chunk
.id
.is_set
= true,
1753 .chunk
.id
.value
= chunk_id
,
1754 .session_id
= session_id
,
1756 const unsigned long element_hash
=
1757 lttng_trace_chunk_registry_element_hash(
1759 struct cds_lfht_node
*published_node
;
1760 struct cds_lfht_iter iter
;
1763 cds_lfht_lookup(registry
->ht
,
1765 lttng_trace_chunk_registry_element_match
,
1768 published_node
= cds_lfht_iter_get_node(&iter
);
1769 if (!published_node
) {
1770 *chunk_exists
= false;
1774 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
1781 struct lttng_trace_chunk
*
1782 lttng_trace_chunk_registry_find_anonymous_chunk(
1783 const struct lttng_trace_chunk_registry
*registry
,
1784 uint64_t session_id
)
1786 return _lttng_trace_chunk_registry_find_chunk(registry
,
1790 unsigned int lttng_trace_chunk_registry_put_each_chunk(
1791 struct lttng_trace_chunk_registry
*registry
)
1793 struct cds_lfht_iter iter
;
1794 struct lttng_trace_chunk_registry_element
*chunk_element
;
1795 unsigned int trace_chunks_left
= 0;
1797 DBG("Releasing trace chunk registry to all trace chunks");
1799 cds_lfht_for_each_entry(registry
->ht
,
1800 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
1801 const char *chunk_id_str
= "none";
1802 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
1804 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
1805 if (chunk_element
->chunk
.id
.is_set
) {
1808 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
1810 chunk_element
->chunk
.id
.value
);
1811 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
1812 chunk_id_str
= "formatting error";
1814 chunk_id_str
= chunk_id_buf
;
1818 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
1819 "chunk_id = %s, name = \"%s\", status = %s",
1820 chunk_element
->session_id
,
1822 chunk_element
->chunk
.name
? : "none",
1823 chunk_element
->chunk
.close_command
.is_set
?
1825 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
1826 lttng_trace_chunk_put(&chunk_element
->chunk
);
1827 trace_chunks_left
++;
1830 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
1833 return trace_chunks_left
;