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 struct lttng_trace_chunk
{
72 LTTNG_OPTIONAL(enum trace_chunk_mode
) mode
;
74 * First-level directories created within the trace chunk.
75 * Elements are of type 'char *'.
77 struct lttng_dynamic_pointer_array top_level_directories
;
78 /* Is contained within an lttng_trace_chunk_registry_element? */
79 bool in_registry_element
;
82 /* An unset id means the chunk is anonymous. */
83 LTTNG_OPTIONAL(uint64_t) id
;
84 LTTNG_OPTIONAL(time_t) timestamp_creation
;
85 LTTNG_OPTIONAL(time_t) timestamp_close
;
86 LTTNG_OPTIONAL(struct chunk_credentials
) credentials
;
87 LTTNG_OPTIONAL(struct lttng_directory_handle
) session_output_directory
;
88 LTTNG_OPTIONAL(struct lttng_directory_handle
) chunk_directory
;
89 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type
) close_command
;
92 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
93 struct lttng_trace_chunk_registry_element
{
95 struct lttng_trace_chunk chunk
;
96 /* Weak and only set when added. */
97 struct lttng_trace_chunk_registry
*registry
;
98 struct cds_lfht_node trace_chunk_registry_ht_node
;
99 /* call_rcu delayed reclaim. */
100 struct rcu_head rcu_node
;
103 struct lttng_trace_chunk_registry
{
107 const char *close_command_names
[] = {
108 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
109 "move to completed chunk folder",
112 chunk_close_command close_command_funcs
[] = {
113 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
114 lttng_trace_chunk_move_to_completed
,
118 bool lttng_trace_chunk_registry_element_equals(
119 const struct lttng_trace_chunk_registry_element
*a
,
120 const struct lttng_trace_chunk_registry_element
*b
)
122 if (a
->session_id
!= b
->session_id
) {
125 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
128 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
137 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
,
140 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
142 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
143 element_b
= caa_container_of(node
, typeof(*element_b
),
144 trace_chunk_registry_ht_node
);
145 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
149 unsigned long lttng_trace_chunk_registry_element_hash(
150 const struct lttng_trace_chunk_registry_element
*element
)
152 unsigned long hash
= hash_key_u64(&element
->session_id
,
155 if (element
->chunk
.id
.is_set
) {
156 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
163 char *generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
,
164 const time_t *close_timestamp
)
167 char *new_name
= NULL
;
168 char start_datetime
[sizeof("YYYYmmddTHHMMSS+HHMM")] = {};
169 char end_datetime_suffix
[sizeof("-YYYYmmddTHHMMSS+HHMM")] = {};
171 ret
= time_to_iso8601_str(
173 start_datetime
, sizeof(start_datetime
));
175 ERR("Failed to format trace chunk start date time");
178 if (close_timestamp
) {
179 *end_datetime_suffix
= '-';
180 ret
= time_to_iso8601_str(
182 end_datetime_suffix
+ 1,
183 sizeof(end_datetime_suffix
));
185 ERR("Failed to format trace chunk end date time");
189 new_name
= zmalloc(GENERATED_CHUNK_NAME_LEN
);
191 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
194 ret
= snprintf(new_name
, GENERATED_CHUNK_NAME_LEN
, "%s%s-%" PRIu64
,
195 start_datetime
, end_datetime_suffix
, chunk_id
);
196 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
197 ERR("Failed to format trace chunk name");
208 void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
210 urcu_ref_init(&chunk
->ref
);
211 pthread_mutex_init(&chunk
->lock
, NULL
);
212 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
216 void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
218 if (chunk
->session_output_directory
.is_set
) {
219 lttng_directory_handle_fini(
220 &chunk
->session_output_directory
.value
);
222 if (chunk
->chunk_directory
.is_set
) {
223 lttng_directory_handle_fini(&chunk
->chunk_directory
.value
);
227 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
228 pthread_mutex_destroy(&chunk
->lock
);
232 struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
234 struct lttng_trace_chunk
*chunk
= NULL
;
236 chunk
= zmalloc(sizeof(*chunk
));
238 ERR("Failed to allocate trace chunk");
241 lttng_trace_chunk_init(chunk
);
247 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
249 DBG("Creating anonymous trace chunk");
250 return lttng_trace_chunk_allocate();
254 struct lttng_trace_chunk
*lttng_trace_chunk_create(
255 uint64_t chunk_id
, time_t chunk_creation_time
)
257 struct lttng_trace_chunk
*chunk
;
258 char chunk_creation_datetime_buf
[16] = {};
259 const char *chunk_creation_datetime_str
= "(formatting error)";
260 struct tm timeinfo_buf
, *timeinfo
;
262 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
266 /* Don't fail because of this; it is only used for logging. */
267 strftime_ret
= strftime(chunk_creation_datetime_buf
,
268 sizeof(chunk_creation_datetime_buf
),
269 "%Y%m%d-%H%M%S", timeinfo
);
271 chunk_creation_datetime_str
=
272 chunk_creation_datetime_buf
;
276 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
277 chunk_id
, chunk_creation_datetime_str
);
278 chunk
= lttng_trace_chunk_allocate();
283 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
284 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
286 chunk
->name
= generate_chunk_name(chunk_id
,
287 chunk_creation_time
, NULL
);
289 ERR("Failed to allocate trace chunk name storage");
294 DBG("Chunk name set to \"%s\"", chunk
->name
? : "(none)");
298 lttng_trace_chunk_put(chunk
);
303 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(
304 struct lttng_trace_chunk
*chunk
, uint64_t *id
)
306 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
308 pthread_mutex_lock(&chunk
->lock
);
309 if (chunk
->id
.is_set
) {
310 *id
= chunk
->id
.value
;
312 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
314 pthread_mutex_unlock(&chunk
->lock
);
319 enum lttng_trace_chunk_status
lttng_trace_chunk_get_creation_timestamp(
320 struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
323 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
325 pthread_mutex_lock(&chunk
->lock
);
326 if (chunk
->timestamp_creation
.is_set
) {
327 *creation_ts
= chunk
->timestamp_creation
.value
;
329 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
331 pthread_mutex_unlock(&chunk
->lock
);
336 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_timestamp(
337 struct lttng_trace_chunk
*chunk
, time_t *close_ts
)
339 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
341 pthread_mutex_lock(&chunk
->lock
);
342 if (chunk
->timestamp_close
.is_set
) {
343 *close_ts
= chunk
->timestamp_close
.value
;
345 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
347 pthread_mutex_unlock(&chunk
->lock
);
352 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(
353 struct lttng_trace_chunk
*chunk
, time_t close_ts
)
355 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
357 pthread_mutex_lock(&chunk
->lock
);
358 if (!chunk
->timestamp_creation
.is_set
) {
359 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
360 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
363 if (chunk
->timestamp_creation
.value
> close_ts
) {
364 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
365 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
368 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
370 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
371 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
374 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
377 pthread_mutex_unlock(&chunk
->lock
);
382 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(
383 struct lttng_trace_chunk
*chunk
, const char **name
,
384 bool *name_overriden
)
386 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
388 pthread_mutex_lock(&chunk
->lock
);
389 if (name_overriden
) {
390 *name_overriden
= chunk
->name_overriden
;
393 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
398 pthread_mutex_unlock(&chunk
->lock
);
403 bool is_valid_chunk_name(const char *name
)
411 len
= strnlen(name
, LTTNG_NAME_MAX
);
412 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
416 if (strchr(name
, '/') || strchr(name
, '.')) {
424 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
425 struct lttng_trace_chunk
*chunk
, const char *name
)
429 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
431 if (!is_valid_chunk_name(name
)) {
432 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
434 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
438 pthread_mutex_lock(&chunk
->lock
);
439 if (!chunk
->id
.is_set
) {
440 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
442 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
445 new_name
= strdup(name
);
447 ERR("Failed to allocate new trace chunk name");
448 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
452 chunk
->name
= new_name
;
453 chunk
->name_overriden
= true;
455 pthread_mutex_unlock(&chunk
->lock
);
461 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
462 struct lttng_trace_chunk
*chunk
,
463 struct lttng_credentials
*credentials
)
465 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
467 pthread_mutex_lock(&chunk
->lock
);
468 if (chunk
->credentials
.is_set
) {
469 if (chunk
->credentials
.value
.use_current_user
) {
470 credentials
->uid
= geteuid();
471 credentials
->gid
= getegid();
473 *credentials
= chunk
->credentials
.value
.user
;
476 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
478 pthread_mutex_unlock(&chunk
->lock
);
483 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
484 struct lttng_trace_chunk
*chunk
,
485 const struct lttng_credentials
*user_credentials
)
487 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
488 const struct chunk_credentials credentials
= {
489 .user
= *user_credentials
,
490 .use_current_user
= false,
493 pthread_mutex_lock(&chunk
->lock
);
494 if (chunk
->credentials
.is_set
) {
495 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
498 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
500 pthread_mutex_unlock(&chunk
->lock
);
505 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
506 struct lttng_trace_chunk
*chunk
)
508 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
509 const struct chunk_credentials credentials
= {
510 .use_current_user
= true,
513 pthread_mutex_lock(&chunk
->lock
);
514 if (chunk
->credentials
.is_set
) {
515 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
518 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
520 pthread_mutex_unlock(&chunk
->lock
);
526 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
527 struct lttng_trace_chunk
*chunk
,
528 struct lttng_directory_handle
*session_output_directory
)
531 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
532 struct lttng_directory_handle chunk_directory_handle
;
534 pthread_mutex_lock(&chunk
->lock
);
535 if (chunk
->mode
.is_set
) {
536 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
539 if (!chunk
->credentials
.is_set
) {
541 * Fatal error, credentials must be set before a
542 * directory is created.
544 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
545 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
551 * A nameless chunk does not need its own output directory.
552 * The session's output directory will be used.
554 ret
= lttng_directory_handle_create_subdirectory_as_user(
555 session_output_directory
,
558 !chunk
->credentials
.value
.use_current_user
?
559 &chunk
->credentials
.value
.user
: NULL
);
561 PERROR("Failed to create chunk output directory \"%s\"",
563 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
567 ret
= lttng_directory_handle_init_from_handle(&chunk_directory_handle
,
569 session_output_directory
);
571 /* The function already logs on all error paths. */
572 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
575 LTTNG_OPTIONAL_SET(&chunk
->session_output_directory
,
576 lttng_directory_handle_move(session_output_directory
));
577 LTTNG_OPTIONAL_SET(&chunk
->chunk_directory
,
578 lttng_directory_handle_move(&chunk_directory_handle
));
579 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
581 pthread_mutex_unlock(&chunk
->lock
);
586 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
587 struct lttng_trace_chunk
*chunk
,
588 struct lttng_directory_handle
*chunk_directory
)
590 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
592 pthread_mutex_lock(&chunk
->lock
);
593 if (chunk
->mode
.is_set
) {
594 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
597 if (!chunk
->credentials
.is_set
) {
598 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
599 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
602 LTTNG_OPTIONAL_SET(&chunk
->chunk_directory
,
603 lttng_directory_handle_move(chunk_directory
));
604 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
606 pthread_mutex_unlock(&chunk
->lock
);
611 enum lttng_trace_chunk_status
lttng_trace_chunk_get_chunk_directory_handle(
612 struct lttng_trace_chunk
*chunk
,
613 const struct lttng_directory_handle
**handle
)
615 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
617 pthread_mutex_lock(&chunk
->lock
);
618 if (!chunk
->chunk_directory
.is_set
) {
619 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
623 *handle
= &chunk
->chunk_directory
.value
;
625 pthread_mutex_unlock(&chunk
->lock
);
629 /* Add a top-level directory to the trace chunk if it was previously unknown. */
631 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
632 const char *new_path
)
636 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
637 &chunk
->top_level_directories
);
638 const char *new_path_separator_pos
= strchr(new_path
, '/');
639 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
640 new_path_separator_pos
- new_path
: strlen(new_path
);
642 for (i
= 0; i
< count
; i
++) {
643 const char *path
= lttng_dynamic_pointer_array_get_pointer(
644 &chunk
->top_level_directories
, i
);
645 const ptrdiff_t path_top_level_len
= strlen(path
);
647 if (path_top_level_len
!= new_path_top_level_len
) {
650 if (!strncmp(path
, new_path
, path_top_level_len
)) {
657 char *copy
= strndup(new_path
, new_path_top_level_len
);
659 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
660 new_path
, chunk
->name
? : "(unnamed)");
662 PERROR("Failed to copy path");
666 ret
= lttng_dynamic_pointer_array_add_pointer(
667 &chunk
->top_level_directories
, copy
);
669 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
679 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
680 struct lttng_trace_chunk
*chunk
,
684 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
686 DBG("Creating trace chunk subdirectory \"%s\"", path
);
687 pthread_mutex_lock(&chunk
->lock
);
688 if (!chunk
->credentials
.is_set
) {
690 * Fatal error, credentials must be set before a
691 * directory is created.
693 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
695 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
698 if (!chunk
->mode
.is_set
||
699 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
700 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
702 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
705 if (!chunk
->chunk_directory
.is_set
) {
706 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
708 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
712 ERR("Refusing to create absolute trace chunk directory \"%s\"",
714 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
717 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
718 &chunk
->chunk_directory
.value
, path
,
720 chunk
->credentials
.value
.use_current_user
?
721 NULL
: &chunk
->credentials
.value
.user
);
723 PERROR("Failed to create trace chunk subdirectory \"%s\"",
725 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
728 ret
= add_top_level_directory_unique(chunk
, path
);
730 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
734 pthread_mutex_unlock(&chunk
->lock
);
739 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
740 struct lttng_trace_chunk
*chunk
, const char *file_path
,
741 int flags
, mode_t mode
, int *out_fd
)
744 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
746 DBG("Opening trace chunk file \"%s\"", file_path
);
747 pthread_mutex_lock(&chunk
->lock
);
748 if (!chunk
->credentials
.is_set
) {
750 * Fatal error, credentials must be set before a
753 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
755 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
758 if (!chunk
->chunk_directory
.is_set
) {
759 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
761 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
764 ret
= lttng_directory_handle_open_file_as_user(
765 &chunk
->chunk_directory
.value
, file_path
, flags
, mode
,
766 chunk
->credentials
.value
.use_current_user
?
767 NULL
: &chunk
->credentials
.value
.user
);
769 ERR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
770 file_path
, flags
, (int) mode
);
771 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
776 pthread_mutex_unlock(&chunk
->lock
);
781 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
782 const char *file_path
)
785 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
787 DBG("Unlinking trace chunk file \"%s\"", file_path
);
788 pthread_mutex_lock(&chunk
->lock
);
789 if (!chunk
->credentials
.is_set
) {
791 * Fatal error, credentials must be set before a
792 * directory is created.
794 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
796 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
799 if (!chunk
->chunk_directory
.is_set
) {
800 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
802 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
805 ret
= lttng_directory_handle_unlink_file_as_user(
806 &chunk
->chunk_directory
.value
, file_path
,
807 chunk
->credentials
.value
.use_current_user
?
808 NULL
: &chunk
->credentials
.value
.user
);
810 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
814 pthread_mutex_unlock(&chunk
->lock
);
819 void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk
*trace_chunk
)
822 char *directory_to_rename
= NULL
;
823 bool free_directory_to_rename
= false;
824 char *archived_chunk_name
= NULL
;
825 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
826 const time_t creation_timestamp
=
827 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
828 const time_t close_timestamp
=
829 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
830 LTTNG_OPTIONAL(struct lttng_directory_handle
) archived_chunks_directory
;
832 assert(trace_chunk
->mode
.is_set
);
833 assert(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
834 assert(!trace_chunk
->name_overriden
);
837 * The fist trace chunk of a session is directly output to the
838 * session's output folder. In this case, the top level directories
839 * must be moved to a temporary folder before that temporary directory
840 * is renamed to match the chunk's name.
843 struct lttng_directory_handle temporary_rename_directory
;
844 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
845 &trace_chunk
->top_level_directories
);
847 ret
= lttng_directory_handle_create_subdirectory_as_user(
848 &trace_chunk
->session_output_directory
.value
,
849 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
,
851 !trace_chunk
->credentials
.value
.use_current_user
?
852 &trace_chunk
->credentials
.value
.user
: NULL
);
854 PERROR("Failed to create temporary trace chunk rename directory \"%s\"",
855 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
);
858 ret
= lttng_directory_handle_init_from_handle(&temporary_rename_directory
,
859 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
,
860 &trace_chunk
->session_output_directory
.value
);
862 ERR("Failed to get handle to temporary trace chunk rename directory");
866 for (i
= 0; i
< count
; i
++) {
867 const char *top_level_name
=
868 lttng_dynamic_pointer_array_get_pointer(
869 &trace_chunk
->top_level_directories
, i
);
871 ret
= lttng_directory_handle_rename_as_user(
872 &trace_chunk
->session_output_directory
.value
,
874 &temporary_rename_directory
,
876 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
878 &trace_chunk
->credentials
.value
.user
);
880 PERROR("Failed to move \"%s\" to temporary trace chunk rename directory",
882 lttng_directory_handle_fini(
883 &temporary_rename_directory
);
887 lttng_directory_handle_fini(&temporary_rename_directory
);
888 directory_to_rename
= DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
;
889 free_directory_to_rename
= false;
891 directory_to_rename
= generate_chunk_name(chunk_id
,
892 creation_timestamp
, NULL
);
893 if (!directory_to_rename
) {
894 ERR("Failed to generate initial trace chunk name while renaming trace chunk");
896 free_directory_to_rename
= true;
899 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
901 if (!archived_chunk_name
) {
902 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
906 ret
= lttng_directory_handle_create_subdirectory_as_user(
907 &trace_chunk
->session_output_directory
.value
,
908 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
910 !trace_chunk
->credentials
.value
.use_current_user
?
911 &trace_chunk
->credentials
.value
.user
:
914 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
915 "\" directory for archived trace chunks");
919 ret
= lttng_directory_handle_init_from_handle(
920 &archived_chunks_directory
.value
,
921 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
922 &trace_chunk
->session_output_directory
.value
);
924 PERROR("Failed to get handle to archived trace chunks directory");
927 archived_chunks_directory
.is_set
= true;
929 ret
= lttng_directory_handle_rename_as_user(
930 &trace_chunk
->session_output_directory
.value
,
932 &archived_chunks_directory
.value
,
934 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
936 &trace_chunk
->credentials
.value
.user
);
938 PERROR("Failed to rename folder \"%s\" to \"%s\"",
939 directory_to_rename
, archived_chunk_name
);
943 if (archived_chunks_directory
.is_set
) {
944 lttng_directory_handle_fini(&archived_chunks_directory
.value
);
946 free(archived_chunk_name
);
947 if (free_directory_to_rename
) {
948 free(directory_to_rename
);
953 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
954 struct lttng_trace_chunk
*chunk
,
955 enum lttng_trace_chunk_command_type close_command
)
957 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
959 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
960 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
961 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
965 pthread_mutex_lock(&chunk
->lock
);
966 if (chunk
->close_command
.is_set
) {
967 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
968 close_command_names
[chunk
->close_command
.value
],
969 close_command_names
[close_command
]);
971 DBG("Setting trace chunk close command to \"%s\"",
972 close_command_names
[close_command
]);
974 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
975 pthread_mutex_unlock(&chunk
->lock
);
981 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
983 return urcu_ref_get_unless_zero(&chunk
->ref
);
987 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
989 struct lttng_trace_chunk_registry_element
*element
=
990 container_of(node
, typeof(*element
), rcu_node
);
992 lttng_trace_chunk_fini(&element
->chunk
);
997 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
999 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1002 if (chunk
->close_command
.is_set
) {
1003 close_command_funcs
[chunk
->close_command
.value
](chunk
);
1006 if (chunk
->in_registry_element
) {
1007 struct lttng_trace_chunk_registry_element
*element
;
1009 element
= container_of(chunk
, typeof(*element
), chunk
);
1010 if (element
->registry
) {
1012 cds_lfht_del(element
->registry
->ht
,
1013 &element
->trace_chunk_registry_ht_node
);
1015 call_rcu(&element
->rcu_node
,
1016 free_lttng_trace_chunk_registry_element
);
1018 /* Never published, can be free'd immediately. */
1019 free_lttng_trace_chunk_registry_element(
1020 &element
->rcu_node
);
1023 /* Not RCU-protected, free immediately. */
1024 lttng_trace_chunk_fini(chunk
);
1030 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1035 assert(chunk
->ref
.refcount
);
1036 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1040 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1042 struct lttng_trace_chunk_registry
*registry
;
1044 registry
= zmalloc(sizeof(*registry
));
1049 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1050 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1051 if (!registry
->ht
) {
1057 lttng_trace_chunk_registry_destroy(registry
);
1062 void lttng_trace_chunk_registry_destroy(
1063 struct lttng_trace_chunk_registry
*registry
)
1069 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1076 struct lttng_trace_chunk_registry_element
*
1077 lttng_trace_chunk_registry_element_create_from_chunk(
1078 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1080 struct lttng_trace_chunk_registry_element
*element
=
1081 zmalloc(sizeof(*element
));
1086 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1087 element
->session_id
= session_id
;
1089 element
->chunk
= *chunk
;
1090 lttng_trace_chunk_init(&element
->chunk
);
1091 if (chunk
->session_output_directory
.is_set
) {
1092 element
->chunk
.session_output_directory
.value
=
1093 lttng_directory_handle_move(
1094 &chunk
->session_output_directory
.value
);
1096 if (chunk
->chunk_directory
.is_set
) {
1097 element
->chunk
.chunk_directory
.value
=
1098 lttng_directory_handle_move(
1099 &chunk
->chunk_directory
.value
);
1102 * The original chunk becomes invalid; the name attribute is transferred
1103 * to the new chunk instance.
1106 element
->chunk
.in_registry_element
= true;
1112 struct lttng_trace_chunk
*
1113 lttng_trace_chunk_registry_publish_chunk(
1114 struct lttng_trace_chunk_registry
*registry
,
1115 uint64_t session_id
, struct lttng_trace_chunk
*chunk
)
1117 struct lttng_trace_chunk_registry_element
*element
;
1118 unsigned long element_hash
;
1120 pthread_mutex_lock(&chunk
->lock
);
1121 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
1123 pthread_mutex_unlock(&chunk
->lock
);
1128 * chunk is now invalid, the only valid operation is a 'put' from the
1132 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1136 struct cds_lfht_node
*published_node
;
1137 struct lttng_trace_chunk
*published_chunk
;
1138 struct lttng_trace_chunk_registry_element
*published_element
;
1140 published_node
= cds_lfht_add_unique(registry
->ht
,
1142 lttng_trace_chunk_registry_element_match
,
1144 &element
->trace_chunk_registry_ht_node
);
1145 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
1146 /* Successfully published the new element. */
1147 element
->registry
= registry
;
1148 /* Acquire a reference for the caller. */
1149 if (lttng_trace_chunk_get(&element
->chunk
)) {
1153 * Another thread concurrently unpublished the
1154 * trace chunk. This is currently unexpected.
1156 * Re-attempt to publish.
1158 ERR("Attemp to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1164 * An equivalent trace chunk was published before this trace
1165 * chunk. Attempt to acquire a reference to the one that was
1166 * already published and release the reference to the copy we
1167 * created if successful.
1169 published_element
= container_of(published_node
,
1170 typeof(*published_element
),
1171 trace_chunk_registry_ht_node
);
1172 published_chunk
= &published_element
->chunk
;
1173 if (lttng_trace_chunk_get(published_chunk
)) {
1174 lttng_trace_chunk_put(&element
->chunk
);
1175 element
= published_element
;
1179 * A reference to the previously published trace chunk could not
1180 * be acquired. Hence, retry to publish our copy of the trace
1186 return element
? &element
->chunk
: NULL
;
1190 * Note that the caller must be registered as an RCU thread.
1191 * However, it does not need to hold the RCU read lock. The RCU read lock is
1192 * acquired to perform the look-up in the registry's hash table and held until
1193 * after a reference to the "found" trace chunk is acquired.
1195 * IOW, holding a reference guarantees the existence of the object for the
1199 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
1200 const struct lttng_trace_chunk_registry
*registry
,
1201 uint64_t session_id
, uint64_t *chunk_id
)
1203 const struct lttng_trace_chunk_registry_element target_element
= {
1204 .chunk
.id
.is_set
= !!chunk_id
,
1205 .chunk
.id
.value
= chunk_id
? *chunk_id
: 0,
1206 .session_id
= session_id
,
1208 const unsigned long element_hash
=
1209 lttng_trace_chunk_registry_element_hash(
1211 struct cds_lfht_node
*published_node
;
1212 struct lttng_trace_chunk_registry_element
*published_element
;
1213 struct lttng_trace_chunk
*published_chunk
= NULL
;
1214 struct cds_lfht_iter iter
;
1217 cds_lfht_lookup(registry
->ht
,
1219 lttng_trace_chunk_registry_element_match
,
1222 published_node
= cds_lfht_iter_get_node(&iter
);
1223 if (!published_node
) {
1227 published_element
= container_of(published_node
,
1228 typeof(*published_element
),
1229 trace_chunk_registry_ht_node
);
1230 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
1231 published_chunk
= &published_element
->chunk
;
1235 return published_chunk
;
1239 struct lttng_trace_chunk
*
1240 lttng_trace_chunk_registry_find_chunk(
1241 const struct lttng_trace_chunk_registry
*registry
,
1242 uint64_t session_id
, uint64_t chunk_id
)
1244 return _lttng_trace_chunk_registry_find_chunk(registry
,
1245 session_id
, &chunk_id
);
1249 struct lttng_trace_chunk
*
1250 lttng_trace_chunk_registry_find_anonymous_chunk(
1251 const struct lttng_trace_chunk_registry
*registry
,
1252 uint64_t session_id
)
1254 return _lttng_trace_chunk_registry_find_chunk(registry
,