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 void (*chunk_close_command
)(struct lttng_trace_chunk
*trace_chunk
);
60 /* Move a completed trace chunk to the 'completed' trace archive folder. */
62 void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk
*trace_chunk
);
64 struct chunk_credentials
{
65 bool use_current_user
;
66 struct lttng_credentials user
;
69 /* NOTE: Make sure to update lttng_trace_chunk_copy if you modify this. */
70 struct lttng_trace_chunk
{
73 LTTNG_OPTIONAL(enum trace_chunk_mode
) mode
;
75 * First-level directories created within the trace chunk.
76 * Elements are of type 'char *'.
78 * Only used by _owner_ mode chunks.
80 struct lttng_dynamic_pointer_array top_level_directories
;
81 /* Is contained within an lttng_trace_chunk_registry_element? */
82 bool in_registry_element
;
85 /* An unset id means the chunk is anonymous. */
86 LTTNG_OPTIONAL(uint64_t) id
;
87 LTTNG_OPTIONAL(time_t) timestamp_creation
;
88 LTTNG_OPTIONAL(time_t) timestamp_close
;
89 LTTNG_OPTIONAL(struct chunk_credentials
) credentials
;
90 struct lttng_directory_handle
*session_output_directory
;
91 struct lttng_directory_handle
*chunk_directory
;
92 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type
) close_command
;
95 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
96 struct lttng_trace_chunk_registry_element
{
97 struct lttng_trace_chunk chunk
;
99 /* Weak and only set when added. */
100 struct lttng_trace_chunk_registry
*registry
;
101 struct cds_lfht_node trace_chunk_registry_ht_node
;
102 /* call_rcu delayed reclaim. */
103 struct rcu_head rcu_node
;
106 struct lttng_trace_chunk_registry
{
111 char *close_command_names
[] = {
112 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
113 "move to completed chunk folder",
114 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
] =
116 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
] =
121 chunk_close_command close_command_funcs
[] = {
122 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
123 lttng_trace_chunk_move_to_completed
,
127 bool lttng_trace_chunk_registry_element_equals(
128 const struct lttng_trace_chunk_registry_element
*a
,
129 const struct lttng_trace_chunk_registry_element
*b
)
131 if (a
->session_id
!= b
->session_id
) {
134 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
137 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
146 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
,
149 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
151 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
152 element_b
= caa_container_of(node
, typeof(*element_b
),
153 trace_chunk_registry_ht_node
);
154 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
158 unsigned long lttng_trace_chunk_registry_element_hash(
159 const struct lttng_trace_chunk_registry_element
*element
)
161 unsigned long hash
= hash_key_u64(&element
->session_id
,
164 if (element
->chunk
.id
.is_set
) {
165 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
172 char *generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
,
173 const time_t *close_timestamp
)
176 char *new_name
= NULL
;
177 char start_datetime
[ISO8601_STR_LEN
] = {};
178 /* Add 1 for a '-' prefix. */
179 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
181 ret
= time_to_iso8601_str(
183 start_datetime
, sizeof(start_datetime
));
185 ERR("Failed to format trace chunk start date time");
188 if (close_timestamp
) {
189 *end_datetime_suffix
= '-';
190 ret
= time_to_iso8601_str(
192 end_datetime_suffix
+ 1,
193 sizeof(end_datetime_suffix
) - 1);
195 ERR("Failed to format trace chunk end date time");
199 new_name
= zmalloc(GENERATED_CHUNK_NAME_LEN
);
201 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
204 ret
= snprintf(new_name
, GENERATED_CHUNK_NAME_LEN
, "%s%s-%" PRIu64
,
205 start_datetime
, end_datetime_suffix
, chunk_id
);
206 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
207 ERR("Failed to format trace chunk name");
218 void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
220 urcu_ref_init(&chunk
->ref
);
221 pthread_mutex_init(&chunk
->lock
, NULL
);
222 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
226 void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
228 if (chunk
->session_output_directory
) {
229 lttng_directory_handle_put(
230 chunk
->session_output_directory
);
231 chunk
->session_output_directory
= NULL
;
233 if (chunk
->chunk_directory
) {
234 lttng_directory_handle_put(chunk
->chunk_directory
);
235 chunk
->chunk_directory
= NULL
;
239 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
240 pthread_mutex_destroy(&chunk
->lock
);
244 struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
246 struct lttng_trace_chunk
*chunk
= NULL
;
248 chunk
= zmalloc(sizeof(*chunk
));
250 ERR("Failed to allocate trace chunk");
253 lttng_trace_chunk_init(chunk
);
259 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
261 DBG("Creating anonymous trace chunk");
262 return lttng_trace_chunk_allocate();
266 struct lttng_trace_chunk
*lttng_trace_chunk_create(
267 uint64_t chunk_id
, time_t chunk_creation_time
)
269 struct lttng_trace_chunk
*chunk
;
270 char chunk_creation_datetime_buf
[16] = {};
271 const char *chunk_creation_datetime_str
= "(formatting error)";
272 struct tm timeinfo_buf
, *timeinfo
;
274 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
278 /* Don't fail because of this; it is only used for logging. */
279 strftime_ret
= strftime(chunk_creation_datetime_buf
,
280 sizeof(chunk_creation_datetime_buf
),
281 "%Y%m%d-%H%M%S", timeinfo
);
283 chunk_creation_datetime_str
=
284 chunk_creation_datetime_buf
;
288 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
289 chunk_id
, chunk_creation_datetime_str
);
290 chunk
= lttng_trace_chunk_allocate();
295 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
296 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
298 chunk
->name
= generate_chunk_name(chunk_id
,
299 chunk_creation_time
, NULL
);
301 ERR("Failed to allocate trace chunk name storage");
306 DBG("Chunk name set to \"%s\"", chunk
->name
? : "(none)");
310 lttng_trace_chunk_put(chunk
);
315 struct lttng_trace_chunk
*lttng_trace_chunk_copy(
316 struct lttng_trace_chunk
*source_chunk
)
318 struct lttng_trace_chunk
*new_chunk
= lttng_trace_chunk_allocate();
324 pthread_mutex_lock(&source_chunk
->lock
);
326 * A new chunk is always a user; it shall create no new trace
329 new_chunk
->mode
= (typeof(new_chunk
->mode
)) {
331 .value
= TRACE_CHUNK_MODE_USER
,
334 * top_level_directories is not copied as it is never used
335 * by _user_ mode chunks.
337 /* The new chunk is not part of a registry (yet, at least). */
338 new_chunk
->in_registry_element
= false;
339 new_chunk
->name_overridden
= source_chunk
->name_overridden
;
340 if (source_chunk
->name
) {
341 new_chunk
->name
= strdup(source_chunk
->name
);
342 if (!new_chunk
->name
) {
343 ERR("Failed to copy source trace chunk name in %s()",
348 new_chunk
->id
= source_chunk
->id
;
349 new_chunk
->timestamp_creation
= source_chunk
->timestamp_creation
;
350 new_chunk
->timestamp_close
= source_chunk
->timestamp_close
;
351 new_chunk
->credentials
= source_chunk
->credentials
;
352 if (source_chunk
->session_output_directory
) {
353 const bool reference_acquired
= lttng_directory_handle_get(
354 source_chunk
->session_output_directory
);
356 assert(reference_acquired
);
357 new_chunk
->session_output_directory
=
358 source_chunk
->session_output_directory
;
360 if (source_chunk
->chunk_directory
) {
361 const bool reference_acquired
= lttng_directory_handle_get(
362 source_chunk
->chunk_directory
);
364 assert(reference_acquired
);
365 new_chunk
->chunk_directory
= source_chunk
->chunk_directory
;
367 new_chunk
->close_command
= source_chunk
->close_command
;
368 pthread_mutex_unlock(&source_chunk
->lock
);
372 pthread_mutex_unlock(&source_chunk
->lock
);
373 lttng_trace_chunk_put(new_chunk
);
378 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(
379 struct lttng_trace_chunk
*chunk
, uint64_t *id
)
381 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
383 pthread_mutex_lock(&chunk
->lock
);
384 if (chunk
->id
.is_set
) {
385 *id
= chunk
->id
.value
;
387 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
389 pthread_mutex_unlock(&chunk
->lock
);
394 enum lttng_trace_chunk_status
lttng_trace_chunk_get_creation_timestamp(
395 struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
398 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
400 pthread_mutex_lock(&chunk
->lock
);
401 if (chunk
->timestamp_creation
.is_set
) {
402 *creation_ts
= chunk
->timestamp_creation
.value
;
404 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
406 pthread_mutex_unlock(&chunk
->lock
);
411 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_timestamp(
412 struct lttng_trace_chunk
*chunk
, time_t *close_ts
)
414 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
416 pthread_mutex_lock(&chunk
->lock
);
417 if (chunk
->timestamp_close
.is_set
) {
418 *close_ts
= chunk
->timestamp_close
.value
;
420 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
422 pthread_mutex_unlock(&chunk
->lock
);
427 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(
428 struct lttng_trace_chunk
*chunk
, time_t close_ts
)
430 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
432 pthread_mutex_lock(&chunk
->lock
);
433 if (!chunk
->timestamp_creation
.is_set
) {
434 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
435 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
438 if (chunk
->timestamp_creation
.value
> close_ts
) {
439 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
440 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
443 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
444 if (!chunk
->name_overridden
) {
446 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
447 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
450 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
454 pthread_mutex_unlock(&chunk
->lock
);
459 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(
460 struct lttng_trace_chunk
*chunk
, const char **name
,
461 bool *name_overridden
)
463 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
465 pthread_mutex_lock(&chunk
->lock
);
466 if (name_overridden
) {
467 *name_overridden
= chunk
->name_overridden
;
470 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
475 pthread_mutex_unlock(&chunk
->lock
);
480 bool is_valid_chunk_name(const char *name
)
488 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
489 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
493 if (strchr(name
, '/') || strchr(name
, '.')) {
501 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
502 struct lttng_trace_chunk
*chunk
, const char *name
)
506 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
508 if (!is_valid_chunk_name(name
)) {
509 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
511 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
515 pthread_mutex_lock(&chunk
->lock
);
516 if (!chunk
->id
.is_set
) {
517 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
519 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
522 new_name
= strdup(name
);
524 ERR("Failed to allocate new trace chunk name");
525 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
529 chunk
->name
= new_name
;
530 chunk
->name_overridden
= true;
532 pthread_mutex_unlock(&chunk
->lock
);
538 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
539 struct lttng_trace_chunk
*chunk
,
540 struct lttng_credentials
*credentials
)
542 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
544 pthread_mutex_lock(&chunk
->lock
);
545 if (chunk
->credentials
.is_set
) {
546 if (chunk
->credentials
.value
.use_current_user
) {
547 credentials
->uid
= geteuid();
548 credentials
->gid
= getegid();
550 *credentials
= chunk
->credentials
.value
.user
;
553 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
555 pthread_mutex_unlock(&chunk
->lock
);
560 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
561 struct lttng_trace_chunk
*chunk
,
562 const struct lttng_credentials
*user_credentials
)
564 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
565 const struct chunk_credentials credentials
= {
566 .user
= *user_credentials
,
567 .use_current_user
= false,
570 pthread_mutex_lock(&chunk
->lock
);
571 if (chunk
->credentials
.is_set
) {
572 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
575 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
577 pthread_mutex_unlock(&chunk
->lock
);
582 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
583 struct lttng_trace_chunk
*chunk
)
585 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
586 const struct chunk_credentials credentials
= {
587 .use_current_user
= true,
590 pthread_mutex_lock(&chunk
->lock
);
591 if (chunk
->credentials
.is_set
) {
592 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
595 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
597 pthread_mutex_unlock(&chunk
->lock
);
603 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
604 struct lttng_trace_chunk
*chunk
,
605 struct lttng_directory_handle
*session_output_directory
)
608 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
609 struct lttng_directory_handle
*chunk_directory_handle
= NULL
;
610 bool reference_acquired
;
612 pthread_mutex_lock(&chunk
->lock
);
613 if (chunk
->mode
.is_set
) {
614 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
617 if (!chunk
->credentials
.is_set
) {
619 * Fatal error, credentials must be set before a
620 * directory is created.
622 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
623 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
629 * A nameless chunk does not need its own output directory.
630 * The session's output directory will be used.
632 ret
= lttng_directory_handle_create_subdirectory_as_user(
633 session_output_directory
,
636 !chunk
->credentials
.value
.use_current_user
?
637 &chunk
->credentials
.value
.user
: NULL
);
639 PERROR("Failed to create chunk output directory \"%s\"",
641 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
645 chunk_directory_handle
= lttng_directory_handle_create_from_handle(
647 session_output_directory
);
648 if (!chunk_directory_handle
) {
649 /* The function already logs on all error paths. */
650 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
653 chunk
->chunk_directory
= chunk_directory_handle
;
654 chunk_directory_handle
= NULL
;
655 reference_acquired
= lttng_directory_handle_get(
656 session_output_directory
);
657 assert(reference_acquired
);
658 chunk
->session_output_directory
= session_output_directory
;
659 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
661 pthread_mutex_unlock(&chunk
->lock
);
666 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
667 struct lttng_trace_chunk
*chunk
,
668 struct lttng_directory_handle
*chunk_directory
)
670 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
671 bool reference_acquired
;
673 pthread_mutex_lock(&chunk
->lock
);
674 if (chunk
->mode
.is_set
) {
675 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
678 if (!chunk
->credentials
.is_set
) {
679 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
680 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
683 reference_acquired
= lttng_directory_handle_get(chunk_directory
);
684 assert(reference_acquired
);
685 chunk
->chunk_directory
= chunk_directory
;
686 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
688 pthread_mutex_unlock(&chunk
->lock
);
693 enum lttng_trace_chunk_status
lttng_trace_chunk_borrow_chunk_directory_handle(
694 struct lttng_trace_chunk
*chunk
,
695 const struct lttng_directory_handle
**handle
)
697 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
699 pthread_mutex_lock(&chunk
->lock
);
700 if (!chunk
->chunk_directory
) {
701 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
705 *handle
= chunk
->chunk_directory
;
707 pthread_mutex_unlock(&chunk
->lock
);
711 /* Add a top-level directory to the trace chunk if it was previously unknown. */
713 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
714 const char *new_path
)
718 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
719 &chunk
->top_level_directories
);
720 const char *new_path_separator_pos
= strchr(new_path
, '/');
721 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
722 new_path_separator_pos
- new_path
: strlen(new_path
);
724 for (i
= 0; i
< count
; i
++) {
725 const char *path
= lttng_dynamic_pointer_array_get_pointer(
726 &chunk
->top_level_directories
, i
);
727 const ptrdiff_t path_top_level_len
= strlen(path
);
729 if (path_top_level_len
!= new_path_top_level_len
) {
732 if (!strncmp(path
, new_path
, path_top_level_len
)) {
739 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
741 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
742 new_path
, chunk
->name
? : "(unnamed)");
744 PERROR("Failed to copy path");
748 ret
= lttng_dynamic_pointer_array_add_pointer(
749 &chunk
->top_level_directories
, copy
);
751 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
761 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
762 struct lttng_trace_chunk
*chunk
,
766 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
768 DBG("Creating trace chunk subdirectory \"%s\"", path
);
769 pthread_mutex_lock(&chunk
->lock
);
770 if (!chunk
->credentials
.is_set
) {
772 * Fatal error, credentials must be set before a
773 * directory is created.
775 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
777 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
780 if (!chunk
->mode
.is_set
||
781 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
782 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
784 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
787 if (!chunk
->chunk_directory
) {
788 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
790 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
794 ERR("Refusing to create absolute trace chunk directory \"%s\"",
796 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
799 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
800 chunk
->chunk_directory
, path
,
802 chunk
->credentials
.value
.use_current_user
?
803 NULL
: &chunk
->credentials
.value
.user
);
805 PERROR("Failed to create trace chunk subdirectory \"%s\"",
807 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
810 ret
= add_top_level_directory_unique(chunk
, path
);
812 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
816 pthread_mutex_unlock(&chunk
->lock
);
821 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
822 struct lttng_trace_chunk
*chunk
, const char *file_path
,
823 int flags
, mode_t mode
, int *out_fd
, bool expect_no_file
)
826 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
828 DBG("Opening trace chunk file \"%s\"", file_path
);
829 pthread_mutex_lock(&chunk
->lock
);
830 if (!chunk
->credentials
.is_set
) {
832 * Fatal error, credentials must be set before a
835 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
837 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
840 if (!chunk
->chunk_directory
) {
841 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
843 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
846 ret
= lttng_directory_handle_open_file_as_user(
847 chunk
->chunk_directory
, file_path
, flags
, mode
,
848 chunk
->credentials
.value
.use_current_user
?
849 NULL
: &chunk
->credentials
.value
.user
);
851 if (errno
== ENOENT
&& expect_no_file
) {
852 status
= LTTNG_TRACE_CHUNK_STATUS_NO_FILE
;
854 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
855 file_path
, flags
, (int) mode
);
856 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
862 pthread_mutex_unlock(&chunk
->lock
);
867 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
868 const char *file_path
)
871 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
873 DBG("Unlinking trace chunk file \"%s\"", file_path
);
874 pthread_mutex_lock(&chunk
->lock
);
875 if (!chunk
->credentials
.is_set
) {
877 * Fatal error, credentials must be set before a
878 * directory is created.
880 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
882 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
885 if (!chunk
->chunk_directory
) {
886 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
888 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
891 ret
= lttng_directory_handle_unlink_file_as_user(
892 chunk
->chunk_directory
, file_path
,
893 chunk
->credentials
.value
.use_current_user
?
894 NULL
: &chunk
->credentials
.value
.user
);
896 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
900 pthread_mutex_unlock(&chunk
->lock
);
905 void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk
*trace_chunk
)
908 char *directory_to_rename
= NULL
;
909 bool free_directory_to_rename
= false;
910 char *archived_chunk_name
= NULL
;
911 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
912 const time_t creation_timestamp
=
913 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
914 const time_t close_timestamp
=
915 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
916 struct lttng_directory_handle
*archived_chunks_directory
= NULL
;
918 if (!trace_chunk
->mode
.is_set
||
919 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
920 !trace_chunk
->session_output_directory
) {
922 * This command doesn't need to run if the output is remote
923 * or if the trace chunk is not owned by this process.
928 assert(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
929 assert(!trace_chunk
->name_overridden
);
932 * The fist trace chunk of a session is directly output to the
933 * session's output folder. In this case, the top level directories
934 * must be moved to a temporary folder before that temporary directory
935 * is renamed to match the chunk's name.
938 struct lttng_directory_handle
*temporary_rename_directory
=
940 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
941 &trace_chunk
->top_level_directories
);
943 ret
= lttng_directory_handle_create_subdirectory_as_user(
944 trace_chunk
->session_output_directory
,
945 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
,
947 !trace_chunk
->credentials
.value
.use_current_user
?
948 &trace_chunk
->credentials
.value
.user
: NULL
);
950 PERROR("Failed to create temporary trace chunk rename directory \"%s\"",
951 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
);
954 temporary_rename_directory
= lttng_directory_handle_create_from_handle(
955 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
,
956 trace_chunk
->session_output_directory
);
957 if (!temporary_rename_directory
) {
958 ERR("Failed to get handle to temporary trace chunk rename directory");
962 for (i
= 0; i
< count
; i
++) {
963 const char *top_level_name
=
964 lttng_dynamic_pointer_array_get_pointer(
965 &trace_chunk
->top_level_directories
, i
);
967 ret
= lttng_directory_handle_rename_as_user(
968 trace_chunk
->session_output_directory
,
970 temporary_rename_directory
,
972 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
974 &trace_chunk
->credentials
.value
.user
);
976 PERROR("Failed to move \"%s\" to temporary trace chunk rename directory",
978 lttng_directory_handle_put(
979 temporary_rename_directory
);
983 lttng_directory_handle_put(temporary_rename_directory
);
984 directory_to_rename
= DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
;
985 free_directory_to_rename
= false;
987 directory_to_rename
= generate_chunk_name(chunk_id
,
988 creation_timestamp
, NULL
);
989 if (!directory_to_rename
) {
990 ERR("Failed to generate initial trace chunk name while renaming trace chunk");
993 free_directory_to_rename
= true;
996 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
998 if (!archived_chunk_name
) {
999 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1003 ret
= lttng_directory_handle_create_subdirectory_as_user(
1004 trace_chunk
->session_output_directory
,
1005 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1007 !trace_chunk
->credentials
.value
.use_current_user
?
1008 &trace_chunk
->credentials
.value
.user
:
1011 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1012 "\" directory for archived trace chunks");
1016 archived_chunks_directory
= lttng_directory_handle_create_from_handle(
1017 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1018 trace_chunk
->session_output_directory
);
1019 if (!archived_chunks_directory
) {
1020 PERROR("Failed to get handle to archived trace chunks directory");
1024 ret
= lttng_directory_handle_rename_as_user(
1025 trace_chunk
->session_output_directory
,
1026 directory_to_rename
,
1027 archived_chunks_directory
,
1028 archived_chunk_name
,
1029 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1031 &trace_chunk
->credentials
.value
.user
);
1033 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1034 directory_to_rename
, archived_chunk_name
);
1038 lttng_directory_handle_put(archived_chunks_directory
);
1039 free(archived_chunk_name
);
1040 if (free_directory_to_rename
) {
1041 free(directory_to_rename
);
1046 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
1047 struct lttng_trace_chunk
*chunk
,
1048 enum lttng_trace_chunk_command_type
*command_type
)
1050 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1052 pthread_mutex_lock(&chunk
->lock
);
1053 if (chunk
->close_command
.is_set
) {
1054 *command_type
= chunk
->close_command
.value
;
1055 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1057 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1059 pthread_mutex_unlock(&chunk
->lock
);
1064 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
1065 struct lttng_trace_chunk
*chunk
,
1066 enum lttng_trace_chunk_command_type close_command
)
1068 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1070 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1071 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1072 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1076 pthread_mutex_lock(&chunk
->lock
);
1077 if (chunk
->close_command
.is_set
) {
1078 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1079 close_command_names
[chunk
->close_command
.value
],
1080 close_command_names
[close_command
]);
1082 DBG("Setting trace chunk close command to \"%s\"",
1083 close_command_names
[close_command
]);
1086 * Unset close command for no-op for backward compatibility with relayd
1089 if (close_command
!= LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
) {
1090 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1092 LTTNG_OPTIONAL_UNSET(&chunk
->close_command
);
1094 pthread_mutex_unlock(&chunk
->lock
);
1100 const char *lttng_trace_chunk_command_type_get_name(
1101 enum lttng_trace_chunk_command_type command
)
1104 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1105 return "move to completed trace chunk folder";
1106 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION
:
1107 return "no operation";
1108 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE
:
1116 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1118 return urcu_ref_get_unless_zero(&chunk
->ref
);
1122 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1124 struct lttng_trace_chunk_registry_element
*element
=
1125 container_of(node
, typeof(*element
), rcu_node
);
1127 lttng_trace_chunk_fini(&element
->chunk
);
1132 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1134 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1137 if (chunk
->close_command
.is_set
) {
1138 close_command_funcs
[chunk
->close_command
.value
](chunk
);
1141 if (chunk
->in_registry_element
) {
1142 struct lttng_trace_chunk_registry_element
*element
;
1144 element
= container_of(chunk
, typeof(*element
), chunk
);
1145 if (element
->registry
) {
1147 cds_lfht_del(element
->registry
->ht
,
1148 &element
->trace_chunk_registry_ht_node
);
1150 call_rcu(&element
->rcu_node
,
1151 free_lttng_trace_chunk_registry_element
);
1153 /* Never published, can be free'd immediately. */
1154 free_lttng_trace_chunk_registry_element(
1155 &element
->rcu_node
);
1158 /* Not RCU-protected, free immediately. */
1159 lttng_trace_chunk_fini(chunk
);
1165 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1170 assert(chunk
->ref
.refcount
);
1171 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1175 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1177 struct lttng_trace_chunk_registry
*registry
;
1179 registry
= zmalloc(sizeof(*registry
));
1184 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1185 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1186 if (!registry
->ht
) {
1192 lttng_trace_chunk_registry_destroy(registry
);
1197 void lttng_trace_chunk_registry_destroy(
1198 struct lttng_trace_chunk_registry
*registry
)
1204 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1211 struct lttng_trace_chunk_registry_element
*
1212 lttng_trace_chunk_registry_element_create_from_chunk(
1213 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1215 struct lttng_trace_chunk_registry_element
*element
=
1216 zmalloc(sizeof(*element
));
1221 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1222 element
->session_id
= session_id
;
1224 element
->chunk
= *chunk
;
1225 lttng_trace_chunk_init(&element
->chunk
);
1226 if (chunk
->session_output_directory
) {
1227 /* Transferred ownership. */
1228 element
->chunk
.session_output_directory
=
1229 chunk
->session_output_directory
;
1230 chunk
->session_output_directory
= NULL
;
1232 if (chunk
->chunk_directory
) {
1233 /* Transferred ownership. */
1234 element
->chunk
.chunk_directory
= chunk
->chunk_directory
;
1235 chunk
->chunk_directory
= NULL
;
1238 * The original chunk becomes invalid; the name attribute is transferred
1239 * to the new chunk instance.
1242 element
->chunk
.in_registry_element
= true;
1248 struct lttng_trace_chunk
*
1249 lttng_trace_chunk_registry_publish_chunk(
1250 struct lttng_trace_chunk_registry
*registry
,
1251 uint64_t session_id
, struct lttng_trace_chunk
*chunk
)
1253 struct lttng_trace_chunk_registry_element
*element
;
1254 unsigned long element_hash
;
1256 pthread_mutex_lock(&chunk
->lock
);
1257 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
1259 pthread_mutex_unlock(&chunk
->lock
);
1264 * chunk is now invalid, the only valid operation is a 'put' from the
1268 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1272 struct cds_lfht_node
*published_node
;
1273 struct lttng_trace_chunk
*published_chunk
;
1274 struct lttng_trace_chunk_registry_element
*published_element
;
1276 published_node
= cds_lfht_add_unique(registry
->ht
,
1278 lttng_trace_chunk_registry_element_match
,
1280 &element
->trace_chunk_registry_ht_node
);
1281 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
1282 /* Successfully published the new element. */
1283 element
->registry
= registry
;
1284 /* Acquire a reference for the caller. */
1285 if (lttng_trace_chunk_get(&element
->chunk
)) {
1289 * Another thread concurrently unpublished the
1290 * trace chunk. This is currently unexpected.
1292 * Re-attempt to publish.
1294 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1300 * An equivalent trace chunk was published before this trace
1301 * chunk. Attempt to acquire a reference to the one that was
1302 * already published and release the reference to the copy we
1303 * created if successful.
1305 published_element
= container_of(published_node
,
1306 typeof(*published_element
),
1307 trace_chunk_registry_ht_node
);
1308 published_chunk
= &published_element
->chunk
;
1309 if (lttng_trace_chunk_get(published_chunk
)) {
1310 lttng_trace_chunk_put(&element
->chunk
);
1311 element
= published_element
;
1315 * A reference to the previously published trace chunk could not
1316 * be acquired. Hence, retry to publish our copy of the trace
1322 return element
? &element
->chunk
: NULL
;
1326 * Note that the caller must be registered as an RCU thread.
1327 * However, it does not need to hold the RCU read lock. The RCU read lock is
1328 * acquired to perform the look-up in the registry's hash table and held until
1329 * after a reference to the "found" trace chunk is acquired.
1331 * IOW, holding a reference guarantees the existence of the object for the
1335 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
1336 const struct lttng_trace_chunk_registry
*registry
,
1337 uint64_t session_id
, uint64_t *chunk_id
)
1339 const struct lttng_trace_chunk_registry_element target_element
= {
1340 .chunk
.id
.is_set
= !!chunk_id
,
1341 .chunk
.id
.value
= chunk_id
? *chunk_id
: 0,
1342 .session_id
= session_id
,
1344 const unsigned long element_hash
=
1345 lttng_trace_chunk_registry_element_hash(
1347 struct cds_lfht_node
*published_node
;
1348 struct lttng_trace_chunk_registry_element
*published_element
;
1349 struct lttng_trace_chunk
*published_chunk
= NULL
;
1350 struct cds_lfht_iter iter
;
1353 cds_lfht_lookup(registry
->ht
,
1355 lttng_trace_chunk_registry_element_match
,
1358 published_node
= cds_lfht_iter_get_node(&iter
);
1359 if (!published_node
) {
1363 published_element
= container_of(published_node
,
1364 typeof(*published_element
),
1365 trace_chunk_registry_ht_node
);
1366 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
1367 published_chunk
= &published_element
->chunk
;
1371 return published_chunk
;
1375 struct lttng_trace_chunk
*
1376 lttng_trace_chunk_registry_find_chunk(
1377 const struct lttng_trace_chunk_registry
*registry
,
1378 uint64_t session_id
, uint64_t chunk_id
)
1380 return _lttng_trace_chunk_registry_find_chunk(registry
,
1381 session_id
, &chunk_id
);
1385 int lttng_trace_chunk_registry_chunk_exists(
1386 const struct lttng_trace_chunk_registry
*registry
,
1387 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
1390 const struct lttng_trace_chunk_registry_element target_element
= {
1391 .chunk
.id
.is_set
= true,
1392 .chunk
.id
.value
= chunk_id
,
1393 .session_id
= session_id
,
1395 const unsigned long element_hash
=
1396 lttng_trace_chunk_registry_element_hash(
1398 struct cds_lfht_node
*published_node
;
1399 struct cds_lfht_iter iter
;
1402 cds_lfht_lookup(registry
->ht
,
1404 lttng_trace_chunk_registry_element_match
,
1407 published_node
= cds_lfht_iter_get_node(&iter
);
1408 if (!published_node
) {
1409 *chunk_exists
= false;
1413 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
1420 struct lttng_trace_chunk
*
1421 lttng_trace_chunk_registry_find_anonymous_chunk(
1422 const struct lttng_trace_chunk_registry
*registry
,
1423 uint64_t session_id
)
1425 return _lttng_trace_chunk_registry_find_chunk(registry
,
1429 unsigned int lttng_trace_chunk_registry_put_each_chunk(
1430 struct lttng_trace_chunk_registry
*registry
)
1432 struct cds_lfht_iter iter
;
1433 struct lttng_trace_chunk_registry_element
*chunk_element
;
1434 unsigned int trace_chunks_left
= 0;
1436 DBG("Releasing trace chunk registry to all trace chunks");
1438 cds_lfht_for_each_entry(registry
->ht
,
1439 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
1440 const char *chunk_id_str
= "none";
1441 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
1443 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
1444 if (chunk_element
->chunk
.id
.is_set
) {
1447 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
1449 chunk_element
->chunk
.id
.value
);
1450 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
1451 chunk_id_str
= "formatting error";
1453 chunk_id_str
= chunk_id_buf
;
1457 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
1458 "chunk_id = %s, name = \"%s\", status = %s",
1459 chunk_element
->session_id
,
1461 chunk_element
->chunk
.name
? : "none",
1462 chunk_element
->chunk
.close_command
.is_set
?
1464 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
1465 lttng_trace_chunk_put(&chunk_element
->chunk
);
1466 trace_chunks_left
++;
1469 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
1472 return trace_chunks_left
;