2 * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, version 2.1 only,
6 * as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 #include <common/compat/directory-handle.h>
19 #include <common/credentials.h>
20 #include <common/defaults.h>
21 #include <common/dynamic-array.h>
22 #include <common/error.h>
23 #include <common/fd-tracker/fd-tracker.h>
24 #include <common/fs-handle-internal.h>
25 #include <common/hashtable/hashtable.h>
26 #include <common/hashtable/utils.h>
27 #include <common/optional.h>
28 #include <common/string-utils/format.h>
29 #include <common/time.h>
30 #include <common/trace-chunk-registry.h>
31 #include <common/trace-chunk.h>
32 #include <common/utils.h>
33 #include <lttng/constant.h>
39 #include <urcu/rculfhash.h>
43 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
44 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
46 #define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
47 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
49 enum trace_chunk_mode
{
50 TRACE_CHUNK_MODE_USER
,
51 TRACE_CHUNK_MODE_OWNER
,
55 * Callback to invoke on release of a trace chunk. Note that there is no
56 * need to 'lock' the trace chunk during the execution of these callbacks
57 * since only one thread may access a chunk during its destruction (the last
58 * to release its reference to the chunk).
60 typedef int (*chunk_command
)(struct lttng_trace_chunk
*trace_chunk
);
62 /* Move a completed trace chunk to the 'completed' trace archive folder. */
64 int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk
*trace_chunk
);
67 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
);
68 /* Unlink old chunk files. */
70 int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk
*trace_chunk
);
72 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
73 struct lttng_trace_chunk
*chunk
, const char *path
);
75 struct chunk_credentials
{
76 bool use_current_user
;
77 struct lttng_credentials user
;
81 * NOTE: Make sure to update:
82 * - lttng_trace_chunk_copy(),
83 * - lttng_trace_chunk_registry_element_create_from_chunk()
84 * if you modify this structure.
86 struct lttng_trace_chunk
{
89 LTTNG_OPTIONAL(enum trace_chunk_mode
) mode
;
91 * First-level directories created within the trace chunk.
92 * Elements are of type 'char *'.
94 * Only used by _owner_ mode chunks.
96 struct lttng_dynamic_pointer_array top_level_directories
;
98 * All files contained within the trace chunk.
99 * Array of paths (char *).
101 struct lttng_dynamic_pointer_array files
;
102 /* Is contained within an lttng_trace_chunk_registry_element? */
103 bool in_registry_element
;
104 bool name_overridden
;
107 /* An unset id means the chunk is anonymous. */
108 LTTNG_OPTIONAL(uint64_t) id
;
109 LTTNG_OPTIONAL(time_t) timestamp_creation
;
110 LTTNG_OPTIONAL(time_t) timestamp_close
;
111 LTTNG_OPTIONAL(struct chunk_credentials
) credentials
;
112 struct lttng_directory_handle
*session_output_directory
;
113 struct lttng_directory_handle
*chunk_directory
;
114 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type
) close_command
;
116 * fd_tracker instance through which file descriptors should be
119 * An fd_tracker always outlives any trace chunk; there is no
120 * need to perform any reference counting of that object.
122 struct fd_tracker
*fd_tracker
;
125 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
126 struct lttng_trace_chunk_registry_element
{
127 struct lttng_trace_chunk chunk
;
129 /* Weak and only set when added. */
130 struct lttng_trace_chunk_registry
*registry
;
131 struct cds_lfht_node trace_chunk_registry_ht_node
;
132 /* call_rcu delayed reclaim. */
133 struct rcu_head rcu_node
;
136 struct lttng_trace_chunk_registry
{
140 struct fs_handle_untracked
{
141 struct fs_handle parent
;
144 struct lttng_directory_handle
*directory_handle
;
150 int fs_handle_untracked_get_fd(struct fs_handle
*handle
);
152 void fs_handle_untracked_put_fd(struct fs_handle
*handle
);
154 int fs_handle_untracked_unlink(struct fs_handle
*handle
);
156 int fs_handle_untracked_close(struct fs_handle
*handle
);
159 char *close_command_names
[] = {
160 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
161 "move to completed chunk folder",
162 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
164 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
169 chunk_command close_command_post_release_funcs
[] = {
170 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
171 lttng_trace_chunk_move_to_completed_post_release
,
172 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
173 lttng_trace_chunk_no_operation
,
174 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
175 lttng_trace_chunk_delete_post_release
,
179 struct fs_handle
*fs_handle_untracked_create(
180 struct lttng_directory_handle
*directory_handle
,
184 struct fs_handle_untracked
*handle
= NULL
;
185 bool reference_acquired
;
186 char *path_copy
= strdup(path
);
190 PERROR("Failed to copy file path while creating untracked filesystem handle");
194 handle
= zmalloc(sizeof(typeof(*handle
)));
196 PERROR("Failed to allocate untracked filesystem handle");
200 handle
->parent
= (typeof(handle
->parent
)) {
201 .get_fd
= fs_handle_untracked_get_fd
,
202 .put_fd
= fs_handle_untracked_put_fd
,
203 .unlink
= fs_handle_untracked_unlink
,
204 .close
= fs_handle_untracked_close
,
208 reference_acquired
= lttng_directory_handle_get(directory_handle
);
209 assert(reference_acquired
);
210 handle
->location
.directory_handle
= directory_handle
;
211 /* Ownership is transferred. */
212 handle
->location
.path
= path_copy
;
216 return handle
? &handle
->parent
: NULL
;
220 int fs_handle_untracked_get_fd(struct fs_handle
*_handle
)
222 struct fs_handle_untracked
*handle
= container_of(
223 _handle
, struct fs_handle_untracked
, parent
);
229 void fs_handle_untracked_put_fd(struct fs_handle
*_handle
)
235 int fs_handle_untracked_unlink(struct fs_handle
*_handle
)
237 struct fs_handle_untracked
*handle
= container_of(
238 _handle
, struct fs_handle_untracked
, parent
);
240 return lttng_directory_handle_unlink_file(
241 handle
->location
.directory_handle
,
242 handle
->location
.path
);
246 void fs_handle_untracked_destroy(struct fs_handle_untracked
*handle
)
248 lttng_directory_handle_put(handle
->location
.directory_handle
);
249 free(handle
->location
.path
);
254 int fs_handle_untracked_close(struct fs_handle
*_handle
)
256 struct fs_handle_untracked
*handle
= container_of(
257 _handle
, struct fs_handle_untracked
, parent
);
258 int ret
= close(handle
->fd
);
260 fs_handle_untracked_destroy(handle
);
265 bool lttng_trace_chunk_registry_element_equals(
266 const struct lttng_trace_chunk_registry_element
*a
,
267 const struct lttng_trace_chunk_registry_element
*b
)
269 if (a
->session_id
!= b
->session_id
) {
272 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
275 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
284 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
,
287 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
289 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
290 element_b
= caa_container_of(node
, typeof(*element_b
),
291 trace_chunk_registry_ht_node
);
292 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
296 unsigned long lttng_trace_chunk_registry_element_hash(
297 const struct lttng_trace_chunk_registry_element
*element
)
299 unsigned long hash
= hash_key_u64(&element
->session_id
,
302 if (element
->chunk
.id
.is_set
) {
303 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
310 char *generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
,
311 const time_t *close_timestamp
)
314 char *new_name
= NULL
;
315 char start_datetime
[ISO8601_STR_LEN
] = {};
316 /* Add 1 for a '-' prefix. */
317 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
319 ret
= time_to_iso8601_str(
321 start_datetime
, sizeof(start_datetime
));
323 ERR("Failed to format trace chunk start date time");
326 if (close_timestamp
) {
327 *end_datetime_suffix
= '-';
328 ret
= time_to_iso8601_str(
330 end_datetime_suffix
+ 1,
331 sizeof(end_datetime_suffix
) - 1);
333 ERR("Failed to format trace chunk end date time");
337 new_name
= zmalloc(GENERATED_CHUNK_NAME_LEN
);
339 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
342 ret
= snprintf(new_name
, GENERATED_CHUNK_NAME_LEN
, "%s%s-%" PRIu64
,
343 start_datetime
, end_datetime_suffix
, chunk_id
);
344 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
345 ERR("Failed to format trace chunk name");
356 void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
358 urcu_ref_init(&chunk
->ref
);
359 pthread_mutex_init(&chunk
->lock
, NULL
);
360 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
361 lttng_dynamic_pointer_array_init(&chunk
->files
, free
);
365 void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
367 if (chunk
->session_output_directory
) {
368 lttng_directory_handle_put(
369 chunk
->session_output_directory
);
370 chunk
->session_output_directory
= NULL
;
372 if (chunk
->chunk_directory
) {
373 lttng_directory_handle_put(chunk
->chunk_directory
);
374 chunk
->chunk_directory
= NULL
;
380 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
381 lttng_dynamic_pointer_array_reset(&chunk
->files
);
382 pthread_mutex_destroy(&chunk
->lock
);
386 struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
388 struct lttng_trace_chunk
*chunk
= NULL
;
390 chunk
= zmalloc(sizeof(*chunk
));
392 ERR("Failed to allocate trace chunk");
395 lttng_trace_chunk_init(chunk
);
401 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
403 DBG("Creating anonymous trace chunk");
404 return lttng_trace_chunk_allocate();
408 struct lttng_trace_chunk
*lttng_trace_chunk_create(
409 uint64_t chunk_id
, time_t chunk_creation_time
, const char *path
)
411 struct lttng_trace_chunk
*chunk
;
412 char chunk_creation_datetime_buf
[16] = {};
413 const char *chunk_creation_datetime_str
= "(formatting error)";
414 struct tm timeinfo_buf
, *timeinfo
;
416 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
420 /* Don't fail because of this; it is only used for logging. */
421 strftime_ret
= strftime(chunk_creation_datetime_buf
,
422 sizeof(chunk_creation_datetime_buf
),
423 "%Y%m%d-%H%M%S", timeinfo
);
425 chunk_creation_datetime_str
=
426 chunk_creation_datetime_buf
;
430 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
431 chunk_id
, chunk_creation_datetime_str
);
432 chunk
= lttng_trace_chunk_allocate();
437 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
438 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
440 chunk
->name
= generate_chunk_name(chunk_id
,
441 chunk_creation_time
, NULL
);
443 ERR("Failed to allocate trace chunk name storage");
448 chunk
->path
= strdup(path
);
454 chunk
->path
= strdup(chunk
->name
);
461 DBG("Chunk name set to \"%s\"", chunk
->name
? : "(none)");
465 lttng_trace_chunk_put(chunk
);
470 void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk
*chunk
,
471 struct fd_tracker
*fd_tracker
)
473 assert(!chunk
->session_output_directory
);
474 assert(!chunk
->chunk_directory
);
475 assert(lttng_dynamic_pointer_array_get_count(&chunk
->files
) == 0);
476 chunk
->fd_tracker
= fd_tracker
;
480 struct lttng_trace_chunk
*lttng_trace_chunk_copy(
481 struct lttng_trace_chunk
*source_chunk
)
483 struct lttng_trace_chunk
*new_chunk
= lttng_trace_chunk_allocate();
489 pthread_mutex_lock(&source_chunk
->lock
);
491 * A new chunk is always a user; it shall create no new trace
494 new_chunk
->mode
= (typeof(new_chunk
->mode
)) {
496 .value
= TRACE_CHUNK_MODE_USER
,
499 * top_level_directories is not copied as it is never used
500 * by _user_ mode chunks.
502 /* The new chunk is not part of a registry (yet, at least). */
503 new_chunk
->in_registry_element
= false;
504 new_chunk
->name_overridden
= source_chunk
->name_overridden
;
505 if (source_chunk
->name
) {
506 new_chunk
->name
= strdup(source_chunk
->name
);
507 if (!new_chunk
->name
) {
508 ERR("Failed to copy source trace chunk name in %s()",
513 if (source_chunk
->path
) {
514 new_chunk
->path
= strdup(source_chunk
->path
);
515 if (!new_chunk
->path
) {
516 ERR("Failed to copy source trace chunk path in %s()",
520 new_chunk
->id
= source_chunk
->id
;
521 new_chunk
->timestamp_creation
= source_chunk
->timestamp_creation
;
522 new_chunk
->timestamp_close
= source_chunk
->timestamp_close
;
523 new_chunk
->credentials
= source_chunk
->credentials
;
524 if (source_chunk
->session_output_directory
) {
525 const bool reference_acquired
= lttng_directory_handle_get(
526 source_chunk
->session_output_directory
);
528 assert(reference_acquired
);
529 new_chunk
->session_output_directory
=
530 source_chunk
->session_output_directory
;
532 if (source_chunk
->chunk_directory
) {
533 const bool reference_acquired
= lttng_directory_handle_get(
534 source_chunk
->chunk_directory
);
536 assert(reference_acquired
);
537 new_chunk
->chunk_directory
= source_chunk
->chunk_directory
;
539 new_chunk
->close_command
= source_chunk
->close_command
;
540 new_chunk
->fd_tracker
= source_chunk
->fd_tracker
;
541 pthread_mutex_unlock(&source_chunk
->lock
);
545 pthread_mutex_unlock(&source_chunk
->lock
);
546 lttng_trace_chunk_put(new_chunk
);
551 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(
552 struct lttng_trace_chunk
*chunk
, uint64_t *id
)
554 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
556 pthread_mutex_lock(&chunk
->lock
);
557 if (chunk
->id
.is_set
) {
558 *id
= chunk
->id
.value
;
560 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
562 pthread_mutex_unlock(&chunk
->lock
);
567 enum lttng_trace_chunk_status
lttng_trace_chunk_get_creation_timestamp(
568 struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
571 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
573 pthread_mutex_lock(&chunk
->lock
);
574 if (chunk
->timestamp_creation
.is_set
) {
575 *creation_ts
= chunk
->timestamp_creation
.value
;
577 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
579 pthread_mutex_unlock(&chunk
->lock
);
584 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_timestamp(
585 struct lttng_trace_chunk
*chunk
, time_t *close_ts
)
587 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
589 pthread_mutex_lock(&chunk
->lock
);
590 if (chunk
->timestamp_close
.is_set
) {
591 *close_ts
= chunk
->timestamp_close
.value
;
593 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
595 pthread_mutex_unlock(&chunk
->lock
);
600 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(
601 struct lttng_trace_chunk
*chunk
, time_t close_ts
)
603 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
605 pthread_mutex_lock(&chunk
->lock
);
606 if (!chunk
->timestamp_creation
.is_set
) {
607 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
608 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
611 if (chunk
->timestamp_creation
.value
> close_ts
) {
612 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
613 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
616 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
617 if (!chunk
->name_overridden
) {
619 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
620 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
623 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
627 pthread_mutex_unlock(&chunk
->lock
);
632 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(
633 struct lttng_trace_chunk
*chunk
, const char **name
,
634 bool *name_overridden
)
636 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
638 pthread_mutex_lock(&chunk
->lock
);
639 if (name_overridden
) {
640 *name_overridden
= chunk
->name_overridden
;
643 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
648 pthread_mutex_unlock(&chunk
->lock
);
653 bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk
*chunk
)
655 bool name_overridden
;
657 pthread_mutex_lock(&chunk
->lock
);
658 name_overridden
= chunk
->name_overridden
;
659 pthread_mutex_unlock(&chunk
->lock
);
660 return name_overridden
;
664 bool is_valid_chunk_name(const char *name
)
672 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
673 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
677 if (strchr(name
, '/') || strchr(name
, '.')) {
685 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
686 struct lttng_trace_chunk
*chunk
, const char *name
)
689 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
690 char *new_name
, *new_path
;
692 DBG("Override trace chunk name from %s to %s", chunk
->name
, name
);
693 if (!is_valid_chunk_name(name
)) {
694 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
696 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
700 pthread_mutex_lock(&chunk
->lock
);
701 if (!chunk
->id
.is_set
) {
702 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
704 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
708 new_name
= strdup(name
);
710 ERR("Failed to allocate new trace chunk name");
711 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
715 chunk
->name
= new_name
;
717 new_path
= strdup(name
);
719 ERR("Failed to allocate new trace chunk path");
720 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
724 chunk
->path
= new_path
;
726 chunk
->name_overridden
= true;
728 pthread_mutex_unlock(&chunk
->lock
);
734 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path_no_lock(
735 struct lttng_trace_chunk
*chunk
, const char *path
)
738 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
739 struct lttng_directory_handle
*rename_directory
= NULL
;
740 char *new_path
, *old_path
;
743 if (chunk
->name_overridden
) {
744 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
748 old_path
= chunk
->path
;
749 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path
, path
);
751 if ((!old_path
&& !path
) ||
752 (old_path
&& path
&& !strcmp(old_path
, path
))) {
756 * Use chunk name as path if NULL path is specified.
762 /* Renaming from "" to "" is not accepted. */
763 if (path
[0] == '\0' && old_path
[0] == '\0') {
764 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
769 * If a rename is performed on a chunk for which the chunk_directory
770 * is not set (yet), or the session_output_directory is not set
771 * (interacting with a relay daemon), there is no rename to perform.
773 if (!chunk
->chunk_directory
||
774 !chunk
->session_output_directory
) {
778 if (old_path
[0] != '\0' && path
[0] != '\0') {
779 /* Rename chunk directory. */
780 ret
= lttng_directory_handle_rename_as_user(
781 chunk
->session_output_directory
,
783 chunk
->session_output_directory
,
785 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
787 &chunk
->credentials
.value
.user
);
789 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
791 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
794 rename_directory
= lttng_directory_handle_create_from_handle(
796 chunk
->session_output_directory
);
797 if (!rename_directory
) {
798 ERR("Failed to get handle to trace chunk rename directory");
799 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
803 /* Release old handle. */
804 lttng_directory_handle_put(chunk
->chunk_directory
);
806 * Transfer new handle reference to chunk as the current chunk
809 chunk
->chunk_directory
= rename_directory
;
810 rename_directory
= NULL
;
811 } else if (old_path
[0] == '\0') {
812 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
813 &chunk
->top_level_directories
);
815 ret
= lttng_directory_handle_create_subdirectory_as_user(
816 chunk
->session_output_directory
,
819 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
821 &chunk
->credentials
.value
.user
);
823 PERROR("Failed to create trace chunk rename directory \"%s\"",
825 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
829 rename_directory
= lttng_directory_handle_create_from_handle(
830 path
, chunk
->session_output_directory
);
831 if (!rename_directory
) {
832 ERR("Failed to get handle to trace chunk rename directory");
833 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
837 /* Move toplevel directories. */
838 for (i
= 0; i
< count
; i
++) {
839 const char *top_level_name
=
840 lttng_dynamic_pointer_array_get_pointer(
841 &chunk
->top_level_directories
, i
);
843 ret
= lttng_directory_handle_rename_as_user(
844 chunk
->chunk_directory
,
848 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
850 &chunk
->credentials
.value
.user
);
852 PERROR("Failed to move \"%s\" to trace chunk rename directory",
854 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
858 /* Release old handle. */
859 lttng_directory_handle_put(chunk
->chunk_directory
);
861 * Transfer new handle reference to chunk as the current chunk
864 chunk
->chunk_directory
= rename_directory
;
865 rename_directory
= NULL
;
867 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
868 &chunk
->top_level_directories
);
869 const bool reference_acquired
= lttng_directory_handle_get(
870 chunk
->session_output_directory
);
872 assert(reference_acquired
);
873 rename_directory
= chunk
->session_output_directory
;
875 /* Move toplevel directories. */
876 for (i
= 0; i
< count
; i
++) {
877 const char *top_level_name
=
878 lttng_dynamic_pointer_array_get_pointer(
879 &chunk
->top_level_directories
, i
);
881 ret
= lttng_directory_handle_rename_as_user(
882 chunk
->chunk_directory
,
886 LTTNG_OPTIONAL_GET(chunk
->credentials
).use_current_user
?
888 &chunk
->credentials
.value
.user
);
890 PERROR("Failed to move \"%s\" to trace chunk rename directory",
892 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
896 /* Release old handle. */
897 lttng_directory_handle_put(chunk
->chunk_directory
);
899 * Transfer new handle reference to chunk as the current chunk
902 chunk
->chunk_directory
= rename_directory
;
903 rename_directory
= NULL
;
905 /* Remove old directory. */
906 status
= lttng_directory_handle_remove_subdirectory(
907 chunk
->session_output_directory
,
909 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
910 ERR("Error removing subdirectory '%s' file when deleting chunk",
919 new_path
= strdup(path
);
921 ERR("Failed to allocate new trace chunk path");
922 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
929 chunk
->path
= new_path
;
931 lttng_directory_handle_put(rename_directory
);
936 enum lttng_trace_chunk_status
lttng_trace_chunk_rename_path(
937 struct lttng_trace_chunk
*chunk
, const char *path
)
940 enum lttng_trace_chunk_status status
;
942 pthread_mutex_lock(&chunk
->lock
);
943 status
= lttng_trace_chunk_rename_path_no_lock(chunk
, path
);
944 pthread_mutex_unlock(&chunk
->lock
);
950 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
951 struct lttng_trace_chunk
*chunk
,
952 struct lttng_credentials
*credentials
)
954 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
956 pthread_mutex_lock(&chunk
->lock
);
957 if (chunk
->credentials
.is_set
) {
958 if (chunk
->credentials
.value
.use_current_user
) {
959 credentials
->uid
= geteuid();
960 credentials
->gid
= getegid();
962 *credentials
= chunk
->credentials
.value
.user
;
965 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
967 pthread_mutex_unlock(&chunk
->lock
);
972 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
973 struct lttng_trace_chunk
*chunk
,
974 const struct lttng_credentials
*user_credentials
)
976 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
977 const struct chunk_credentials credentials
= {
978 .user
= *user_credentials
,
979 .use_current_user
= false,
982 pthread_mutex_lock(&chunk
->lock
);
983 if (chunk
->credentials
.is_set
) {
984 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
987 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
989 pthread_mutex_unlock(&chunk
->lock
);
994 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
995 struct lttng_trace_chunk
*chunk
)
997 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
998 const struct chunk_credentials credentials
= {
999 .use_current_user
= true,
1002 pthread_mutex_lock(&chunk
->lock
);
1003 if (chunk
->credentials
.is_set
) {
1004 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1007 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
1009 pthread_mutex_unlock(&chunk
->lock
);
1015 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
1016 struct lttng_trace_chunk
*chunk
,
1017 struct lttng_directory_handle
*session_output_directory
)
1020 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1021 struct lttng_directory_handle
*chunk_directory_handle
= NULL
;
1022 bool reference_acquired
;
1024 pthread_mutex_lock(&chunk
->lock
);
1025 if (chunk
->mode
.is_set
) {
1026 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1029 if (!chunk
->credentials
.is_set
) {
1031 * Fatal error, credentials must be set before a
1032 * directory is created.
1034 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
1035 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1038 if (chunk
->path
[0] != '\0') {
1039 ret
= lttng_directory_handle_create_subdirectory_as_user(
1040 session_output_directory
,
1043 !chunk
->credentials
.value
.use_current_user
?
1044 &chunk
->credentials
.value
.user
: NULL
);
1046 PERROR("Failed to create chunk output directory \"%s\"",
1048 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1051 chunk_directory_handle
=
1052 lttng_directory_handle_create_from_handle(
1054 session_output_directory
);
1055 if (!chunk_directory_handle
) {
1056 /* The function already logs on all error paths. */
1057 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1062 * A nameless chunk does not need its own output directory.
1063 * The session's output directory will be used.
1065 const bool reference_acquired
=
1066 lttng_directory_handle_get(
1067 session_output_directory
);
1069 assert(reference_acquired
);
1070 chunk_directory_handle
= session_output_directory
;
1072 chunk
->chunk_directory
= chunk_directory_handle
;
1073 chunk_directory_handle
= NULL
;
1074 reference_acquired
= lttng_directory_handle_get(
1075 session_output_directory
);
1076 assert(reference_acquired
);
1077 chunk
->session_output_directory
= session_output_directory
;
1078 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
1080 pthread_mutex_unlock(&chunk
->lock
);
1085 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
1086 struct lttng_trace_chunk
*chunk
,
1087 struct lttng_directory_handle
*chunk_directory
)
1089 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1090 bool reference_acquired
;
1092 pthread_mutex_lock(&chunk
->lock
);
1093 if (chunk
->mode
.is_set
) {
1094 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1097 if (!chunk
->credentials
.is_set
) {
1098 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
1099 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1102 reference_acquired
= lttng_directory_handle_get(chunk_directory
);
1103 assert(reference_acquired
);
1104 chunk
->chunk_directory
= chunk_directory
;
1105 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
1107 pthread_mutex_unlock(&chunk
->lock
);
1112 enum lttng_trace_chunk_status
1113 lttng_trace_chunk_get_session_output_directory_handle(
1114 struct lttng_trace_chunk
*chunk
,
1115 struct lttng_directory_handle
**handle
)
1117 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1119 pthread_mutex_lock(&chunk
->lock
);
1120 if (!chunk
->session_output_directory
) {
1121 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1125 const bool reference_acquired
= lttng_directory_handle_get(
1126 chunk
->session_output_directory
);
1128 assert(reference_acquired
);
1129 *handle
= chunk
->session_output_directory
;
1132 pthread_mutex_unlock(&chunk
->lock
);
1137 enum lttng_trace_chunk_status
lttng_trace_chunk_borrow_chunk_directory_handle(
1138 struct lttng_trace_chunk
*chunk
,
1139 const struct lttng_directory_handle
**handle
)
1141 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1143 pthread_mutex_lock(&chunk
->lock
);
1144 if (!chunk
->chunk_directory
) {
1145 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1149 *handle
= chunk
->chunk_directory
;
1151 pthread_mutex_unlock(&chunk
->lock
);
1155 /* Add a top-level directory to the trace chunk if it was previously unknown. */
1157 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
1158 const char *new_path
)
1162 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
1163 &chunk
->top_level_directories
);
1164 const char *new_path_separator_pos
= strchr(new_path
, '/');
1165 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
1166 new_path_separator_pos
- new_path
: strlen(new_path
);
1168 for (i
= 0; i
< count
; i
++) {
1169 const char *path
= lttng_dynamic_pointer_array_get_pointer(
1170 &chunk
->top_level_directories
, i
);
1171 const ptrdiff_t path_top_level_len
= strlen(path
);
1173 if (path_top_level_len
!= new_path_top_level_len
) {
1176 if (!strncmp(path
, new_path
, path_top_level_len
)) {
1183 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
1185 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1186 new_path
, chunk
->name
? : "(unnamed)");
1188 PERROR("Failed to copy path");
1192 ret
= lttng_dynamic_pointer_array_add_pointer(
1193 &chunk
->top_level_directories
, copy
);
1195 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1205 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
1206 struct lttng_trace_chunk
*chunk
,
1210 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1212 DBG("Creating trace chunk subdirectory \"%s\"", path
);
1213 pthread_mutex_lock(&chunk
->lock
);
1214 if (!chunk
->credentials
.is_set
) {
1216 * Fatal error, credentials must be set before a
1217 * directory is created.
1219 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1221 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1224 if (!chunk
->mode
.is_set
||
1225 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
1226 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1228 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
1231 if (!chunk
->chunk_directory
) {
1232 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1234 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1238 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1240 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1243 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
1244 chunk
->chunk_directory
, path
,
1246 chunk
->credentials
.value
.use_current_user
?
1247 NULL
: &chunk
->credentials
.value
.user
);
1249 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1251 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1254 ret
= add_top_level_directory_unique(chunk
, path
);
1256 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1260 pthread_mutex_unlock(&chunk
->lock
);
1265 * TODO: Implement O(1) lookup.
1268 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk
*chunk
,
1269 const char *path
, size_t *index
)
1273 count
= lttng_dynamic_pointer_array_get_count(&chunk
->files
);
1274 for (i
= 0; i
< count
; i
++) {
1275 const char *iter_path
=
1276 lttng_dynamic_pointer_array_get_pointer(
1278 if (!strcmp(iter_path
, path
)) {
1289 enum lttng_trace_chunk_status
lttng_trace_chunk_add_file(
1290 struct lttng_trace_chunk
*chunk
,
1295 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1297 if (lttng_trace_chunk_find_file(chunk
, path
, NULL
)) {
1298 return LTTNG_TRACE_CHUNK_STATUS_OK
;
1300 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1301 path
, chunk
->name
? : "(unnamed)");
1302 copy
= strdup(path
);
1304 PERROR("Failed to copy path");
1305 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1308 ret
= lttng_dynamic_pointer_array_add_pointer(
1309 &chunk
->files
, copy
);
1311 ERR("Allocation failure while adding file to a trace chunk");
1313 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1321 void lttng_trace_chunk_remove_file(
1322 struct lttng_trace_chunk
*chunk
,
1329 found
= lttng_trace_chunk_find_file(chunk
, path
, &index
);
1333 ret
= lttng_dynamic_pointer_array_remove_pointer(
1334 &chunk
->files
, index
);
1339 enum lttng_trace_chunk_status
_lttng_trace_chunk_open_fs_handle_locked(
1340 struct lttng_trace_chunk
*chunk
,
1341 const char *file_path
,
1344 struct fs_handle
**out_handle
,
1345 bool expect_no_file
)
1348 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1350 DBG("Opening trace chunk file \"%s\"", file_path
);
1351 if (!chunk
->credentials
.is_set
) {
1353 * Fatal error, credentials must be set before a
1356 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1358 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1361 if (!chunk
->chunk_directory
) {
1362 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1364 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1367 status
= lttng_trace_chunk_add_file(chunk
, file_path
);
1368 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1371 if (chunk
->fd_tracker
) {
1372 assert(chunk
->credentials
.value
.use_current_user
);
1373 *out_handle
= fd_tracker_open_fs_handle(chunk
->fd_tracker
,
1374 chunk
->chunk_directory
, file_path
, flags
, &mode
);
1375 ret
= *out_handle
? 0 : -1;
1377 ret
= lttng_directory_handle_open_file_as_user(
1378 chunk
->chunk_directory
, file_path
, flags
, mode
,
1379 chunk
->credentials
.value
.use_current_user
?
1381 &chunk
->credentials
.value
.user
);
1383 *out_handle
= fs_handle_untracked_create(
1384 chunk
->chunk_directory
, file_path
, ret
);
1386 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1392 if (errno
== ENOENT
&& expect_no_file
) {
1393 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
1395 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1396 file_path
, flags
, (int) mode
);
1397 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1399 lttng_trace_chunk_remove_file(chunk
, file_path
);
1407 enum lttng_trace_chunk_status
lttng_trace_chunk_open_fs_handle(
1408 struct lttng_trace_chunk
*chunk
,
1409 const char *file_path
,
1412 struct fs_handle
**out_handle
,
1413 bool expect_no_file
)
1415 enum lttng_trace_chunk_status status
;
1417 pthread_mutex_lock(&chunk
->lock
);
1418 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1419 flags
, mode
, out_handle
, expect_no_file
);
1420 pthread_mutex_unlock(&chunk
->lock
);
1425 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
1426 struct lttng_trace_chunk
*chunk
,
1427 const char *file_path
,
1431 bool expect_no_file
)
1433 enum lttng_trace_chunk_status status
;
1434 struct fs_handle
*fs_handle
;
1436 pthread_mutex_lock(&chunk
->lock
);
1438 * Using this method is never valid when an fd_tracker is being
1439 * used since the resulting file descriptor would not be tracked.
1441 assert(!chunk
->fd_tracker
);
1442 status
= _lttng_trace_chunk_open_fs_handle_locked(chunk
, file_path
,
1443 flags
, mode
, &fs_handle
, expect_no_file
);
1444 pthread_mutex_unlock(&chunk
->lock
);
1446 if (status
== LTTNG_TRACE_CHUNK_STATUS_OK
) {
1447 *out_fd
= fs_handle_get_fd(fs_handle
);
1449 * Does not close the fd; we just "unbox" it from the fs_handle.
1451 fs_handle_untracked_destroy(container_of(
1452 fs_handle
, struct fs_handle_untracked
, parent
));
1459 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
1460 const char *file_path
)
1463 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1465 DBG("Unlinking trace chunk file \"%s\"", file_path
);
1466 pthread_mutex_lock(&chunk
->lock
);
1467 if (!chunk
->credentials
.is_set
) {
1469 * Fatal error, credentials must be set before a
1472 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1474 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1477 if (!chunk
->chunk_directory
) {
1478 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1480 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1483 ret
= lttng_directory_handle_unlink_file_as_user(
1484 chunk
->chunk_directory
, file_path
,
1485 chunk
->credentials
.value
.use_current_user
?
1486 NULL
: &chunk
->credentials
.value
.user
);
1488 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1491 lttng_trace_chunk_remove_file(chunk
, file_path
);
1493 pthread_mutex_unlock(&chunk
->lock
);
1498 int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk
*chunk
,
1502 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1504 DBG("Recursively removing trace chunk directory \"%s\"", path
);
1505 pthread_mutex_lock(&chunk
->lock
);
1506 if (!chunk
->credentials
.is_set
) {
1508 * Fatal error, credentials must be set before a
1509 * directory is removed.
1511 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1513 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1516 if (!chunk
->chunk_directory
) {
1517 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1519 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1522 ret
= lttng_directory_handle_remove_subdirectory_recursive_as_user(
1523 chunk
->chunk_directory
, path
,
1524 chunk
->credentials
.value
.use_current_user
?
1525 NULL
: &chunk
->credentials
.value
.user
,
1526 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
);
1528 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
1532 pthread_mutex_unlock(&chunk
->lock
);
1537 int lttng_trace_chunk_move_to_completed_post_release(
1538 struct lttng_trace_chunk
*trace_chunk
)
1541 char *archived_chunk_name
= NULL
;
1542 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
1543 const time_t creation_timestamp
=
1544 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
1545 const time_t close_timestamp
=
1546 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
1547 struct lttng_directory_handle
*archived_chunks_directory
= NULL
;
1548 enum lttng_trace_chunk_status status
;
1550 if (!trace_chunk
->mode
.is_set
||
1551 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
1552 !trace_chunk
->session_output_directory
) {
1554 * This command doesn't need to run if the output is remote
1555 * or if the trace chunk is not owned by this process.
1560 assert(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
1561 assert(!trace_chunk
->name_overridden
);
1562 assert(trace_chunk
->path
);
1564 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
1566 if (!archived_chunk_name
) {
1567 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1572 ret
= lttng_directory_handle_create_subdirectory_as_user(
1573 trace_chunk
->session_output_directory
,
1574 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1576 !trace_chunk
->credentials
.value
.use_current_user
?
1577 &trace_chunk
->credentials
.value
.user
:
1580 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1581 "\" directory for archived trace chunks");
1585 archived_chunks_directory
= lttng_directory_handle_create_from_handle(
1586 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1587 trace_chunk
->session_output_directory
);
1588 if (!archived_chunks_directory
) {
1589 PERROR("Failed to get handle to archived trace chunks directory");
1595 * Make sure chunk is renamed to old directory if not already done by
1596 * the creation of the next chunk. This happens if a rotation is
1597 * performed while tracing is stopped.
1599 if (!trace_chunk
->path
|| strcmp(trace_chunk
->path
,
1600 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
)) {
1601 status
= lttng_trace_chunk_rename_path_no_lock(trace_chunk
,
1602 DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1603 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1604 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY
);
1610 ret
= lttng_directory_handle_rename_as_user(
1611 trace_chunk
->session_output_directory
,
1613 archived_chunks_directory
,
1614 archived_chunk_name
,
1615 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1617 &trace_chunk
->credentials
.value
.user
);
1619 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1621 archived_chunk_name
);
1625 lttng_directory_handle_put(archived_chunks_directory
);
1626 free(archived_chunk_name
);
1631 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk
*trace_chunk
)
1637 int lttng_trace_chunk_delete_post_release_user(
1638 struct lttng_trace_chunk
*trace_chunk
)
1642 DBG("Trace chunk \"delete\" close command post-release (User)");
1644 /* Unlink all files. */
1645 while (lttng_dynamic_pointer_array_get_count(&trace_chunk
->files
) != 0) {
1646 enum lttng_trace_chunk_status status
;
1650 path
= lttng_dynamic_pointer_array_get_pointer(
1651 &trace_chunk
->files
, 0);
1652 DBG("Unlink file: %s", path
);
1653 status
= lttng_trace_chunk_unlink_file(trace_chunk
, path
);
1654 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1655 ERR("Error unlinking file '%s' when deleting chunk", path
);
1665 int lttng_trace_chunk_delete_post_release_owner(
1666 struct lttng_trace_chunk
*trace_chunk
)
1668 enum lttng_trace_chunk_status status
;
1672 ret
= lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1677 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1679 assert(trace_chunk
->session_output_directory
);
1680 assert(trace_chunk
->chunk_directory
);
1682 /* Remove empty directories. */
1683 count
= lttng_dynamic_pointer_array_get_count(
1684 &trace_chunk
->top_level_directories
);
1686 for (i
= 0; i
< count
; i
++) {
1687 const char *top_level_name
=
1688 lttng_dynamic_pointer_array_get_pointer(
1689 &trace_chunk
->top_level_directories
, i
);
1691 status
= lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk
, top_level_name
);
1692 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1693 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1700 lttng_directory_handle_put(trace_chunk
->chunk_directory
);
1701 trace_chunk
->chunk_directory
= NULL
;
1703 if (trace_chunk
->path
&& trace_chunk
->path
[0] != '\0') {
1704 status
= lttng_directory_handle_remove_subdirectory(
1705 trace_chunk
->session_output_directory
,
1707 if (status
!= LTTNG_TRACE_CHUNK_STATUS_OK
) {
1708 ERR("Error removing subdirectory '%s' file when deleting chunk",
1714 free(trace_chunk
->path
);
1715 trace_chunk
->path
= NULL
;
1721 * For local files, session and consumer daemons all run the delete hook. The
1722 * consumer daemons have the list of files to unlink, and technically the
1723 * session daemon is the owner of the chunk. Unlink all files owned by each
1727 int lttng_trace_chunk_delete_post_release(
1728 struct lttng_trace_chunk
*trace_chunk
)
1730 if (!trace_chunk
->chunk_directory
) {
1734 if (trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
) {
1735 return lttng_trace_chunk_delete_post_release_owner(trace_chunk
);
1737 return lttng_trace_chunk_delete_post_release_user(trace_chunk
);
1742 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
1743 struct lttng_trace_chunk
*chunk
,
1744 enum lttng_trace_chunk_command_type
*command_type
)
1746 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1748 pthread_mutex_lock(&chunk
->lock
);
1749 if (chunk
->close_command
.is_set
) {
1750 *command_type
= chunk
->close_command
.value
;
1751 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1753 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1755 pthread_mutex_unlock(&chunk
->lock
);
1760 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
1761 struct lttng_trace_chunk
*chunk
,
1762 enum lttng_trace_chunk_command_type close_command
)
1764 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1766 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1767 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1768 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1772 pthread_mutex_lock(&chunk
->lock
);
1773 if (chunk
->close_command
.is_set
) {
1774 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1775 close_command_names
[chunk
->close_command
.value
],
1776 close_command_names
[close_command
]);
1778 DBG("Setting trace chunk close command to \"%s\"",
1779 close_command_names
[close_command
]);
1782 * Unset close command for no-op for backward compatibility with relayd
1785 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1786 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1788 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1790 pthread_mutex_unlock(&chunk
->lock
);
1796 const char *lttng_trace_chunk_command_type_get_name(
1797 enum lttng_trace_chunk_command_type command
)
1800 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1801 return "move to completed trace chunk folder";
1802 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1803 return "no operation";
1804 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1812 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1814 return urcu_ref_get_unless_zero(&chunk
->ref
);
1818 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1820 struct lttng_trace_chunk_registry_element
*element
=
1821 container_of(node
, typeof(*element
), rcu_node
);
1823 lttng_trace_chunk_fini(&element
->chunk
);
1828 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1830 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1833 if (chunk
->close_command
.is_set
) {
1834 if (close_command_post_release_funcs
[
1835 chunk
->close_command
.value
](chunk
)) {
1836 ERR("Trace chunk post-release command %s has failed.",
1837 close_command_names
[chunk
->close_command
.value
]);
1841 if (chunk
->in_registry_element
) {
1842 struct lttng_trace_chunk_registry_element
*element
;
1844 element
= container_of(chunk
, typeof(*element
), chunk
);
1845 if (element
->registry
) {
1847 cds_lfht_del(element
->registry
->ht
,
1848 &element
->trace_chunk_registry_ht_node
);
1850 call_rcu(&element
->rcu_node
,
1851 free_lttng_trace_chunk_registry_element
);
1853 /* Never published, can be free'd immediately. */
1854 free_lttng_trace_chunk_registry_element(
1855 &element
->rcu_node
);
1858 /* Not RCU-protected, free immediately. */
1859 lttng_trace_chunk_fini(chunk
);
1865 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1870 assert(chunk
->ref
.refcount
);
1871 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1875 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1877 struct lttng_trace_chunk_registry
*registry
;
1879 registry
= zmalloc(sizeof(*registry
));
1884 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1885 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1886 if (!registry
->ht
) {
1892 lttng_trace_chunk_registry_destroy(registry
);
1897 void lttng_trace_chunk_registry_destroy(
1898 struct lttng_trace_chunk_registry
*registry
)
1904 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1911 struct lttng_trace_chunk_registry_element
*
1912 lttng_trace_chunk_registry_element_create_from_chunk(
1913 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1915 struct lttng_trace_chunk_registry_element
*element
=
1916 zmalloc(sizeof(*element
));
1921 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1922 element
->session_id
= session_id
;
1924 element
->chunk
= *chunk
;
1925 lttng_trace_chunk_init(&element
->chunk
);
1926 if (chunk
->session_output_directory
) {
1927 /* Transferred ownership. */
1928 element
->chunk
.session_output_directory
=
1929 chunk
->session_output_directory
;
1930 chunk
->session_output_directory
= NULL
;
1932 if (chunk
->chunk_directory
) {
1933 /* Transferred ownership. */
1934 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1935 chunk
->chunk_directory
= NULL
;
1938 * The original chunk becomes invalid; the name and path attributes are
1939 * transferred to the new chunk instance.
1943 element
->chunk
.fd_tracker
= chunk
->fd_tracker
;
1944 element
->chunk
.in_registry_element
= true;
1950 struct lttng_trace_chunk
*
1951 lttng_trace_chunk_registry_publish_chunk(
1952 struct lttng_trace_chunk_registry
*registry
,
1953 uint64_t session_id
, struct lttng_trace_chunk
*chunk
)
1955 struct lttng_trace_chunk_registry_element
*element
;
1956 unsigned long element_hash
;
1958 pthread_mutex_lock(&chunk
->lock
);
1959 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
1961 pthread_mutex_unlock(&chunk
->lock
);
1966 * chunk is now invalid, the only valid operation is a 'put' from the
1970 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1974 struct cds_lfht_node
*published_node
;
1975 struct lttng_trace_chunk
*published_chunk
;
1976 struct lttng_trace_chunk_registry_element
*published_element
;
1978 published_node
= cds_lfht_add_unique(registry
->ht
,
1980 lttng_trace_chunk_registry_element_match
,
1982 &element
->trace_chunk_registry_ht_node
);
1983 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
1984 /* Successfully published the new element. */
1985 element
->registry
= registry
;
1986 /* Acquire a reference for the caller. */
1987 if (lttng_trace_chunk_get(&element
->chunk
)) {
1991 * Another thread concurrently unpublished the
1992 * trace chunk. This is currently unexpected.
1994 * Re-attempt to publish.
1996 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
2002 * An equivalent trace chunk was published before this trace
2003 * chunk. Attempt to acquire a reference to the one that was
2004 * already published and release the reference to the copy we
2005 * created if successful.
2007 published_element
= container_of(published_node
,
2008 typeof(*published_element
),
2009 trace_chunk_registry_ht_node
);
2010 published_chunk
= &published_element
->chunk
;
2011 if (lttng_trace_chunk_get(published_chunk
)) {
2012 lttng_trace_chunk_put(&element
->chunk
);
2013 element
= published_element
;
2017 * A reference to the previously published trace chunk could not
2018 * be acquired. Hence, retry to publish our copy of the trace
2024 return element
? &element
->chunk
: NULL
;
2028 * Note that the caller must be registered as an RCU thread.
2029 * However, it does not need to hold the RCU read lock. The RCU read lock is
2030 * acquired to perform the look-up in the registry's hash table and held until
2031 * after a reference to the "found" trace chunk is acquired.
2033 * IOW, holding a reference guarantees the existence of the object for the
2037 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
2038 const struct lttng_trace_chunk_registry
*registry
,
2039 uint64_t session_id
, uint64_t *chunk_id
)
2041 const struct lttng_trace_chunk_registry_element target_element
= {
2042 .chunk
.id
.is_set
= !!chunk_id
,
2043 .chunk
.id
.value
= chunk_id
? *chunk_id
: 0,
2044 .session_id
= session_id
,
2046 const unsigned long element_hash
=
2047 lttng_trace_chunk_registry_element_hash(
2049 struct cds_lfht_node
*published_node
;
2050 struct lttng_trace_chunk_registry_element
*published_element
;
2051 struct lttng_trace_chunk
*published_chunk
= NULL
;
2052 struct cds_lfht_iter iter
;
2055 cds_lfht_lookup(registry
->ht
,
2057 lttng_trace_chunk_registry_element_match
,
2060 published_node
= cds_lfht_iter_get_node(&iter
);
2061 if (!published_node
) {
2065 published_element
= container_of(published_node
,
2066 typeof(*published_element
),
2067 trace_chunk_registry_ht_node
);
2068 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
2069 published_chunk
= &published_element
->chunk
;
2073 return published_chunk
;
2077 struct lttng_trace_chunk
*
2078 lttng_trace_chunk_registry_find_chunk(
2079 const struct lttng_trace_chunk_registry
*registry
,
2080 uint64_t session_id
, uint64_t chunk_id
)
2082 return _lttng_trace_chunk_registry_find_chunk(registry
,
2083 session_id
, &chunk_id
);
2087 int lttng_trace_chunk_registry_chunk_exists(
2088 const struct lttng_trace_chunk_registry
*registry
,
2089 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
2092 const struct lttng_trace_chunk_registry_element target_element
= {
2093 .chunk
.id
.is_set
= true,
2094 .chunk
.id
.value
= chunk_id
,
2095 .session_id
= session_id
,
2097 const unsigned long element_hash
=
2098 lttng_trace_chunk_registry_element_hash(
2100 struct cds_lfht_node
*published_node
;
2101 struct cds_lfht_iter iter
;
2104 cds_lfht_lookup(registry
->ht
,
2106 lttng_trace_chunk_registry_element_match
,
2109 published_node
= cds_lfht_iter_get_node(&iter
);
2110 if (!published_node
) {
2111 *chunk_exists
= false;
2115 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
2122 struct lttng_trace_chunk
*
2123 lttng_trace_chunk_registry_find_anonymous_chunk(
2124 const struct lttng_trace_chunk_registry
*registry
,
2125 uint64_t session_id
)
2127 return _lttng_trace_chunk_registry_find_chunk(registry
,
2132 unsigned int lttng_trace_chunk_registry_put_each_chunk(
2133 const struct lttng_trace_chunk_registry
*registry
)
2135 struct cds_lfht_iter iter
;
2136 struct lttng_trace_chunk_registry_element
*chunk_element
;
2137 unsigned int trace_chunks_left
= 0;
2139 DBG("Releasing trace chunk registry to all trace chunks");
2141 cds_lfht_for_each_entry(registry
->ht
,
2142 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
2143 const char *chunk_id_str
= "none";
2144 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
2146 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
2147 if (chunk_element
->chunk
.id
.is_set
) {
2150 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
2152 chunk_element
->chunk
.id
.value
);
2153 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
2154 chunk_id_str
= "formatting error";
2156 chunk_id_str
= chunk_id_buf
;
2160 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
2161 "chunk_id = %s, name = \"%s\", status = %s",
2162 chunk_element
->session_id
,
2164 chunk_element
->chunk
.name
? : "none",
2165 chunk_element
->chunk
.close_command
.is_set
?
2167 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
2168 lttng_trace_chunk_put(&chunk_element
->chunk
);
2169 trace_chunks_left
++;
2172 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
2175 return trace_chunks_left
;