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 LTTNG_OPTIONAL(struct lttng_directory_handle
) session_output_directory
;
91 LTTNG_OPTIONAL(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",
117 chunk_close_command close_command_funcs
[] = {
118 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
119 lttng_trace_chunk_move_to_completed
,
123 bool lttng_trace_chunk_registry_element_equals(
124 const struct lttng_trace_chunk_registry_element
*a
,
125 const struct lttng_trace_chunk_registry_element
*b
)
127 if (a
->session_id
!= b
->session_id
) {
130 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
133 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
142 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
,
145 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
147 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
148 element_b
= caa_container_of(node
, typeof(*element_b
),
149 trace_chunk_registry_ht_node
);
150 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
154 unsigned long lttng_trace_chunk_registry_element_hash(
155 const struct lttng_trace_chunk_registry_element
*element
)
157 unsigned long hash
= hash_key_u64(&element
->session_id
,
160 if (element
->chunk
.id
.is_set
) {
161 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
168 char *generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
,
169 const time_t *close_timestamp
)
172 char *new_name
= NULL
;
173 char start_datetime
[ISO8601_STR_LEN
] = {};
174 /* Add 1 for a '-' prefix. */
175 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
177 ret
= time_to_iso8601_str(
179 start_datetime
, sizeof(start_datetime
));
181 ERR("Failed to format trace chunk start date time");
184 if (close_timestamp
) {
185 *end_datetime_suffix
= '-';
186 ret
= time_to_iso8601_str(
188 end_datetime_suffix
+ 1,
189 sizeof(end_datetime_suffix
) - 1);
191 ERR("Failed to format trace chunk end date time");
195 new_name
= zmalloc(GENERATED_CHUNK_NAME_LEN
);
197 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
200 ret
= snprintf(new_name
, GENERATED_CHUNK_NAME_LEN
, "%s%s-%" PRIu64
,
201 start_datetime
, end_datetime_suffix
, chunk_id
);
202 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
203 ERR("Failed to format trace chunk name");
214 void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
216 urcu_ref_init(&chunk
->ref
);
217 pthread_mutex_init(&chunk
->lock
, NULL
);
218 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
222 void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
224 if (chunk
->session_output_directory
.is_set
) {
225 lttng_directory_handle_fini(
226 &chunk
->session_output_directory
.value
);
228 if (chunk
->chunk_directory
.is_set
) {
229 lttng_directory_handle_fini(&chunk
->chunk_directory
.value
);
233 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
234 pthread_mutex_destroy(&chunk
->lock
);
238 struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
240 struct lttng_trace_chunk
*chunk
= NULL
;
242 chunk
= zmalloc(sizeof(*chunk
));
244 ERR("Failed to allocate trace chunk");
247 lttng_trace_chunk_init(chunk
);
253 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
255 DBG("Creating anonymous trace chunk");
256 return lttng_trace_chunk_allocate();
260 struct lttng_trace_chunk
*lttng_trace_chunk_create(
261 uint64_t chunk_id
, time_t chunk_creation_time
)
263 struct lttng_trace_chunk
*chunk
;
264 char chunk_creation_datetime_buf
[16] = {};
265 const char *chunk_creation_datetime_str
= "(formatting error)";
266 struct tm timeinfo_buf
, *timeinfo
;
268 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
272 /* Don't fail because of this; it is only used for logging. */
273 strftime_ret
= strftime(chunk_creation_datetime_buf
,
274 sizeof(chunk_creation_datetime_buf
),
275 "%Y%m%d-%H%M%S", timeinfo
);
277 chunk_creation_datetime_str
=
278 chunk_creation_datetime_buf
;
282 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
283 chunk_id
, chunk_creation_datetime_str
);
284 chunk
= lttng_trace_chunk_allocate();
289 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
290 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
292 chunk
->name
= generate_chunk_name(chunk_id
,
293 chunk_creation_time
, NULL
);
295 ERR("Failed to allocate trace chunk name storage");
300 DBG("Chunk name set to \"%s\"", chunk
->name
? : "(none)");
304 lttng_trace_chunk_put(chunk
);
309 struct lttng_trace_chunk
*lttng_trace_chunk_copy(
310 struct lttng_trace_chunk
*source_chunk
)
312 struct lttng_trace_chunk
*new_chunk
= lttng_trace_chunk_allocate();
318 pthread_mutex_lock(&source_chunk
->lock
);
320 * A new chunk is always a user; it shall create no new trace
323 new_chunk
->mode
= (typeof(new_chunk
->mode
)) {
325 .value
= TRACE_CHUNK_MODE_USER
,
328 * top_level_directories is not copied as it is never used
329 * by _user_ mode chunks.
331 /* The new chunk is not part of a registry (yet, at least). */
332 new_chunk
->in_registry_element
= false;
333 new_chunk
->name_overridden
= source_chunk
->name_overridden
;
334 if (source_chunk
->name
) {
335 new_chunk
->name
= strdup(source_chunk
->name
);
336 if (!new_chunk
->name
) {
337 ERR("Failed to copy source trace chunk name in %s()",
342 new_chunk
->id
= source_chunk
->id
;
343 new_chunk
->timestamp_creation
= source_chunk
->timestamp_creation
;
344 new_chunk
->timestamp_close
= source_chunk
->timestamp_close
;
345 new_chunk
->credentials
= source_chunk
->credentials
;
346 if (source_chunk
->session_output_directory
.is_set
) {
347 if (lttng_directory_handle_copy(
348 &source_chunk
->session_output_directory
.value
,
349 &new_chunk
->session_output_directory
.value
)) {
352 new_chunk
->session_output_directory
.is_set
= true;
355 if (source_chunk
->chunk_directory
.is_set
) {
356 if (lttng_directory_handle_copy(
357 &source_chunk
->chunk_directory
.value
,
358 &new_chunk
->chunk_directory
.value
)) {
361 new_chunk
->chunk_directory
.is_set
= true;
364 new_chunk
->close_command
= source_chunk
->close_command
;
365 pthread_mutex_unlock(&source_chunk
->lock
);
369 pthread_mutex_unlock(&source_chunk
->lock
);
370 lttng_trace_chunk_put(new_chunk
);
375 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(
376 struct lttng_trace_chunk
*chunk
, uint64_t *id
)
378 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
380 pthread_mutex_lock(&chunk
->lock
);
381 if (chunk
->id
.is_set
) {
382 *id
= chunk
->id
.value
;
384 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
386 pthread_mutex_unlock(&chunk
->lock
);
391 enum lttng_trace_chunk_status
lttng_trace_chunk_get_creation_timestamp(
392 struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
395 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
397 pthread_mutex_lock(&chunk
->lock
);
398 if (chunk
->timestamp_creation
.is_set
) {
399 *creation_ts
= chunk
->timestamp_creation
.value
;
401 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
403 pthread_mutex_unlock(&chunk
->lock
);
408 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_timestamp(
409 struct lttng_trace_chunk
*chunk
, time_t *close_ts
)
411 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
413 pthread_mutex_lock(&chunk
->lock
);
414 if (chunk
->timestamp_close
.is_set
) {
415 *close_ts
= chunk
->timestamp_close
.value
;
417 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
419 pthread_mutex_unlock(&chunk
->lock
);
424 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(
425 struct lttng_trace_chunk
*chunk
, time_t close_ts
)
427 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
429 pthread_mutex_lock(&chunk
->lock
);
430 if (!chunk
->timestamp_creation
.is_set
) {
431 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
432 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
435 if (chunk
->timestamp_creation
.value
> close_ts
) {
436 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
437 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
440 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
441 if (!chunk
->name_overridden
) {
443 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
444 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
447 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
451 pthread_mutex_unlock(&chunk
->lock
);
456 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(
457 struct lttng_trace_chunk
*chunk
, const char **name
,
458 bool *name_overridden
)
460 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
462 pthread_mutex_lock(&chunk
->lock
);
463 if (name_overridden
) {
464 *name_overridden
= chunk
->name_overridden
;
467 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
472 pthread_mutex_unlock(&chunk
->lock
);
477 bool is_valid_chunk_name(const char *name
)
485 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
486 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
490 if (strchr(name
, '/') || strchr(name
, '.')) {
498 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
499 struct lttng_trace_chunk
*chunk
, const char *name
)
503 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
505 if (!is_valid_chunk_name(name
)) {
506 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
508 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
512 pthread_mutex_lock(&chunk
->lock
);
513 if (!chunk
->id
.is_set
) {
514 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
516 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
519 new_name
= strdup(name
);
521 ERR("Failed to allocate new trace chunk name");
522 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
526 chunk
->name
= new_name
;
527 chunk
->name_overridden
= true;
529 pthread_mutex_unlock(&chunk
->lock
);
535 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
536 struct lttng_trace_chunk
*chunk
,
537 struct lttng_credentials
*credentials
)
539 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
541 pthread_mutex_lock(&chunk
->lock
);
542 if (chunk
->credentials
.is_set
) {
543 if (chunk
->credentials
.value
.use_current_user
) {
544 credentials
->uid
= geteuid();
545 credentials
->gid
= getegid();
547 *credentials
= chunk
->credentials
.value
.user
;
550 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
552 pthread_mutex_unlock(&chunk
->lock
);
557 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
558 struct lttng_trace_chunk
*chunk
,
559 const struct lttng_credentials
*user_credentials
)
561 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
562 const struct chunk_credentials credentials
= {
563 .user
= *user_credentials
,
564 .use_current_user
= false,
567 pthread_mutex_lock(&chunk
->lock
);
568 if (chunk
->credentials
.is_set
) {
569 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
572 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
574 pthread_mutex_unlock(&chunk
->lock
);
579 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
580 struct lttng_trace_chunk
*chunk
)
582 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
583 const struct chunk_credentials credentials
= {
584 .use_current_user
= true,
587 pthread_mutex_lock(&chunk
->lock
);
588 if (chunk
->credentials
.is_set
) {
589 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
592 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
594 pthread_mutex_unlock(&chunk
->lock
);
600 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
601 struct lttng_trace_chunk
*chunk
,
602 struct lttng_directory_handle
*session_output_directory
)
605 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
606 struct lttng_directory_handle chunk_directory_handle
;
608 pthread_mutex_lock(&chunk
->lock
);
609 if (chunk
->mode
.is_set
) {
610 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
613 if (!chunk
->credentials
.is_set
) {
615 * Fatal error, credentials must be set before a
616 * directory is created.
618 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
619 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
625 * A nameless chunk does not need its own output directory.
626 * The session's output directory will be used.
628 ret
= lttng_directory_handle_create_subdirectory_as_user(
629 session_output_directory
,
632 !chunk
->credentials
.value
.use_current_user
?
633 &chunk
->credentials
.value
.user
: NULL
);
635 PERROR("Failed to create chunk output directory \"%s\"",
637 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
641 ret
= lttng_directory_handle_init_from_handle(&chunk_directory_handle
,
643 session_output_directory
);
645 /* The function already logs on all error paths. */
646 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
649 LTTNG_OPTIONAL_SET(&chunk
->session_output_directory
,
650 lttng_directory_handle_move(session_output_directory
));
651 LTTNG_OPTIONAL_SET(&chunk
->chunk_directory
,
652 lttng_directory_handle_move(&chunk_directory_handle
));
653 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
655 pthread_mutex_unlock(&chunk
->lock
);
660 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
661 struct lttng_trace_chunk
*chunk
,
662 struct lttng_directory_handle
*chunk_directory
)
664 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
666 pthread_mutex_lock(&chunk
->lock
);
667 if (chunk
->mode
.is_set
) {
668 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
671 if (!chunk
->credentials
.is_set
) {
672 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
673 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
676 LTTNG_OPTIONAL_SET(&chunk
->chunk_directory
,
677 lttng_directory_handle_move(chunk_directory
));
678 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
680 pthread_mutex_unlock(&chunk
->lock
);
685 enum lttng_trace_chunk_status
lttng_trace_chunk_get_chunk_directory_handle(
686 struct lttng_trace_chunk
*chunk
,
687 const struct lttng_directory_handle
**handle
)
689 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
691 pthread_mutex_lock(&chunk
->lock
);
692 if (!chunk
->chunk_directory
.is_set
) {
693 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
697 *handle
= &chunk
->chunk_directory
.value
;
699 pthread_mutex_unlock(&chunk
->lock
);
703 /* Add a top-level directory to the trace chunk if it was previously unknown. */
705 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
706 const char *new_path
)
710 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
711 &chunk
->top_level_directories
);
712 const char *new_path_separator_pos
= strchr(new_path
, '/');
713 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
714 new_path_separator_pos
- new_path
: strlen(new_path
);
716 for (i
= 0; i
< count
; i
++) {
717 const char *path
= lttng_dynamic_pointer_array_get_pointer(
718 &chunk
->top_level_directories
, i
);
719 const ptrdiff_t path_top_level_len
= strlen(path
);
721 if (path_top_level_len
!= new_path_top_level_len
) {
724 if (!strncmp(path
, new_path
, path_top_level_len
)) {
731 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
733 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
734 new_path
, chunk
->name
? : "(unnamed)");
736 PERROR("Failed to copy path");
740 ret
= lttng_dynamic_pointer_array_add_pointer(
741 &chunk
->top_level_directories
, copy
);
743 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
753 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
754 struct lttng_trace_chunk
*chunk
,
758 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
760 DBG("Creating trace chunk subdirectory \"%s\"", path
);
761 pthread_mutex_lock(&chunk
->lock
);
762 if (!chunk
->credentials
.is_set
) {
764 * Fatal error, credentials must be set before a
765 * directory is created.
767 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
769 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
772 if (!chunk
->mode
.is_set
||
773 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
774 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
776 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
779 if (!chunk
->chunk_directory
.is_set
) {
780 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
782 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
786 ERR("Refusing to create absolute trace chunk directory \"%s\"",
788 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
791 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
792 &chunk
->chunk_directory
.value
, path
,
794 chunk
->credentials
.value
.use_current_user
?
795 NULL
: &chunk
->credentials
.value
.user
);
797 PERROR("Failed to create trace chunk subdirectory \"%s\"",
799 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
802 ret
= add_top_level_directory_unique(chunk
, path
);
804 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
808 pthread_mutex_unlock(&chunk
->lock
);
813 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
814 struct lttng_trace_chunk
*chunk
, const char *file_path
,
815 int flags
, mode_t mode
, int *out_fd
)
818 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
820 DBG("Opening trace chunk file \"%s\"", file_path
);
821 pthread_mutex_lock(&chunk
->lock
);
822 if (!chunk
->credentials
.is_set
) {
824 * Fatal error, credentials must be set before a
827 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
829 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
832 if (!chunk
->chunk_directory
.is_set
) {
833 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
835 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
838 ret
= lttng_directory_handle_open_file_as_user(
839 &chunk
->chunk_directory
.value
, file_path
, flags
, mode
,
840 chunk
->credentials
.value
.use_current_user
?
841 NULL
: &chunk
->credentials
.value
.user
);
843 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
844 file_path
, flags
, (int) mode
);
845 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
850 pthread_mutex_unlock(&chunk
->lock
);
855 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
856 const char *file_path
)
859 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
861 DBG("Unlinking trace chunk file \"%s\"", file_path
);
862 pthread_mutex_lock(&chunk
->lock
);
863 if (!chunk
->credentials
.is_set
) {
865 * Fatal error, credentials must be set before a
866 * directory is created.
868 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
870 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
873 if (!chunk
->chunk_directory
.is_set
) {
874 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
876 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
879 ret
= lttng_directory_handle_unlink_file_as_user(
880 &chunk
->chunk_directory
.value
, file_path
,
881 chunk
->credentials
.value
.use_current_user
?
882 NULL
: &chunk
->credentials
.value
.user
);
884 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
888 pthread_mutex_unlock(&chunk
->lock
);
893 void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk
*trace_chunk
)
896 char *directory_to_rename
= NULL
;
897 bool free_directory_to_rename
= false;
898 char *archived_chunk_name
= NULL
;
899 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
900 const time_t creation_timestamp
=
901 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
902 const time_t close_timestamp
=
903 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
904 LTTNG_OPTIONAL(struct lttng_directory_handle
) archived_chunks_directory
= {};
906 if (!trace_chunk
->mode
.is_set
||
907 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
908 !trace_chunk
->session_output_directory
.is_set
) {
910 * This command doesn't need to run if the output is remote
911 * or if the trace chunk is not owned by this process.
916 assert(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
917 assert(!trace_chunk
->name_overridden
);
920 * The fist trace chunk of a session is directly output to the
921 * session's output folder. In this case, the top level directories
922 * must be moved to a temporary folder before that temporary directory
923 * is renamed to match the chunk's name.
926 struct lttng_directory_handle temporary_rename_directory
;
927 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
928 &trace_chunk
->top_level_directories
);
930 ret
= lttng_directory_handle_create_subdirectory_as_user(
931 &trace_chunk
->session_output_directory
.value
,
932 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
,
934 !trace_chunk
->credentials
.value
.use_current_user
?
935 &trace_chunk
->credentials
.value
.user
: NULL
);
937 PERROR("Failed to create temporary trace chunk rename directory \"%s\"",
938 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
);
941 ret
= lttng_directory_handle_init_from_handle(&temporary_rename_directory
,
942 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
,
943 &trace_chunk
->session_output_directory
.value
);
945 ERR("Failed to get handle to temporary trace chunk rename directory");
949 for (i
= 0; i
< count
; i
++) {
950 const char *top_level_name
=
951 lttng_dynamic_pointer_array_get_pointer(
952 &trace_chunk
->top_level_directories
, i
);
954 ret
= lttng_directory_handle_rename_as_user(
955 &trace_chunk
->session_output_directory
.value
,
957 &temporary_rename_directory
,
959 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
961 &trace_chunk
->credentials
.value
.user
);
963 PERROR("Failed to move \"%s\" to temporary trace chunk rename directory",
965 lttng_directory_handle_fini(
966 &temporary_rename_directory
);
970 lttng_directory_handle_fini(&temporary_rename_directory
);
971 directory_to_rename
= DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
;
972 free_directory_to_rename
= false;
974 directory_to_rename
= generate_chunk_name(chunk_id
,
975 creation_timestamp
, NULL
);
976 if (!directory_to_rename
) {
977 ERR("Failed to generate initial trace chunk name while renaming trace chunk");
980 free_directory_to_rename
= true;
983 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
985 if (!archived_chunk_name
) {
986 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
990 ret
= lttng_directory_handle_create_subdirectory_as_user(
991 &trace_chunk
->session_output_directory
.value
,
992 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
994 !trace_chunk
->credentials
.value
.use_current_user
?
995 &trace_chunk
->credentials
.value
.user
:
998 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
999 "\" directory for archived trace chunks");
1003 ret
= lttng_directory_handle_init_from_handle(
1004 &archived_chunks_directory
.value
,
1005 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
1006 &trace_chunk
->session_output_directory
.value
);
1008 PERROR("Failed to get handle to archived trace chunks directory");
1011 archived_chunks_directory
.is_set
= true;
1013 ret
= lttng_directory_handle_rename_as_user(
1014 &trace_chunk
->session_output_directory
.value
,
1015 directory_to_rename
,
1016 &archived_chunks_directory
.value
,
1017 archived_chunk_name
,
1018 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
1020 &trace_chunk
->credentials
.value
.user
);
1022 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1023 directory_to_rename
, archived_chunk_name
);
1027 if (archived_chunks_directory
.is_set
) {
1028 lttng_directory_handle_fini(&archived_chunks_directory
.value
);
1030 free(archived_chunk_name
);
1031 if (free_directory_to_rename
) {
1032 free(directory_to_rename
);
1037 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
1038 struct lttng_trace_chunk
*chunk
,
1039 enum lttng_trace_chunk_command_type
*command_type
)
1041 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1043 pthread_mutex_lock(&chunk
->lock
);
1044 if (chunk
->close_command
.is_set
) {
1045 *command_type
= chunk
->close_command
.value
;
1046 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1048 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
1050 pthread_mutex_unlock(&chunk
->lock
);
1055 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
1056 struct lttng_trace_chunk
*chunk
,
1057 enum lttng_trace_chunk_command_type close_command
)
1059 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
1061 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
1062 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
1063 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
1067 pthread_mutex_lock(&chunk
->lock
);
1068 if (chunk
->close_command
.is_set
) {
1069 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1070 close_command_names
[chunk
->close_command
.value
],
1071 close_command_names
[close_command
]);
1073 DBG("Setting trace chunk close command to \"%s\"",
1074 close_command_names
[close_command
]);
1076 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1077 pthread_mutex_unlock(&chunk
->lock
);
1083 const char *lttng_trace_chunk_command_type_get_name(
1084 enum lttng_trace_chunk_command_type command
)
1087 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1088 return "move to completed trace chunk folder";
1095 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1097 return urcu_ref_get_unless_zero(&chunk
->ref
);
1101 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1103 struct lttng_trace_chunk_registry_element
*element
=
1104 container_of(node
, typeof(*element
), rcu_node
);
1106 lttng_trace_chunk_fini(&element
->chunk
);
1111 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1113 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1116 if (chunk
->close_command
.is_set
) {
1117 close_command_funcs
[chunk
->close_command
.value
](chunk
);
1120 if (chunk
->in_registry_element
) {
1121 struct lttng_trace_chunk_registry_element
*element
;
1123 element
= container_of(chunk
, typeof(*element
), chunk
);
1124 if (element
->registry
) {
1126 cds_lfht_del(element
->registry
->ht
,
1127 &element
->trace_chunk_registry_ht_node
);
1129 call_rcu(&element
->rcu_node
,
1130 free_lttng_trace_chunk_registry_element
);
1132 /* Never published, can be free'd immediately. */
1133 free_lttng_trace_chunk_registry_element(
1134 &element
->rcu_node
);
1137 /* Not RCU-protected, free immediately. */
1138 lttng_trace_chunk_fini(chunk
);
1144 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1149 assert(chunk
->ref
.refcount
);
1150 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1154 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1156 struct lttng_trace_chunk_registry
*registry
;
1158 registry
= zmalloc(sizeof(*registry
));
1163 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1164 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1165 if (!registry
->ht
) {
1171 lttng_trace_chunk_registry_destroy(registry
);
1176 void lttng_trace_chunk_registry_destroy(
1177 struct lttng_trace_chunk_registry
*registry
)
1183 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1190 struct lttng_trace_chunk_registry_element
*
1191 lttng_trace_chunk_registry_element_create_from_chunk(
1192 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1194 struct lttng_trace_chunk_registry_element
*element
=
1195 zmalloc(sizeof(*element
));
1200 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1201 element
->session_id
= session_id
;
1203 element
->chunk
= *chunk
;
1204 lttng_trace_chunk_init(&element
->chunk
);
1205 if (chunk
->session_output_directory
.is_set
) {
1206 element
->chunk
.session_output_directory
.value
=
1207 lttng_directory_handle_move(
1208 &chunk
->session_output_directory
.value
);
1210 if (chunk
->chunk_directory
.is_set
) {
1211 element
->chunk
.chunk_directory
.value
=
1212 lttng_directory_handle_move(
1213 &chunk
->chunk_directory
.value
);
1216 * The original chunk becomes invalid; the name attribute is transferred
1217 * to the new chunk instance.
1220 element
->chunk
.in_registry_element
= true;
1226 struct lttng_trace_chunk
*
1227 lttng_trace_chunk_registry_publish_chunk(
1228 struct lttng_trace_chunk_registry
*registry
,
1229 uint64_t session_id
, struct lttng_trace_chunk
*chunk
)
1231 struct lttng_trace_chunk_registry_element
*element
;
1232 unsigned long element_hash
;
1234 pthread_mutex_lock(&chunk
->lock
);
1235 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
1237 pthread_mutex_unlock(&chunk
->lock
);
1242 * chunk is now invalid, the only valid operation is a 'put' from the
1246 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1250 struct cds_lfht_node
*published_node
;
1251 struct lttng_trace_chunk
*published_chunk
;
1252 struct lttng_trace_chunk_registry_element
*published_element
;
1254 published_node
= cds_lfht_add_unique(registry
->ht
,
1256 lttng_trace_chunk_registry_element_match
,
1258 &element
->trace_chunk_registry_ht_node
);
1259 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
1260 /* Successfully published the new element. */
1261 element
->registry
= registry
;
1262 /* Acquire a reference for the caller. */
1263 if (lttng_trace_chunk_get(&element
->chunk
)) {
1267 * Another thread concurrently unpublished the
1268 * trace chunk. This is currently unexpected.
1270 * Re-attempt to publish.
1272 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1278 * An equivalent trace chunk was published before this trace
1279 * chunk. Attempt to acquire a reference to the one that was
1280 * already published and release the reference to the copy we
1281 * created if successful.
1283 published_element
= container_of(published_node
,
1284 typeof(*published_element
),
1285 trace_chunk_registry_ht_node
);
1286 published_chunk
= &published_element
->chunk
;
1287 if (lttng_trace_chunk_get(published_chunk
)) {
1288 lttng_trace_chunk_put(&element
->chunk
);
1289 element
= published_element
;
1293 * A reference to the previously published trace chunk could not
1294 * be acquired. Hence, retry to publish our copy of the trace
1300 return element
? &element
->chunk
: NULL
;
1304 * Note that the caller must be registered as an RCU thread.
1305 * However, it does not need to hold the RCU read lock. The RCU read lock is
1306 * acquired to perform the look-up in the registry's hash table and held until
1307 * after a reference to the "found" trace chunk is acquired.
1309 * IOW, holding a reference guarantees the existence of the object for the
1313 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
1314 const struct lttng_trace_chunk_registry
*registry
,
1315 uint64_t session_id
, uint64_t *chunk_id
)
1317 const struct lttng_trace_chunk_registry_element target_element
= {
1318 .chunk
.id
.is_set
= !!chunk_id
,
1319 .chunk
.id
.value
= chunk_id
? *chunk_id
: 0,
1320 .session_id
= session_id
,
1322 const unsigned long element_hash
=
1323 lttng_trace_chunk_registry_element_hash(
1325 struct cds_lfht_node
*published_node
;
1326 struct lttng_trace_chunk_registry_element
*published_element
;
1327 struct lttng_trace_chunk
*published_chunk
= NULL
;
1328 struct cds_lfht_iter iter
;
1331 cds_lfht_lookup(registry
->ht
,
1333 lttng_trace_chunk_registry_element_match
,
1336 published_node
= cds_lfht_iter_get_node(&iter
);
1337 if (!published_node
) {
1341 published_element
= container_of(published_node
,
1342 typeof(*published_element
),
1343 trace_chunk_registry_ht_node
);
1344 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
1345 published_chunk
= &published_element
->chunk
;
1349 return published_chunk
;
1353 struct lttng_trace_chunk
*
1354 lttng_trace_chunk_registry_find_chunk(
1355 const struct lttng_trace_chunk_registry
*registry
,
1356 uint64_t session_id
, uint64_t chunk_id
)
1358 return _lttng_trace_chunk_registry_find_chunk(registry
,
1359 session_id
, &chunk_id
);
1363 int lttng_trace_chunk_registry_chunk_exists(
1364 const struct lttng_trace_chunk_registry
*registry
,
1365 uint64_t session_id
, uint64_t chunk_id
, bool *chunk_exists
)
1368 const struct lttng_trace_chunk_registry_element target_element
= {
1369 .chunk
.id
.is_set
= true,
1370 .chunk
.id
.value
= chunk_id
,
1371 .session_id
= session_id
,
1373 const unsigned long element_hash
=
1374 lttng_trace_chunk_registry_element_hash(
1376 struct cds_lfht_node
*published_node
;
1377 struct cds_lfht_iter iter
;
1380 cds_lfht_lookup(registry
->ht
,
1382 lttng_trace_chunk_registry_element_match
,
1385 published_node
= cds_lfht_iter_get_node(&iter
);
1386 if (!published_node
) {
1387 *chunk_exists
= false;
1391 *chunk_exists
= !cds_lfht_is_node_deleted(published_node
);
1398 struct lttng_trace_chunk
*
1399 lttng_trace_chunk_registry_find_anonymous_chunk(
1400 const struct lttng_trace_chunk_registry
*registry
,
1401 uint64_t session_id
)
1403 return _lttng_trace_chunk_registry_find_chunk(registry
,
1407 unsigned int lttng_trace_chunk_registry_put_each_chunk(
1408 struct lttng_trace_chunk_registry
*registry
)
1410 struct cds_lfht_iter iter
;
1411 struct lttng_trace_chunk_registry_element
*chunk_element
;
1412 unsigned int trace_chunks_left
= 0;
1414 DBG("Releasing trace chunk registry to all trace chunks");
1416 cds_lfht_for_each_entry(registry
->ht
,
1417 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
1418 const char *chunk_id_str
= "none";
1419 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
1421 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
1422 if (chunk_element
->chunk
.id
.is_set
) {
1425 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
1427 chunk_element
->chunk
.id
.value
);
1428 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
1429 chunk_id_str
= "formatting error";
1431 chunk_id_str
= chunk_id_buf
;
1435 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
1436 "chunk_id = %s, name = \"%s\", status = %s",
1437 chunk_element
->session_id
,
1439 chunk_element
->chunk
.name
? : "none",
1440 chunk_element
->chunk
.close_command
.is_set
?
1442 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
1443 lttng_trace_chunk_put(&chunk_element
->chunk
);
1444 trace_chunks_left
++;
1447 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
1450 return trace_chunks_left
;