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
{
94 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
{
108 char *close_command_names
[] = {
109 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
110 "move to completed chunk folder",
114 chunk_close_command close_command_funcs
[] = {
115 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
] =
116 lttng_trace_chunk_move_to_completed
,
120 bool lttng_trace_chunk_registry_element_equals(
121 const struct lttng_trace_chunk_registry_element
*a
,
122 const struct lttng_trace_chunk_registry_element
*b
)
124 if (a
->session_id
!= b
->session_id
) {
127 if (a
->chunk
.id
.is_set
!= b
->chunk
.id
.is_set
) {
130 if (a
->chunk
.id
.is_set
&& a
->chunk
.id
.value
!= b
->chunk
.id
.value
) {
139 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node
*node
,
142 const struct lttng_trace_chunk_registry_element
*element_a
, *element_b
;
144 element_a
= (const struct lttng_trace_chunk_registry_element
*) key
;
145 element_b
= caa_container_of(node
, typeof(*element_b
),
146 trace_chunk_registry_ht_node
);
147 return lttng_trace_chunk_registry_element_equals(element_a
, element_b
);
151 unsigned long lttng_trace_chunk_registry_element_hash(
152 const struct lttng_trace_chunk_registry_element
*element
)
154 unsigned long hash
= hash_key_u64(&element
->session_id
,
157 if (element
->chunk
.id
.is_set
) {
158 hash
^= hash_key_u64(&element
->chunk
.id
.value
, lttng_ht_seed
);
165 char *generate_chunk_name(uint64_t chunk_id
, time_t creation_timestamp
,
166 const time_t *close_timestamp
)
169 char *new_name
= NULL
;
170 char start_datetime
[ISO8601_STR_LEN
] = {};
171 /* Add 1 for a '-' prefix. */
172 char end_datetime_suffix
[ISO8601_STR_LEN
+ 1] = {};
174 ret
= time_to_iso8601_str(
176 start_datetime
, sizeof(start_datetime
));
178 ERR("Failed to format trace chunk start date time");
181 if (close_timestamp
) {
182 *end_datetime_suffix
= '-';
183 ret
= time_to_iso8601_str(
185 end_datetime_suffix
+ 1,
186 sizeof(end_datetime_suffix
) - 1);
188 ERR("Failed to format trace chunk end date time");
192 new_name
= zmalloc(GENERATED_CHUNK_NAME_LEN
);
194 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
197 ret
= snprintf(new_name
, GENERATED_CHUNK_NAME_LEN
, "%s%s-%" PRIu64
,
198 start_datetime
, end_datetime_suffix
, chunk_id
);
199 if (ret
>= GENERATED_CHUNK_NAME_LEN
|| ret
== -1) {
200 ERR("Failed to format trace chunk name");
211 void lttng_trace_chunk_init(struct lttng_trace_chunk
*chunk
)
213 urcu_ref_init(&chunk
->ref
);
214 pthread_mutex_init(&chunk
->lock
, NULL
);
215 lttng_dynamic_pointer_array_init(&chunk
->top_level_directories
, free
);
219 void lttng_trace_chunk_fini(struct lttng_trace_chunk
*chunk
)
221 if (chunk
->session_output_directory
.is_set
) {
222 lttng_directory_handle_fini(
223 &chunk
->session_output_directory
.value
);
225 if (chunk
->chunk_directory
.is_set
) {
226 lttng_directory_handle_fini(&chunk
->chunk_directory
.value
);
230 lttng_dynamic_pointer_array_reset(&chunk
->top_level_directories
);
231 pthread_mutex_destroy(&chunk
->lock
);
235 struct lttng_trace_chunk
*lttng_trace_chunk_allocate(void)
237 struct lttng_trace_chunk
*chunk
= NULL
;
239 chunk
= zmalloc(sizeof(*chunk
));
241 ERR("Failed to allocate trace chunk");
244 lttng_trace_chunk_init(chunk
);
250 struct lttng_trace_chunk
*lttng_trace_chunk_create_anonymous(void)
252 DBG("Creating anonymous trace chunk");
253 return lttng_trace_chunk_allocate();
257 struct lttng_trace_chunk
*lttng_trace_chunk_create(
258 uint64_t chunk_id
, time_t chunk_creation_time
)
260 struct lttng_trace_chunk
*chunk
;
261 char chunk_creation_datetime_buf
[16] = {};
262 const char *chunk_creation_datetime_str
= "(formatting error)";
263 struct tm timeinfo_buf
, *timeinfo
;
265 timeinfo
= localtime_r(&chunk_creation_time
, &timeinfo_buf
);
269 /* Don't fail because of this; it is only used for logging. */
270 strftime_ret
= strftime(chunk_creation_datetime_buf
,
271 sizeof(chunk_creation_datetime_buf
),
272 "%Y%m%d-%H%M%S", timeinfo
);
274 chunk_creation_datetime_str
=
275 chunk_creation_datetime_buf
;
279 DBG("Creating trace chunk: chunk_id = %" PRIu64
", creation time = %s",
280 chunk_id
, chunk_creation_datetime_str
);
281 chunk
= lttng_trace_chunk_allocate();
286 LTTNG_OPTIONAL_SET(&chunk
->id
, chunk_id
);
287 LTTNG_OPTIONAL_SET(&chunk
->timestamp_creation
, chunk_creation_time
);
289 chunk
->name
= generate_chunk_name(chunk_id
,
290 chunk_creation_time
, NULL
);
292 ERR("Failed to allocate trace chunk name storage");
297 DBG("Chunk name set to \"%s\"", chunk
->name
? : "(none)");
301 lttng_trace_chunk_put(chunk
);
306 enum lttng_trace_chunk_status
lttng_trace_chunk_get_id(
307 struct lttng_trace_chunk
*chunk
, uint64_t *id
)
309 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
311 pthread_mutex_lock(&chunk
->lock
);
312 if (chunk
->id
.is_set
) {
313 *id
= chunk
->id
.value
;
315 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
317 pthread_mutex_unlock(&chunk
->lock
);
322 enum lttng_trace_chunk_status
lttng_trace_chunk_get_creation_timestamp(
323 struct lttng_trace_chunk
*chunk
, time_t *creation_ts
)
326 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
328 pthread_mutex_lock(&chunk
->lock
);
329 if (chunk
->timestamp_creation
.is_set
) {
330 *creation_ts
= chunk
->timestamp_creation
.value
;
332 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
334 pthread_mutex_unlock(&chunk
->lock
);
339 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_timestamp(
340 struct lttng_trace_chunk
*chunk
, time_t *close_ts
)
342 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
344 pthread_mutex_lock(&chunk
->lock
);
345 if (chunk
->timestamp_close
.is_set
) {
346 *close_ts
= chunk
->timestamp_close
.value
;
348 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
350 pthread_mutex_unlock(&chunk
->lock
);
355 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_timestamp(
356 struct lttng_trace_chunk
*chunk
, time_t close_ts
)
358 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
360 pthread_mutex_lock(&chunk
->lock
);
361 if (!chunk
->timestamp_creation
.is_set
) {
362 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
363 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
366 if (chunk
->timestamp_creation
.value
> close_ts
) {
367 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
368 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
371 LTTNG_OPTIONAL_SET(&chunk
->timestamp_close
, close_ts
);
372 if (!chunk
->name_overridden
) {
374 chunk
->name
= generate_chunk_name(LTTNG_OPTIONAL_GET(chunk
->id
),
375 LTTNG_OPTIONAL_GET(chunk
->timestamp_creation
),
378 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
382 pthread_mutex_unlock(&chunk
->lock
);
387 enum lttng_trace_chunk_status
lttng_trace_chunk_get_name(
388 struct lttng_trace_chunk
*chunk
, const char **name
,
389 bool *name_overridden
)
391 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
393 pthread_mutex_lock(&chunk
->lock
);
394 if (name_overridden
) {
395 *name_overridden
= chunk
->name_overridden
;
398 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
403 pthread_mutex_unlock(&chunk
->lock
);
408 bool is_valid_chunk_name(const char *name
)
416 len
= lttng_strnlen(name
, LTTNG_NAME_MAX
);
417 if (len
== 0 || len
== LTTNG_NAME_MAX
) {
421 if (strchr(name
, '/') || strchr(name
, '.')) {
429 enum lttng_trace_chunk_status
lttng_trace_chunk_override_name(
430 struct lttng_trace_chunk
*chunk
, const char *name
)
434 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
436 if (!is_valid_chunk_name(name
)) {
437 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
439 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
443 pthread_mutex_lock(&chunk
->lock
);
444 if (!chunk
->id
.is_set
) {
445 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
447 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
450 new_name
= strdup(name
);
452 ERR("Failed to allocate new trace chunk name");
453 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
457 chunk
->name
= new_name
;
458 chunk
->name_overridden
= true;
460 pthread_mutex_unlock(&chunk
->lock
);
466 enum lttng_trace_chunk_status
lttng_trace_chunk_get_credentials(
467 struct lttng_trace_chunk
*chunk
,
468 struct lttng_credentials
*credentials
)
470 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
472 pthread_mutex_lock(&chunk
->lock
);
473 if (chunk
->credentials
.is_set
) {
474 if (chunk
->credentials
.value
.use_current_user
) {
475 credentials
->uid
= geteuid();
476 credentials
->gid
= getegid();
478 *credentials
= chunk
->credentials
.value
.user
;
481 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
483 pthread_mutex_unlock(&chunk
->lock
);
488 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials(
489 struct lttng_trace_chunk
*chunk
,
490 const struct lttng_credentials
*user_credentials
)
492 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
493 const struct chunk_credentials credentials
= {
494 .user
= *user_credentials
,
495 .use_current_user
= false,
498 pthread_mutex_lock(&chunk
->lock
);
499 if (chunk
->credentials
.is_set
) {
500 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
503 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
505 pthread_mutex_unlock(&chunk
->lock
);
510 enum lttng_trace_chunk_status
lttng_trace_chunk_set_credentials_current_user(
511 struct lttng_trace_chunk
*chunk
)
513 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
514 const struct chunk_credentials credentials
= {
515 .use_current_user
= true,
518 pthread_mutex_lock(&chunk
->lock
);
519 if (chunk
->credentials
.is_set
) {
520 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
523 LTTNG_OPTIONAL_SET(&chunk
->credentials
, credentials
);
525 pthread_mutex_unlock(&chunk
->lock
);
531 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_owner(
532 struct lttng_trace_chunk
*chunk
,
533 struct lttng_directory_handle
*session_output_directory
)
536 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
537 struct lttng_directory_handle chunk_directory_handle
;
539 pthread_mutex_lock(&chunk
->lock
);
540 if (chunk
->mode
.is_set
) {
541 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
544 if (!chunk
->credentials
.is_set
) {
546 * Fatal error, credentials must be set before a
547 * directory is created.
549 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
550 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
556 * A nameless chunk does not need its own output directory.
557 * The session's output directory will be used.
559 ret
= lttng_directory_handle_create_subdirectory_as_user(
560 session_output_directory
,
563 !chunk
->credentials
.value
.use_current_user
?
564 &chunk
->credentials
.value
.user
: NULL
);
566 PERROR("Failed to create chunk output directory \"%s\"",
568 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
572 ret
= lttng_directory_handle_init_from_handle(&chunk_directory_handle
,
574 session_output_directory
);
576 /* The function already logs on all error paths. */
577 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
580 LTTNG_OPTIONAL_SET(&chunk
->session_output_directory
,
581 lttng_directory_handle_move(session_output_directory
));
582 LTTNG_OPTIONAL_SET(&chunk
->chunk_directory
,
583 lttng_directory_handle_move(&chunk_directory_handle
));
584 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_OWNER
);
586 pthread_mutex_unlock(&chunk
->lock
);
591 enum lttng_trace_chunk_status
lttng_trace_chunk_set_as_user(
592 struct lttng_trace_chunk
*chunk
,
593 struct lttng_directory_handle
*chunk_directory
)
595 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
597 pthread_mutex_lock(&chunk
->lock
);
598 if (chunk
->mode
.is_set
) {
599 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
602 if (!chunk
->credentials
.is_set
) {
603 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
604 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
607 LTTNG_OPTIONAL_SET(&chunk
->chunk_directory
,
608 lttng_directory_handle_move(chunk_directory
));
609 LTTNG_OPTIONAL_SET(&chunk
->mode
, TRACE_CHUNK_MODE_USER
);
611 pthread_mutex_unlock(&chunk
->lock
);
616 enum lttng_trace_chunk_status
lttng_trace_chunk_get_chunk_directory_handle(
617 struct lttng_trace_chunk
*chunk
,
618 const struct lttng_directory_handle
**handle
)
620 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
622 pthread_mutex_lock(&chunk
->lock
);
623 if (!chunk
->chunk_directory
.is_set
) {
624 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
628 *handle
= &chunk
->chunk_directory
.value
;
630 pthread_mutex_unlock(&chunk
->lock
);
634 /* Add a top-level directory to the trace chunk if it was previously unknown. */
636 int add_top_level_directory_unique(struct lttng_trace_chunk
*chunk
,
637 const char *new_path
)
641 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
642 &chunk
->top_level_directories
);
643 const char *new_path_separator_pos
= strchr(new_path
, '/');
644 const ptrdiff_t new_path_top_level_len
= new_path_separator_pos
?
645 new_path_separator_pos
- new_path
: strlen(new_path
);
647 for (i
= 0; i
< count
; i
++) {
648 const char *path
= lttng_dynamic_pointer_array_get_pointer(
649 &chunk
->top_level_directories
, i
);
650 const ptrdiff_t path_top_level_len
= strlen(path
);
652 if (path_top_level_len
!= new_path_top_level_len
) {
655 if (!strncmp(path
, new_path
, path_top_level_len
)) {
662 char *copy
= lttng_strndup(new_path
, new_path_top_level_len
);
664 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
665 new_path
, chunk
->name
? : "(unnamed)");
667 PERROR("Failed to copy path");
671 ret
= lttng_dynamic_pointer_array_add_pointer(
672 &chunk
->top_level_directories
, copy
);
674 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
684 enum lttng_trace_chunk_status
lttng_trace_chunk_create_subdirectory(
685 struct lttng_trace_chunk
*chunk
,
689 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
691 DBG("Creating trace chunk subdirectory \"%s\"", path
);
692 pthread_mutex_lock(&chunk
->lock
);
693 if (!chunk
->credentials
.is_set
) {
695 * Fatal error, credentials must be set before a
696 * directory is created.
698 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
700 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
703 if (!chunk
->mode
.is_set
||
704 chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
) {
705 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
707 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION
;
710 if (!chunk
->chunk_directory
.is_set
) {
711 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
713 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
717 ERR("Refusing to create absolute trace chunk directory \"%s\"",
719 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
722 ret
= lttng_directory_handle_create_subdirectory_recursive_as_user(
723 &chunk
->chunk_directory
.value
, path
,
725 chunk
->credentials
.value
.use_current_user
?
726 NULL
: &chunk
->credentials
.value
.user
);
728 PERROR("Failed to create trace chunk subdirectory \"%s\"",
730 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
733 ret
= add_top_level_directory_unique(chunk
, path
);
735 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
739 pthread_mutex_unlock(&chunk
->lock
);
744 enum lttng_trace_chunk_status
lttng_trace_chunk_open_file(
745 struct lttng_trace_chunk
*chunk
, const char *file_path
,
746 int flags
, mode_t mode
, int *out_fd
)
749 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
751 DBG("Opening trace chunk file \"%s\"", file_path
);
752 pthread_mutex_lock(&chunk
->lock
);
753 if (!chunk
->credentials
.is_set
) {
755 * Fatal error, credentials must be set before a
758 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
760 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
763 if (!chunk
->chunk_directory
.is_set
) {
764 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
766 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
769 ret
= lttng_directory_handle_open_file_as_user(
770 &chunk
->chunk_directory
.value
, file_path
, flags
, mode
,
771 chunk
->credentials
.value
.use_current_user
?
772 NULL
: &chunk
->credentials
.value
.user
);
774 ERR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
775 file_path
, flags
, (int) mode
);
776 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
781 pthread_mutex_unlock(&chunk
->lock
);
786 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk
*chunk
,
787 const char *file_path
)
790 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
792 DBG("Unlinking trace chunk file \"%s\"", file_path
);
793 pthread_mutex_lock(&chunk
->lock
);
794 if (!chunk
->credentials
.is_set
) {
796 * Fatal error, credentials must be set before a
797 * directory is created.
799 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
801 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
804 if (!chunk
->chunk_directory
.is_set
) {
805 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
807 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
810 ret
= lttng_directory_handle_unlink_file_as_user(
811 &chunk
->chunk_directory
.value
, file_path
,
812 chunk
->credentials
.value
.use_current_user
?
813 NULL
: &chunk
->credentials
.value
.user
);
815 status
= LTTNG_TRACE_CHUNK_STATUS_ERROR
;
819 pthread_mutex_unlock(&chunk
->lock
);
824 void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk
*trace_chunk
)
827 char *directory_to_rename
= NULL
;
828 bool free_directory_to_rename
= false;
829 char *archived_chunk_name
= NULL
;
830 const uint64_t chunk_id
= LTTNG_OPTIONAL_GET(trace_chunk
->id
);
831 const time_t creation_timestamp
=
832 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_creation
);
833 const time_t close_timestamp
=
834 LTTNG_OPTIONAL_GET(trace_chunk
->timestamp_close
);
835 LTTNG_OPTIONAL(struct lttng_directory_handle
) archived_chunks_directory
= {};
837 if (!trace_chunk
->mode
.is_set
||
838 trace_chunk
->mode
.value
!= TRACE_CHUNK_MODE_OWNER
||
839 !trace_chunk
->session_output_directory
.is_set
) {
841 * This command doesn't need to run if the output is remote
842 * or if the trace chunk is not owned by this process.
847 assert(trace_chunk
->mode
.value
== TRACE_CHUNK_MODE_OWNER
);
848 assert(!trace_chunk
->name_overridden
);
851 * The fist trace chunk of a session is directly output to the
852 * session's output folder. In this case, the top level directories
853 * must be moved to a temporary folder before that temporary directory
854 * is renamed to match the chunk's name.
857 struct lttng_directory_handle temporary_rename_directory
;
858 size_t i
, count
= lttng_dynamic_pointer_array_get_count(
859 &trace_chunk
->top_level_directories
);
861 ret
= lttng_directory_handle_create_subdirectory_as_user(
862 &trace_chunk
->session_output_directory
.value
,
863 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
,
865 !trace_chunk
->credentials
.value
.use_current_user
?
866 &trace_chunk
->credentials
.value
.user
: NULL
);
868 PERROR("Failed to create temporary trace chunk rename directory \"%s\"",
869 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
);
872 ret
= lttng_directory_handle_init_from_handle(&temporary_rename_directory
,
873 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
,
874 &trace_chunk
->session_output_directory
.value
);
876 ERR("Failed to get handle to temporary trace chunk rename directory");
880 for (i
= 0; i
< count
; i
++) {
881 const char *top_level_name
=
882 lttng_dynamic_pointer_array_get_pointer(
883 &trace_chunk
->top_level_directories
, i
);
885 ret
= lttng_directory_handle_rename_as_user(
886 &trace_chunk
->session_output_directory
.value
,
888 &temporary_rename_directory
,
890 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
892 &trace_chunk
->credentials
.value
.user
);
894 PERROR("Failed to move \"%s\" to temporary trace chunk rename directory",
896 lttng_directory_handle_fini(
897 &temporary_rename_directory
);
901 lttng_directory_handle_fini(&temporary_rename_directory
);
902 directory_to_rename
= DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY
;
903 free_directory_to_rename
= false;
905 directory_to_rename
= generate_chunk_name(chunk_id
,
906 creation_timestamp
, NULL
);
907 if (!directory_to_rename
) {
908 ERR("Failed to generate initial trace chunk name while renaming trace chunk");
910 free_directory_to_rename
= true;
913 archived_chunk_name
= generate_chunk_name(chunk_id
, creation_timestamp
,
915 if (!archived_chunk_name
) {
916 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
920 ret
= lttng_directory_handle_create_subdirectory_as_user(
921 &trace_chunk
->session_output_directory
.value
,
922 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
924 !trace_chunk
->credentials
.value
.use_current_user
?
925 &trace_chunk
->credentials
.value
.user
:
928 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
929 "\" directory for archived trace chunks");
933 ret
= lttng_directory_handle_init_from_handle(
934 &archived_chunks_directory
.value
,
935 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
,
936 &trace_chunk
->session_output_directory
.value
);
938 PERROR("Failed to get handle to archived trace chunks directory");
941 archived_chunks_directory
.is_set
= true;
943 ret
= lttng_directory_handle_rename_as_user(
944 &trace_chunk
->session_output_directory
.value
,
946 &archived_chunks_directory
.value
,
948 LTTNG_OPTIONAL_GET(trace_chunk
->credentials
).use_current_user
?
950 &trace_chunk
->credentials
.value
.user
);
952 PERROR("Failed to rename folder \"%s\" to \"%s\"",
953 directory_to_rename
, archived_chunk_name
);
957 if (archived_chunks_directory
.is_set
) {
958 lttng_directory_handle_fini(&archived_chunks_directory
.value
);
960 free(archived_chunk_name
);
961 if (free_directory_to_rename
) {
962 free(directory_to_rename
);
967 enum lttng_trace_chunk_status
lttng_trace_chunk_get_close_command(
968 struct lttng_trace_chunk
*chunk
,
969 enum lttng_trace_chunk_command_type
*command_type
)
971 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
973 pthread_mutex_lock(&chunk
->lock
);
974 if (chunk
->close_command
.is_set
) {
975 *command_type
= chunk
->close_command
.value
;
976 status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
978 status
= LTTNG_TRACE_CHUNK_STATUS_NONE
;
980 pthread_mutex_unlock(&chunk
->lock
);
985 enum lttng_trace_chunk_status
lttng_trace_chunk_set_close_command(
986 struct lttng_trace_chunk
*chunk
,
987 enum lttng_trace_chunk_command_type close_command
)
989 enum lttng_trace_chunk_status status
= LTTNG_TRACE_CHUNK_STATUS_OK
;
991 if (close_command
< LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
||
992 close_command
>= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX
) {
993 status
= LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT
;
997 pthread_mutex_lock(&chunk
->lock
);
998 if (chunk
->close_command
.is_set
) {
999 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1000 close_command_names
[chunk
->close_command
.value
],
1001 close_command_names
[close_command
]);
1003 DBG("Setting trace chunk close command to \"%s\"",
1004 close_command_names
[close_command
]);
1006 LTTNG_OPTIONAL_SET(&chunk
->close_command
, close_command
);
1007 pthread_mutex_unlock(&chunk
->lock
);
1013 const char *lttng_trace_chunk_command_type_get_name(
1014 enum lttng_trace_chunk_command_type command
)
1017 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED
:
1018 return "move to completed trace chunk folder";
1025 bool lttng_trace_chunk_get(struct lttng_trace_chunk
*chunk
)
1027 return urcu_ref_get_unless_zero(&chunk
->ref
);
1031 void free_lttng_trace_chunk_registry_element(struct rcu_head
*node
)
1033 struct lttng_trace_chunk_registry_element
*element
=
1034 container_of(node
, typeof(*element
), rcu_node
);
1036 lttng_trace_chunk_fini(&element
->chunk
);
1041 void lttng_trace_chunk_release(struct urcu_ref
*ref
)
1043 struct lttng_trace_chunk
*chunk
= container_of(ref
, typeof(*chunk
),
1046 if (chunk
->close_command
.is_set
) {
1047 close_command_funcs
[chunk
->close_command
.value
](chunk
);
1050 if (chunk
->in_registry_element
) {
1051 struct lttng_trace_chunk_registry_element
*element
;
1053 element
= container_of(chunk
, typeof(*element
), chunk
);
1054 if (element
->registry
) {
1056 cds_lfht_del(element
->registry
->ht
,
1057 &element
->trace_chunk_registry_ht_node
);
1059 call_rcu(&element
->rcu_node
,
1060 free_lttng_trace_chunk_registry_element
);
1062 /* Never published, can be free'd immediately. */
1063 free_lttng_trace_chunk_registry_element(
1064 &element
->rcu_node
);
1067 /* Not RCU-protected, free immediately. */
1068 lttng_trace_chunk_fini(chunk
);
1074 void lttng_trace_chunk_put(struct lttng_trace_chunk
*chunk
)
1079 assert(chunk
->ref
.refcount
);
1080 urcu_ref_put(&chunk
->ref
, lttng_trace_chunk_release
);
1084 struct lttng_trace_chunk_registry
*lttng_trace_chunk_registry_create(void)
1086 struct lttng_trace_chunk_registry
*registry
;
1088 registry
= zmalloc(sizeof(*registry
));
1093 registry
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
1094 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
1095 if (!registry
->ht
) {
1101 lttng_trace_chunk_registry_destroy(registry
);
1106 void lttng_trace_chunk_registry_destroy(
1107 struct lttng_trace_chunk_registry
*registry
)
1113 int ret
= cds_lfht_destroy(registry
->ht
, NULL
);
1120 struct lttng_trace_chunk_registry_element
*
1121 lttng_trace_chunk_registry_element_create_from_chunk(
1122 struct lttng_trace_chunk
*chunk
, uint64_t session_id
)
1124 struct lttng_trace_chunk_registry_element
*element
=
1125 zmalloc(sizeof(*element
));
1130 cds_lfht_node_init(&element
->trace_chunk_registry_ht_node
);
1131 element
->session_id
= session_id
;
1133 element
->chunk
= *chunk
;
1134 lttng_trace_chunk_init(&element
->chunk
);
1135 if (chunk
->session_output_directory
.is_set
) {
1136 element
->chunk
.session_output_directory
.value
=
1137 lttng_directory_handle_move(
1138 &chunk
->session_output_directory
.value
);
1140 if (chunk
->chunk_directory
.is_set
) {
1141 element
->chunk
.chunk_directory
.value
=
1142 lttng_directory_handle_move(
1143 &chunk
->chunk_directory
.value
);
1146 * The original chunk becomes invalid; the name attribute is transferred
1147 * to the new chunk instance.
1150 element
->chunk
.in_registry_element
= true;
1156 struct lttng_trace_chunk
*
1157 lttng_trace_chunk_registry_publish_chunk(
1158 struct lttng_trace_chunk_registry
*registry
,
1159 uint64_t session_id
, struct lttng_trace_chunk
*chunk
)
1161 struct lttng_trace_chunk_registry_element
*element
;
1162 unsigned long element_hash
;
1164 pthread_mutex_lock(&chunk
->lock
);
1165 element
= lttng_trace_chunk_registry_element_create_from_chunk(chunk
,
1167 pthread_mutex_unlock(&chunk
->lock
);
1172 * chunk is now invalid, the only valid operation is a 'put' from the
1176 element_hash
= lttng_trace_chunk_registry_element_hash(element
);
1180 struct cds_lfht_node
*published_node
;
1181 struct lttng_trace_chunk
*published_chunk
;
1182 struct lttng_trace_chunk_registry_element
*published_element
;
1184 published_node
= cds_lfht_add_unique(registry
->ht
,
1186 lttng_trace_chunk_registry_element_match
,
1188 &element
->trace_chunk_registry_ht_node
);
1189 if (published_node
== &element
->trace_chunk_registry_ht_node
) {
1190 /* Successfully published the new element. */
1191 element
->registry
= registry
;
1192 /* Acquire a reference for the caller. */
1193 if (lttng_trace_chunk_get(&element
->chunk
)) {
1197 * Another thread concurrently unpublished the
1198 * trace chunk. This is currently unexpected.
1200 * Re-attempt to publish.
1202 ERR("Attemp to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1208 * An equivalent trace chunk was published before this trace
1209 * chunk. Attempt to acquire a reference to the one that was
1210 * already published and release the reference to the copy we
1211 * created if successful.
1213 published_element
= container_of(published_node
,
1214 typeof(*published_element
),
1215 trace_chunk_registry_ht_node
);
1216 published_chunk
= &published_element
->chunk
;
1217 if (lttng_trace_chunk_get(published_chunk
)) {
1218 lttng_trace_chunk_put(&element
->chunk
);
1219 element
= published_element
;
1223 * A reference to the previously published trace chunk could not
1224 * be acquired. Hence, retry to publish our copy of the trace
1230 return element
? &element
->chunk
: NULL
;
1234 * Note that the caller must be registered as an RCU thread.
1235 * However, it does not need to hold the RCU read lock. The RCU read lock is
1236 * acquired to perform the look-up in the registry's hash table and held until
1237 * after a reference to the "found" trace chunk is acquired.
1239 * IOW, holding a reference guarantees the existence of the object for the
1243 struct lttng_trace_chunk
*_lttng_trace_chunk_registry_find_chunk(
1244 const struct lttng_trace_chunk_registry
*registry
,
1245 uint64_t session_id
, uint64_t *chunk_id
)
1247 const struct lttng_trace_chunk_registry_element target_element
= {
1248 .chunk
.id
.is_set
= !!chunk_id
,
1249 .chunk
.id
.value
= chunk_id
? *chunk_id
: 0,
1250 .session_id
= session_id
,
1252 const unsigned long element_hash
=
1253 lttng_trace_chunk_registry_element_hash(
1255 struct cds_lfht_node
*published_node
;
1256 struct lttng_trace_chunk_registry_element
*published_element
;
1257 struct lttng_trace_chunk
*published_chunk
= NULL
;
1258 struct cds_lfht_iter iter
;
1261 cds_lfht_lookup(registry
->ht
,
1263 lttng_trace_chunk_registry_element_match
,
1266 published_node
= cds_lfht_iter_get_node(&iter
);
1267 if (!published_node
) {
1271 published_element
= container_of(published_node
,
1272 typeof(*published_element
),
1273 trace_chunk_registry_ht_node
);
1274 if (lttng_trace_chunk_get(&published_element
->chunk
)) {
1275 published_chunk
= &published_element
->chunk
;
1279 return published_chunk
;
1283 struct lttng_trace_chunk
*
1284 lttng_trace_chunk_registry_find_chunk(
1285 const struct lttng_trace_chunk_registry
*registry
,
1286 uint64_t session_id
, uint64_t chunk_id
)
1288 return _lttng_trace_chunk_registry_find_chunk(registry
,
1289 session_id
, &chunk_id
);
1293 struct lttng_trace_chunk
*
1294 lttng_trace_chunk_registry_find_anonymous_chunk(
1295 const struct lttng_trace_chunk_registry
*registry
,
1296 uint64_t session_id
)
1298 return _lttng_trace_chunk_registry_find_chunk(registry
,
1302 unsigned int lttng_trace_chunk_registry_put_each_chunk(
1303 struct lttng_trace_chunk_registry
*registry
)
1305 struct cds_lfht_iter iter
;
1306 struct lttng_trace_chunk_registry_element
*chunk_element
;
1307 unsigned int trace_chunks_left
= 0;
1309 DBG("Releasing trace chunk registry to all trace chunks");
1311 cds_lfht_for_each_entry(registry
->ht
,
1312 &iter
, chunk_element
, trace_chunk_registry_ht_node
) {
1313 const char *chunk_id_str
= "none";
1314 char chunk_id_buf
[MAX_INT_DEC_LEN(uint64_t)];
1316 pthread_mutex_lock(&chunk_element
->chunk
.lock
);
1317 if (chunk_element
->chunk
.id
.is_set
) {
1320 fmt_ret
= snprintf(chunk_id_buf
, sizeof(chunk_id_buf
),
1322 chunk_element
->chunk
.id
.value
);
1323 if (fmt_ret
< 0 || fmt_ret
>= sizeof(chunk_id_buf
)) {
1324 chunk_id_str
= "formatting error";
1326 chunk_id_str
= chunk_id_buf
;
1330 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
1331 "chunk_id = %s, name = \"%s\", status = %s",
1332 chunk_element
->session_id
,
1334 chunk_element
->chunk
.name
? : "none",
1335 chunk_element
->chunk
.close_command
.is_set
?
1337 pthread_mutex_unlock(&chunk_element
->chunk
.lock
);
1338 lttng_trace_chunk_put(&chunk_element
->chunk
);
1339 trace_chunks_left
++;
1342 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left
,
1345 return trace_chunks_left
;