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
);
65 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
);
66 /* Unlink old chunk files. */
68 int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk
*trace_chunk
);
70 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
71 struct lttng_trace_chunk
*chunk
, const char *path
);
73 struct chunk_credentials
{
74 bool use_current_user
;
75 struct lttng_credentials user
;
79 * NOTE: Make sure to update:
80 * - lttng_trace_chunk_copy(),
81 * - lttng_trace_chunk_registry_element_create_from_chunk()
82 * if you modify this structure.
84 struct lttng_trace_chunk
{
87 LTTNG_OPTIONAL(enum trace_chunk_mode
) mode
;
89 * First-level directories created within the trace chunk.
90 * Elements are of type 'char *'.
92 * Only used by _owner_ mode chunks.
94 struct lttng_dynamic_pointer_array top_level_directories
;
96 * All files contained within the trace chunk.
97 * Array of paths (char *).
99 struct lttng_dynamic_pointer_array files
;
100 /* Is contained within an lttng_trace_chunk_registry_element? */
101 bool in_registry_element
;
102 bool name_overridden
;
105 /* An unset id means the chunk is anonymous. */
106 LTTNG_OPTIONAL(uint64_t) id
;
107 LTTNG_OPTIONAL(time_t) timestamp_creation
;
108 LTTNG_OPTIONAL(time_t) timestamp_close
;
109 LTTNG_OPTIONAL(struct chunk_credentials
) credentials
;
110 struct lttng_directory_handle
*session_output_directory
;
111 struct lttng_directory_handle
*chunk_directory
;
112 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type
) close_command
;
115 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
116 struct lttng_trace_chunk_registry_element
{
117 struct lttng_trace_chunk chunk
;
119 /* Weak and only set when added. */
120 struct lttng_trace_chunk_registry
*registry
;
121 struct cds_lfht_node trace_chunk_registry_ht_node
;
122 /* call_rcu delayed reclaim. */
123 struct rcu_head rcu_node
;
126 struct lttng_trace_chunk_registry
{
131 char *close_command_names
[] = {
132 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
133 "move to completed chunk folder",
134 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
136 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
141 chunk_command close_command_post_release_funcs
[] = {
142 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
143 lttng_trace_chunk_move_to_completed_post_release
,
144 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
145 lttng_trace_chunk_no_operation
,
146 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
147 lttng_trace_chunk_delete_post_release
,
151 bool lttng_trace_chunk_registry_element_equals(
152 const struct lttng_trace_chunk_registry_element
*a
,
153 const struct lttng_trace_chunk_registry_element
*b
)
155 if (a
->session_id
!= b
->session_id
) {
158 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
161 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
170 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
,
173 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
175 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
176 element_b
= caa_container_of(node
, typeof(*element_b
),
177 trace_chunk_registry_ht_node
);
178 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
182 unsigned long lttng_trace_chunk_registry_element_hash(
183 const struct lttng_trace_chunk_registry_element
*element
)
185 unsigned long hash
= hash_key_u64(&element
->session_id
,
188 if (element
->chunk
.id
.is_set
) {
189 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
196 char *generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
,
197 const time_t *close_timestamp
)
200 char *new_name
= NULL
;
201 char start_datetime
[ISO8601_STR_LEN
] = {};
202 /* Add 1 for a '-' prefix. */
203 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
205 ret
= time_to_iso8601_str(
207 start_datetime
, sizeof(start_datetime
));
209 ERR("Failed to format trace chunk start date time");
212 if (close_timestamp
) {
213 *end_datetime_suffix
= '-';
214 ret
= time_to_iso8601_str(
216 end_datetime_suffix
+ 1,
217 sizeof(end_datetime_suffix
) - 1);
219 ERR("Failed to format trace chunk end date time");
223 new_name
= zmalloc(GENERATED_CHUNK_NAME_LEN
);
225 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
228 ret
= snprintf(new_name
, GENERATED_CHUNK_NAME_LEN
, "%s%s-%" PRIu64
,
229 start_datetime
, end_datetime_suffix
, chunk_id
);
230 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
231 ERR("Failed to format trace chunk name");
242 void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
244 urcu_ref_init(&chunk
->ref
);
245 pthread_mutex_init(&chunk
->lock
, NULL
);
246 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
247 lttng_dynamic_pointer_array_init(&chunk
->files
, free
);
251 void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
253 if (chunk
->session_output_directory
) {
254 lttng_directory_handle_put(
255 chunk
->session_output_directory
);
256 chunk
->session_output_directory
= NULL
;
258 if (chunk
->chunk_directory
) {
259 lttng_directory_handle_put(chunk
->chunk_directory
);
260 chunk
->chunk_directory
= NULL
;
266 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
267 lttng_dynamic_pointer_array_reset(&chunk
->files
);
268 pthread_mutex_destroy(&chunk
->lock
);
272 struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
274 struct lttng_trace_chunk
*chunk
= NULL
;
276 chunk
= zmalloc(sizeof(*chunk
));
278 ERR("Failed to allocate trace chunk");
281 lttng_trace_chunk_init(chunk
);
287 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
289 DBG("Creating anonymous trace chunk");
290 return lttng_trace_chunk_allocate();
294 struct lttng_trace_chunk
*lttng_trace_chunk_create(
295 uint64_t chunk_id
, time_t chunk_creation_time
, const char *path
)
297 struct lttng_trace_chunk
*chunk
;
298 char chunk_creation_datetime_buf
[16] = {};
299 const char *chunk_creation_datetime_str
= "(formatting error)";
300 struct tm timeinfo_buf
, *timeinfo
;
302 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
306 /* Don't fail because of this; it is only used for logging. */
307 strftime_ret
= strftime(chunk_creation_datetime_buf
,
308 sizeof(chunk_creation_datetime_buf
),
309 "%Y%m%d-%H%M%S", timeinfo
);
311 chunk_creation_datetime_str
=
312 chunk_creation_datetime_buf
;
316 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
317 chunk_id
, chunk_creation_datetime_str
);
318 chunk
= lttng_trace_chunk_allocate();
323 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
324 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
326 chunk
->name
= generate_chunk_name(chunk_id
,
327 chunk_creation_time
, NULL
);
329 ERR("Failed to allocate trace chunk name storage");
334 chunk
->path
= strdup(path
);
340 chunk
->path
= strdup(chunk
->name
);
347 DBG("Chunk name set to \"%s\"", chunk
->name
? : "(none)");
351 lttng_trace_chunk_put(chunk
);
356 struct lttng_trace_chunk
*lttng_trace_chunk_copy(
357 struct lttng_trace_chunk
*source_chunk
)
359 struct lttng_trace_chunk
*new_chunk
= lttng_trace_chunk_allocate();
365 pthread_mutex_lock(&source_chunk
->lock
);
367 * A new chunk is always a user; it shall create no new trace
370 new_chunk
->mode
= (typeof(new_chunk
->mode
)) {
372 .value
= TRACE_CHUNK_MODE_USER
,
375 * top_level_directories is not copied as it is never used
376 * by _user_ mode chunks.
378 /* The new chunk is not part of a registry (yet, at least). */
379 new_chunk
->in_registry_element
= false;
380 new_chunk
->name_overridden
= source_chunk
->name_overridden
;
381 if (source_chunk
->name
) {
382 new_chunk
->name
= strdup(source_chunk
->name
);
383 if (!new_chunk
->name
) {
384 ERR("Failed to copy source trace chunk name in %s()",
389 if (source_chunk
->path
) {
390 new_chunk
->path
= strdup(source_chunk
->path
);
391 if (!new_chunk
->path
) {
392 ERR("Failed to copy source trace chunk path in %s()",
396 new_chunk
->id
= source_chunk
->id
;
397 new_chunk
->timestamp_creation
= source_chunk
->timestamp_creation
;
398 new_chunk
->timestamp_close
= source_chunk
->timestamp_close
;
399 new_chunk
->credentials
= source_chunk
->credentials
;
400 if (source_chunk
->session_output_directory
) {
401 const bool reference_acquired
= lttng_directory_handle_get(
402 source_chunk
->session_output_directory
);
404 assert(reference_acquired
);
405 new_chunk
->session_output_directory
=
406 source_chunk
->session_output_directory
;
408 if (source_chunk
->chunk_directory
) {
409 const bool reference_acquired
= lttng_directory_handle_get(
410 source_chunk
->chunk_directory
);
412 assert(reference_acquired
);
413 new_chunk
->chunk_directory
= source_chunk
->chunk_directory
;
415 new_chunk
->close_command
= source_chunk
->close_command
;
416 pthread_mutex_unlock(&source_chunk
->lock
);
420 pthread_mutex_unlock(&source_chunk
->lock
);
421 lttng_trace_chunk_put(new_chunk
);
426 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(
427 struct lttng_trace_chunk
*chunk
, uint64_t *id
)
429 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
431 pthread_mutex_lock(&chunk
->lock
);
432 if (chunk
->id
.is_set
) {
433 *id
= chunk
->id
.value
;
435 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
437 pthread_mutex_unlock(&chunk
->lock
);
442 enum lttng_trace_chunk_status
lttng_trace_chunk_get_creation_timestamp(
443 struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
446 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
448 pthread_mutex_lock(&chunk
->lock
);
449 if (chunk
->timestamp_creation
.is_set
) {
450 *creation_ts
= chunk
->timestamp_creation
.value
;
452 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
454 pthread_mutex_unlock(&chunk
->lock
);
459 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_timestamp(
460 struct lttng_trace_chunk
*chunk
, time_t *close_ts
)
462 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
464 pthread_mutex_lock(&chunk
->lock
);
465 if (chunk
->timestamp_close
.is_set
) {
466 *close_ts
= chunk
->timestamp_close
.value
;
468 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
470 pthread_mutex_unlock(&chunk
->lock
);
475 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(
476 struct lttng_trace_chunk
*chunk
, time_t close_ts
)
478 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
480 pthread_mutex_lock(&chunk
->lock
);
481 if (!chunk
->timestamp_creation
.is_set
) {
482 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
483 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
486 if (chunk
->timestamp_creation
.value
> close_ts
) {
487 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
488 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
491 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
492 if (!chunk
->name_overridden
) {
494 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
495 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
498 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
502 pthread_mutex_unlock(&chunk
->lock
);
507 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(
508 struct lttng_trace_chunk
*chunk
, const char **name
,
509 bool *name_overridden
)
511 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
513 pthread_mutex_lock(&chunk
->lock
);
514 if (name_overridden
) {
515 *name_overridden
= chunk
->name_overridden
;
518 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
523 pthread_mutex_unlock(&chunk
->lock
);
528 bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk
*chunk
)
530 bool name_overridden
;
532 pthread_mutex_lock(&chunk
->lock
);
533 name_overridden
= chunk
->name_overridden
;
534 pthread_mutex_unlock(&chunk
->lock
);
535 return name_overridden
;
539 bool is_valid_chunk_name(const char *name
)
547 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
548 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
552 if (strchr(name
, '/') || strchr(name
, '.')) {
560 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
561 struct lttng_trace_chunk
*chunk
, const char *name
)
564 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
565 char *new_name
, *new_path
;
567 DBG("Override trace chunk name from %s to %s", chunk
->name
, name
);
568 if (!is_valid_chunk_name(name
)) {
569 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
571 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
575 pthread_mutex_lock(&chunk
->lock
);
576 if (!chunk
->id
.is_set
) {
577 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
579 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
583 new_name
= strdup(name
);
585 ERR("Failed to allocate new trace chunk name");
586 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
590 chunk
->name
= new_name
;
592 new_path
= strdup(name
);
594 ERR("Failed to allocate new trace chunk path");
595 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
599 chunk
->path
= new_path
;
601 chunk
->name_overridden
= true;
603 pthread_mutex_unlock(&chunk
->lock
);
609 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
610 struct lttng_trace_chunk
*chunk
, const char *path
)
613 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
614 struct lttng_directory_handle
*rename_directory
= NULL
;
615 char *new_path
, *old_path
;
618 if (chunk
->name_overridden
) {
619 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
623 old_path
= chunk
->path
;
624 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path
, path
);
626 if ((!old_path
&& !path
) ||
627 (old_path
&& path
&& !strcmp(old_path
, path
))) {
631 * Use chunk name as path if NULL path is specified.
637 /* Renaming from "" to "" is not accepted. */
638 if (path
[0] == '\0' && old_path
[0] == '\0') {
639 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
644 * If a rename is performed on a chunk for which the chunk_directory
645 * is not set (yet), or the session_output_directory is not set
646 * (interacting with a relay daemon), there is no rename to perform.
648 if (!chunk
->chunk_directory
||
649 !chunk
->session_output_directory
) {
653 if (old_path
[0] != '\0' && path
[0] != '\0') {
654 /* Rename chunk directory. */
655 ret
= lttng_directory_handle_rename_as_user(
656 chunk
->session_output_directory
,
658 chunk
->session_output_directory
,
660 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
662 &chunk
->credentials
.value
.user
);
664 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
666 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
669 rename_directory
= lttng_directory_handle_create_from_handle(
671 chunk
->session_output_directory
);
672 if (!rename_directory
) {
673 ERR("Failed to get handle to trace chunk rename directory");
674 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
678 /* Release old handle. */
679 lttng_directory_handle_put(chunk
->chunk_directory
);
681 * Transfer new handle reference to chunk as the current chunk
684 chunk
->chunk_directory
= rename_directory
;
685 rename_directory
= NULL
;
686 } else if (old_path
[0] == '\0') {
687 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
688 &chunk
->top_level_directories
);
690 ret
= lttng_directory_handle_create_subdirectory_as_user(
691 chunk
->session_output_directory
,
694 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
696 &chunk
->credentials
.value
.user
);
698 PERROR("Failed to create trace chunk rename directory \"%s\"",
700 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
704 rename_directory
= lttng_directory_handle_create_from_handle(
705 path
, chunk
->session_output_directory
);
706 if (!rename_directory
) {
707 ERR("Failed to get handle to trace chunk rename directory");
708 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
712 /* Move toplevel directories. */
713 for (i
= 0; i
< count
; i
++) {
714 const char *top_level_name
=
715 lttng_dynamic_pointer_array_get_pointer(
716 &chunk
->top_level_directories
, i
);
718 ret
= lttng_directory_handle_rename_as_user(
719 chunk
->chunk_directory
,
723 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
725 &chunk
->credentials
.value
.user
);
727 PERROR("Failed to move \"%s\" to trace chunk rename directory",
729 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
733 /* Release old handle. */
734 lttng_directory_handle_put(chunk
->chunk_directory
);
736 * Transfer new handle reference to chunk as the current chunk
739 chunk
->chunk_directory
= rename_directory
;
740 rename_directory
= NULL
;
742 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
743 &chunk
->top_level_directories
);
744 const bool reference_acquired
= lttng_directory_handle_get(
745 chunk
->session_output_directory
);
747 assert(reference_acquired
);
748 rename_directory
= chunk
->session_output_directory
;
750 /* Move toplevel directories. */
751 for (i
= 0; i
< count
; i
++) {
752 const char *top_level_name
=
753 lttng_dynamic_pointer_array_get_pointer(
754 &chunk
->top_level_directories
, i
);
756 ret
= lttng_directory_handle_rename_as_user(
757 chunk
->chunk_directory
,
761 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
763 &chunk
->credentials
.value
.user
);
765 PERROR("Failed to move \"%s\" to trace chunk rename directory",
767 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
771 /* Release old handle. */
772 lttng_directory_handle_put(chunk
->chunk_directory
);
774 * Transfer new handle reference to chunk as the current chunk
777 chunk
->chunk_directory
= rename_directory
;
778 rename_directory
= NULL
;
780 /* Remove old directory. */
781 status
= lttng_directory_handle_remove_subdirectory(
782 chunk
->session_output_directory
,
784 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
785 ERR("Error removing subdirectory '%s' file when deleting chunk",
794 new_path
= strdup(path
);
796 ERR("Failed to allocate new trace chunk path");
797 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
804 chunk
->path
= new_path
;
806 lttng_directory_handle_put(rename_directory
);
811 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path(
812 struct lttng_trace_chunk
*chunk
, const char *path
)
815 enum lttng_trace_chunk_status status
;
817 pthread_mutex_lock(&chunk
->lock
);
818 status
= lttng_trace_chunk_rename_path_no_lock(chunk
, path
);
819 pthread_mutex_unlock(&chunk
->lock
);
825 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
826 struct lttng_trace_chunk
*chunk
,
827 struct lttng_credentials
*credentials
)
829 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
831 pthread_mutex_lock(&chunk
->lock
);
832 if (chunk
->credentials
.is_set
) {
833 if (chunk
->credentials
.value
.use_current_user
) {
834 credentials
->uid
= geteuid();
835 credentials
->gid
= getegid();
837 *credentials
= chunk
->credentials
.value
.user
;
840 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
842 pthread_mutex_unlock(&chunk
->lock
);
847 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
848 struct lttng_trace_chunk
*chunk
,
849 const struct lttng_credentials
*user_credentials
)
851 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
852 const struct chunk_credentials credentials
= {
853 .user
= *user_credentials
,
854 .use_current_user
= false,
857 pthread_mutex_lock(&chunk
->lock
);
858 if (chunk
->credentials
.is_set
) {
859 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
862 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
864 pthread_mutex_unlock(&chunk
->lock
);
869 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
870 struct lttng_trace_chunk
*chunk
)
872 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
873 const struct chunk_credentials credentials
= {
874 .use_current_user
= true,
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
);
890 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
891 struct lttng_trace_chunk
*chunk
,
892 struct lttng_directory_handle
*session_output_directory
)
895 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
896 struct lttng_directory_handle
*chunk_directory_handle
= NULL
;
897 bool reference_acquired
;
899 pthread_mutex_lock(&chunk
->lock
);
900 if (chunk
->mode
.is_set
) {
901 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
904 if (!chunk
->credentials
.is_set
) {
906 * Fatal error, credentials must be set before a
907 * directory is created.
909 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
910 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
913 if (chunk
->path
[0] != '\0') {
914 ret
= lttng_directory_handle_create_subdirectory_as_user(
915 session_output_directory
,
918 !chunk
->credentials
.value
.use_current_user
?
919 &chunk
->credentials
.value
.user
: NULL
);
921 PERROR("Failed to create chunk output directory \"%s\"",
923 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
926 chunk_directory_handle
=
927 lttng_directory_handle_create_from_handle(
929 session_output_directory
);
930 if (!chunk_directory_handle
) {
931 /* The function already logs on all error paths. */
932 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
937 * A nameless chunk does not need its own output directory.
938 * The session's output directory will be used.
940 const bool reference_acquired
=
941 lttng_directory_handle_get(
942 session_output_directory
);
944 assert(reference_acquired
);
945 chunk_directory_handle
= session_output_directory
;
947 chunk
->chunk_directory
= chunk_directory_handle
;
948 chunk_directory_handle
= NULL
;
949 reference_acquired
= lttng_directory_handle_get(
950 session_output_directory
);
951 assert(reference_acquired
);
952 chunk
->session_output_directory
= session_output_directory
;
953 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
955 pthread_mutex_unlock(&chunk
->lock
);
960 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
961 struct lttng_trace_chunk
*chunk
,
962 struct lttng_directory_handle
*chunk_directory
)
964 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
965 bool reference_acquired
;
967 pthread_mutex_lock(&chunk
->lock
);
968 if (chunk
->mode
.is_set
) {
969 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
972 if (!chunk
->credentials
.is_set
) {
973 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
974 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
977 reference_acquired
= lttng_directory_handle_get(chunk_directory
);
978 assert(reference_acquired
);
979 chunk
->chunk_directory
= chunk_directory
;
980 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
982 pthread_mutex_unlock(&chunk
->lock
);
987 enum lttng_trace_chunk_status
988 lttng_trace_chunk_get_session_output_directory_handle(
989 struct lttng_trace_chunk
*chunk
,
990 struct lttng_directory_handle
**handle
)
992 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
994 pthread_mutex_lock(&chunk
->lock
);
995 if (!chunk
->session_output_directory
) {
996 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1000 const bool reference_acquired
= lttng_directory_handle_get(
1001 chunk
->session_output_directory
);
1003 assert(reference_acquired
);
1004 *handle
= chunk
->session_output_directory
;
1007 pthread_mutex_unlock(&chunk
->lock
);
1012 enum lttng_trace_chunk_status
lttng_trace_chunk_borrow_chunk_directory_handle(
1013 struct lttng_trace_chunk
*chunk
,
1014 const struct lttng_directory_handle
**handle
)
1016 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1018 pthread_mutex_lock(&chunk
->lock
);
1019 if (!chunk
->chunk_directory
) {
1020 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1024 *handle
= chunk
->chunk_directory
;
1026 pthread_mutex_unlock(&chunk
->lock
);
1030 /* Add a top-level directory to the trace chunk if it was previously unknown. */
1032 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
1033 const char *new_path
)
1037 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
1038 &chunk
->top_level_directories
);
1039 const char *new_path_separator_pos
= strchr(new_path
, '/');
1040 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
1041 new_path_separator_pos
- new_path
: strlen(new_path
);
1043 for (i
= 0; i
< count
; i
++) {
1044 const char *path
= lttng_dynamic_pointer_array_get_pointer(
1045 &chunk
->top_level_directories
, i
);
1046 const ptrdiff_t path_top_level_len
= strlen(path
);
1048 if (path_top_level_len
!= new_path_top_level_len
) {
1051 if (!strncmp(path
, new_path
, path_top_level_len
)) {
1058 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
1060 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1061 new_path
, chunk
->name
? : "(unnamed)");
1063 PERROR("Failed to copy path");
1067 ret
= lttng_dynamic_pointer_array_add_pointer(
1068 &chunk
->top_level_directories
, copy
);
1070 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1080 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
1081 struct lttng_trace_chunk
*chunk
,
1085 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1087 DBG("Creating trace chunk subdirectory \"%s\"", path
);
1088 pthread_mutex_lock(&chunk
->lock
);
1089 if (!chunk
->credentials
.is_set
) {
1091 * Fatal error, credentials must be set before a
1092 * directory is created.
1094 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1096 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1099 if (!chunk
->mode
.is_set
||
1100 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
1101 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1103 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1106 if (!chunk
->chunk_directory
) {
1107 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1109 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1113 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1115 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1118 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
1119 chunk
->chunk_directory
, path
,
1121 chunk
->credentials
.value
.use_current_user
?
1122 NULL
: &chunk
->credentials
.value
.user
);
1124 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1126 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1129 ret
= add_top_level_directory_unique(chunk
, path
);
1131 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1135 pthread_mutex_unlock(&chunk
->lock
);
1140 * TODO: Implement O(1) lookup.
1143 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk
*chunk
,
1144 const char *path
, size_t *index
)
1148 count
= lttng_dynamic_pointer_array_get_count(&chunk
->files
);
1149 for (i
= 0; i
< count
; i
++) {
1150 const char *iter_path
=
1151 lttng_dynamic_pointer_array_get_pointer(
1153 if (!strcmp(iter_path
, path
)) {
1164 enum lttng_trace_chunk_status
lttng_trace_chunk_add_file(
1165 struct lttng_trace_chunk
*chunk
,
1170 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1172 if (lttng_trace_chunk_find_file(chunk
, path
, NULL
)) {
1173 return LTTNG_TRACE_CHUNK_STATUS_OK
;
1175 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1176 path
, chunk
->name
? : "(unnamed)");
1177 copy
= strdup(path
);
1179 PERROR("Failed to copy path");
1180 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1183 ret
= lttng_dynamic_pointer_array_add_pointer(
1184 &chunk
->files
, copy
);
1186 ERR("Allocation failure while adding file to a trace chunk");
1188 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1196 void lttng_trace_chunk_remove_file(
1197 struct lttng_trace_chunk
*chunk
,
1204 found
= lttng_trace_chunk_find_file(chunk
, path
, &index
);
1208 ret
= lttng_dynamic_pointer_array_remove_pointer(
1209 &chunk
->files
, index
);
1214 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
1215 struct lttng_trace_chunk
*chunk
, const char *file_path
,
1216 int flags
, mode_t mode
, int *out_fd
, bool expect_no_file
)
1219 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1221 DBG("Opening trace chunk file \"%s\"", file_path
);
1222 pthread_mutex_lock(&chunk
->lock
);
1223 if (!chunk
->credentials
.is_set
) {
1225 * Fatal error, credentials must be set before a
1228 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1230 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1233 if (!chunk
->chunk_directory
) {
1234 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1236 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1239 status
= lttng_trace_chunk_add_file(chunk
, file_path
);
1240 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1243 ret
= lttng_directory_handle_open_file_as_user(
1244 chunk
->chunk_directory
, file_path
, flags
, mode
,
1245 chunk
->credentials
.value
.use_current_user
?
1246 NULL
: &chunk
->credentials
.value
.user
);
1248 if (errno
== ENOENT
&& expect_no_file
) {
1249 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
1251 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1252 file_path
, flags
, (int) mode
);
1253 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1255 lttng_trace_chunk_remove_file(chunk
, file_path
);
1260 pthread_mutex_unlock(&chunk
->lock
);
1265 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
1266 const char *file_path
)
1269 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1271 DBG("Unlinking trace chunk file \"%s\"", file_path
);
1272 pthread_mutex_lock(&chunk
->lock
);
1273 if (!chunk
->credentials
.is_set
) {
1275 * Fatal error, credentials must be set before a
1278 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1280 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1283 if (!chunk
->chunk_directory
) {
1284 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1286 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1289 ret
= lttng_directory_handle_unlink_file_as_user(
1290 chunk
->chunk_directory
, file_path
,
1291 chunk
->credentials
.value
.use_current_user
?
1292 NULL
: &chunk
->credentials
.value
.user
);
1294 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1297 lttng_trace_chunk_remove_file(chunk
, file_path
);
1299 pthread_mutex_unlock(&chunk
->lock
);
1304 int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk
*chunk
,
1308 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1310 DBG("Recursively removing trace chunk directory \"%s\"", path
);
1311 pthread_mutex_lock(&chunk
->lock
);
1312 if (!chunk
->credentials
.is_set
) {
1314 * Fatal error, credentials must be set before a
1315 * directory is removed.
1317 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1319 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1322 if (!chunk
->chunk_directory
) {
1323 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1325 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1328 ret
= lttng_directory_handle_remove_subdirectory_recursive_as_user(
1329 chunk
->chunk_directory
, path
,
1330 chunk
->credentials
.value
.use_current_user
?
1331 NULL
: &chunk
->credentials
.value
.user
,
1332 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
);
1334 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1338 pthread_mutex_unlock(&chunk
->lock
);
1343 int lttng_trace_chunk_move_to_completed_post_release(
1344 struct lttng_trace_chunk
*trace_chunk
)
1347 char *archived_chunk_name
= NULL
;
1348 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
1349 const time_t creation_timestamp
=
1350 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
1351 const time_t close_timestamp
=
1352 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
1353 struct lttng_directory_handle
*archived_chunks_directory
= NULL
;
1354 enum lttng_trace_chunk_status status
;
1356 if (!trace_chunk
->mode
.is_set
||
1357 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
1358 !trace_chunk
->session_output_directory
) {
1360 * This command doesn't need to run if the output is remote
1361 * or if the trace chunk is not owned by this process.
1366 assert(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
1367 assert(!trace_chunk
->name_overridden
);
1368 assert(trace_chunk
->path
);
1370 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
1372 if (!archived_chunk_name
) {
1373 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1378 ret
= lttng_directory_handle_create_subdirectory_as_user(
1379 trace_chunk
->session_output_directory
,
1380 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1382 !trace_chunk
->credentials
.value
.use_current_user
?
1383 &trace_chunk
->credentials
.value
.user
:
1386 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1387 "\" directory for archived trace chunks");
1391 archived_chunks_directory
= lttng_directory_handle_create_from_handle(
1392 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1393 trace_chunk
->session_output_directory
);
1394 if (!archived_chunks_directory
) {
1395 PERROR("Failed to get handle to archived trace chunks directory");
1401 * Make sure chunk is renamed to old directory if not already done by
1402 * the creation of the next chunk. This happens if a rotation is
1403 * performed while tracing is stopped.
1405 if (!trace_chunk
->path
|| strcmp(trace_chunk
->path
,
1406 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
)) {
1407 status
= lttng_trace_chunk_rename_path_no_lock(trace_chunk
,
1408 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1409 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1410 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1416 ret
= lttng_directory_handle_rename_as_user(
1417 trace_chunk
->session_output_directory
,
1419 archived_chunks_directory
,
1420 archived_chunk_name
,
1421 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1423 &trace_chunk
->credentials
.value
.user
);
1425 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1427 archived_chunk_name
);
1431 lttng_directory_handle_put(archived_chunks_directory
);
1432 free(archived_chunk_name
);
1437 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
)
1443 int lttng_trace_chunk_delete_post_release_user(
1444 struct lttng_trace_chunk
*trace_chunk
)
1448 DBG("Trace chunk \"delete\" close command post-release (User)");
1450 /* Unlink all files. */
1451 while (lttng_dynamic_pointer_array_get_count(&trace_chunk
->files
) != 0) {
1452 enum lttng_trace_chunk_status status
;
1456 path
= lttng_dynamic_pointer_array_get_pointer(
1457 &trace_chunk
->files
, 0);
1458 DBG("Unlink file: %s", path
);
1459 status
= lttng_trace_chunk_unlink_file(trace_chunk
, path
);
1460 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1461 ERR("Error unlinking file '%s' when deleting chunk", path
);
1471 int lttng_trace_chunk_delete_post_release_owner(
1472 struct lttng_trace_chunk
*trace_chunk
)
1474 enum lttng_trace_chunk_status status
;
1478 ret
= lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1483 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1485 assert(trace_chunk
->session_output_directory
);
1486 assert(trace_chunk
->chunk_directory
);
1488 /* Remove empty directories. */
1489 count
= lttng_dynamic_pointer_array_get_count(
1490 &trace_chunk
->top_level_directories
);
1492 for (i
= 0; i
< count
; i
++) {
1493 const char *top_level_name
=
1494 lttng_dynamic_pointer_array_get_pointer(
1495 &trace_chunk
->top_level_directories
, i
);
1497 status
= lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk
, top_level_name
);
1498 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1499 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1506 lttng_directory_handle_put(trace_chunk
->chunk_directory
);
1507 trace_chunk
->chunk_directory
= NULL
;
1509 if (trace_chunk
->path
&& trace_chunk
->path
[0] != '\0') {
1510 status
= lttng_directory_handle_remove_subdirectory(
1511 trace_chunk
->session_output_directory
,
1513 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1514 ERR("Error removing subdirectory '%s' file when deleting chunk",
1520 free(trace_chunk
->path
);
1521 trace_chunk
->path
= NULL
;
1527 * For local files, session and consumer daemons all run the delete hook. The
1528 * consumer daemons have the list of files to unlink, and technically the
1529 * session daemon is the owner of the chunk. Unlink all files owned by each
1533 int lttng_trace_chunk_delete_post_release(
1534 struct lttng_trace_chunk
*trace_chunk
)
1536 if (!trace_chunk
->chunk_directory
) {
1540 if (trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
) {
1541 return lttng_trace_chunk_delete_post_release_owner(trace_chunk
);
1543 return lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1548 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
1549 struct lttng_trace_chunk
*chunk
,
1550 enum lttng_trace_chunk_command_type
*command_type
)
1552 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1554 pthread_mutex_lock(&chunk
->lock
);
1555 if (chunk
->close_command
.is_set
) {
1556 *command_type
= chunk
->close_command
.value
;
1557 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1559 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1561 pthread_mutex_unlock(&chunk
->lock
);
1566 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
1567 struct lttng_trace_chunk
*chunk
,
1568 enum lttng_trace_chunk_command_type close_command
)
1570 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1572 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1573 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1574 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1578 pthread_mutex_lock(&chunk
->lock
);
1579 if (chunk
->close_command
.is_set
) {
1580 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1581 close_command_names
[chunk
->close_command
.value
],
1582 close_command_names
[close_command
]);
1584 DBG("Setting trace chunk close command to \"%s\"",
1585 close_command_names
[close_command
]);
1588 * Unset close command for no-op for backward compatibility with relayd
1591 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1592 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1594 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1596 pthread_mutex_unlock(&chunk
->lock
);
1602 const char *lttng_trace_chunk_command_type_get_name(
1603 enum lttng_trace_chunk_command_type command
)
1606 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1607 return "move to completed trace chunk folder";
1608 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1609 return "no operation";
1610 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1618 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1620 return urcu_ref_get_unless_zero(&chunk
->ref
);
1624 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1626 struct lttng_trace_chunk_registry_element
*element
=
1627 container_of(node
, typeof(*element
), rcu_node
);
1629 lttng_trace_chunk_fini(&element
->chunk
);
1634 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1636 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1639 if (chunk
->close_command
.is_set
) {
1640 if (close_command_post_release_funcs
[
1641 chunk
->close_command
.value
](chunk
)) {
1642 ERR("Trace chunk post-release command %s has failed.",
1643 close_command_names
[chunk
->close_command
.value
]);
1647 if (chunk
->in_registry_element
) {
1648 struct lttng_trace_chunk_registry_element
*element
;
1650 element
= container_of(chunk
, typeof(*element
), chunk
);
1651 if (element
->registry
) {
1653 cds_lfht_del(element
->registry
->ht
,
1654 &element
->trace_chunk_registry_ht_node
);
1656 call_rcu(&element
->rcu_node
,
1657 free_lttng_trace_chunk_registry_element
);
1659 /* Never published, can be free'd immediately. */
1660 free_lttng_trace_chunk_registry_element(
1661 &element
->rcu_node
);
1664 /* Not RCU-protected, free immediately. */
1665 lttng_trace_chunk_fini(chunk
);
1671 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1676 assert(chunk
->ref
.refcount
);
1677 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1681 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1683 struct lttng_trace_chunk_registry
*registry
;
1685 registry
= zmalloc(sizeof(*registry
));
1690 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1691 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1692 if (!registry
->ht
) {
1698 lttng_trace_chunk_registry_destroy(registry
);
1703 void lttng_trace_chunk_registry_destroy(
1704 struct lttng_trace_chunk_registry
*registry
)
1710 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1717 struct lttng_trace_chunk_registry_element
*
1718 lttng_trace_chunk_registry_element_create_from_chunk(
1719 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1721 struct lttng_trace_chunk_registry_element
*element
=
1722 zmalloc(sizeof(*element
));
1727 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1728 element
->session_id
= session_id
;
1730 element
->chunk
= *chunk
;
1731 lttng_trace_chunk_init(&element
->chunk
);
1732 if (chunk
->session_output_directory
) {
1733 /* Transferred ownership. */
1734 element
->chunk
.session_output_directory
=
1735 chunk
->session_output_directory
;
1736 chunk
->session_output_directory
= NULL
;
1738 if (chunk
->chunk_directory
) {
1739 /* Transferred ownership. */
1740 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1741 chunk
->chunk_directory
= NULL
;
1744 * The original chunk becomes invalid; the name and path attributes are
1745 * transferred to the new chunk instance.
1749 element
->chunk
.in_registry_element
= true;
1755 struct lttng_trace_chunk
*
1756 lttng_trace_chunk_registry_publish_chunk(
1757 struct lttng_trace_chunk_registry
*registry
,
1758 uint64_t session_id
, struct lttng_trace_chunk
*chunk
)
1760 struct lttng_trace_chunk_registry_element
*element
;
1761 unsigned long element_hash
;
1763 pthread_mutex_lock(&chunk
->lock
);
1764 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
1766 pthread_mutex_unlock(&chunk
->lock
);
1771 * chunk is now invalid, the only valid operation is a 'put' from the
1775 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1779 struct cds_lfht_node
*published_node
;
1780 struct lttng_trace_chunk
*published_chunk
;
1781 struct lttng_trace_chunk_registry_element
*published_element
;
1783 published_node
= cds_lfht_add_unique(registry
->ht
,
1785 lttng_trace_chunk_registry_element_match
,
1787 &element
->trace_chunk_registry_ht_node
);
1788 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
1789 /* Successfully published the new element. */
1790 element
->registry
= registry
;
1791 /* Acquire a reference for the caller. */
1792 if (lttng_trace_chunk_get(&element
->chunk
)) {
1796 * Another thread concurrently unpublished the
1797 * trace chunk. This is currently unexpected.
1799 * Re-attempt to publish.
1801 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1807 * An equivalent trace chunk was published before this trace
1808 * chunk. Attempt to acquire a reference to the one that was
1809 * already published and release the reference to the copy we
1810 * created if successful.
1812 published_element
= container_of(published_node
,
1813 typeof(*published_element
),
1814 trace_chunk_registry_ht_node
);
1815 published_chunk
= &published_element
->chunk
;
1816 if (lttng_trace_chunk_get(published_chunk
)) {
1817 lttng_trace_chunk_put(&element
->chunk
);
1818 element
= published_element
;
1822 * A reference to the previously published trace chunk could not
1823 * be acquired. Hence, retry to publish our copy of the trace
1829 return element
? &element
->chunk
: NULL
;
1833 * Note that the caller must be registered as an RCU thread.
1834 * However, it does not need to hold the RCU read lock. The RCU read lock is
1835 * acquired to perform the look-up in the registry's hash table and held until
1836 * after a reference to the "found" trace chunk is acquired.
1838 * IOW, holding a reference guarantees the existence of the object for the
1842 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
1843 const struct lttng_trace_chunk_registry
*registry
,
1844 uint64_t session_id
, uint64_t *chunk_id
)
1846 const struct lttng_trace_chunk_registry_element target_element
= {
1847 .chunk
.id
.is_set
= !!chunk_id
,
1848 .chunk
.id
.value
= chunk_id
? *chunk_id
: 0,
1849 .session_id
= session_id
,
1851 const unsigned long element_hash
=
1852 lttng_trace_chunk_registry_element_hash(
1854 struct cds_lfht_node
*published_node
;
1855 struct lttng_trace_chunk_registry_element
*published_element
;
1856 struct lttng_trace_chunk
*published_chunk
= NULL
;
1857 struct cds_lfht_iter iter
;
1860 cds_lfht_lookup(registry
->ht
,
1862 lttng_trace_chunk_registry_element_match
,
1865 published_node
= cds_lfht_iter_get_node(&iter
);
1866 if (!published_node
) {
1870 published_element
= container_of(published_node
,
1871 typeof(*published_element
),
1872 trace_chunk_registry_ht_node
);
1873 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
1874 published_chunk
= &published_element
->chunk
;
1878 return published_chunk
;
1882 struct lttng_trace_chunk
*
1883 lttng_trace_chunk_registry_find_chunk(
1884 const struct lttng_trace_chunk_registry
*registry
,
1885 uint64_t session_id
, uint64_t chunk_id
)
1887 return _lttng_trace_chunk_registry_find_chunk(registry
,
1888 session_id
, &chunk_id
);
1892 int lttng_trace_chunk_registry_chunk_exists(
1893 const struct lttng_trace_chunk_registry
*registry
,
1894 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
1897 const struct lttng_trace_chunk_registry_element target_element
= {
1898 .chunk
.id
.is_set
= true,
1899 .chunk
.id
.value
= chunk_id
,
1900 .session_id
= session_id
,
1902 const unsigned long element_hash
=
1903 lttng_trace_chunk_registry_element_hash(
1905 struct cds_lfht_node
*published_node
;
1906 struct cds_lfht_iter iter
;
1909 cds_lfht_lookup(registry
->ht
,
1911 lttng_trace_chunk_registry_element_match
,
1914 published_node
= cds_lfht_iter_get_node(&iter
);
1915 if (!published_node
) {
1916 *chunk_exists
= false;
1920 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
1927 struct lttng_trace_chunk
*
1928 lttng_trace_chunk_registry_find_anonymous_chunk(
1929 const struct lttng_trace_chunk_registry
*registry
,
1930 uint64_t session_id
)
1932 return _lttng_trace_chunk_registry_find_chunk(registry
,
1937 unsigned int lttng_trace_chunk_registry_put_each_chunk(
1938 const struct lttng_trace_chunk_registry
*registry
)
1940 struct cds_lfht_iter iter
;
1941 struct lttng_trace_chunk_registry_element
*chunk_element
;
1942 unsigned int trace_chunks_left
= 0;
1944 DBG("Releasing trace chunk registry to all trace chunks");
1946 cds_lfht_for_each_entry(registry
->ht
,
1947 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
1948 const char *chunk_id_str
= "none";
1949 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
1951 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
1952 if (chunk_element
->chunk
.id
.is_set
) {
1955 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
1957 chunk_element
->chunk
.id
.value
);
1958 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
1959 chunk_id_str
= "formatting error";
1961 chunk_id_str
= chunk_id_buf
;
1965 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
1966 "chunk_id = %s, name = \"%s\", status = %s",
1967 chunk_element
->session_id
,
1969 chunk_element
->chunk
.name
? : "none",
1970 chunk_element
->chunk
.close_command
.is_set
?
1972 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
1973 lttng_trace_chunk_put(&chunk_element
->chunk
);
1974 trace_chunks_left
++;
1977 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
1980 return trace_chunks_left
;