Build fix: brace-enclosed initlializer lists error with g++ 4.8
[lttng-tools.git] / src / common / trace-chunk.cpp
... / ...
CommitLineData
1/*
2 * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only
5 *
6 */
7
8#include <common/compat/directory-handle.hpp>
9#include <common/credentials.hpp>
10#include <common/defaults.hpp>
11#include <common/dynamic-array.hpp>
12#include <common/error.hpp>
13#include <common/fd-tracker/fd-tracker.hpp>
14#include <common/fd-tracker/utils.hpp>
15#include <common/fs-handle-internal.hpp>
16#include <common/fs-handle.hpp>
17#include <common/hashtable/hashtable.hpp>
18#include <common/hashtable/utils.hpp>
19#include <common/optional.hpp>
20#include <common/string-utils/format.hpp>
21#include <common/time.hpp>
22#include <common/trace-chunk-registry.hpp>
23#include <common/trace-chunk.hpp>
24#include <common/urcu.hpp>
25#include <common/utils.hpp>
26
27#include <lttng/constant.h>
28
29#include <inttypes.h>
30#include <pthread.h>
31#include <stdio.h>
32#include <sys/stat.h>
33#include <urcu/rculfhash.h>
34#include <urcu/ref.h>
35
36/*
37 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
38 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
39 */
40#define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
41#define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
42
43enum trace_chunk_mode {
44 TRACE_CHUNK_MODE_USER,
45 TRACE_CHUNK_MODE_OWNER,
46};
47
48/*
49 * Callback to invoke on release of a trace chunk. Note that there is no
50 * need to 'lock' the trace chunk during the execution of these callbacks
51 * since only one thread may access a chunk during its destruction (the last
52 * to release its reference to the chunk).
53 */
54using chunk_command = int (*)(struct lttng_trace_chunk *);
55
56/* Move a completed trace chunk to the 'completed' trace archive folder. */
57static int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk *trace_chunk);
58/* Empty callback. */
59static int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk);
60/* Unlink old chunk files. */
61static int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk *trace_chunk);
62static enum lttng_trace_chunk_status
63lttng_trace_chunk_rename_path_no_lock(struct lttng_trace_chunk *chunk, const char *path);
64
65namespace {
66struct chunk_credentials {
67 bool use_current_user;
68 struct lttng_credentials user;
69};
70} /* namespace */
71
72/*
73 * NOTE: Make sure to update:
74 * - lttng_trace_chunk_copy(),
75 * - lttng_trace_chunk_registry_element_create_from_chunk()
76 * if you modify this structure.
77 */
78struct lttng_trace_chunk {
79 pthread_mutex_t lock;
80 struct urcu_ref ref;
81 LTTNG_OPTIONAL(enum trace_chunk_mode) mode;
82 /*
83 * First-level directories created within the trace chunk.
84 * Elements are of type 'char *'.
85 *
86 * Only used by _owner_ mode chunks.
87 */
88 struct lttng_dynamic_pointer_array top_level_directories;
89 /*
90 * All files contained within the trace chunk.
91 * Array of paths (char *).
92 */
93 struct lttng_dynamic_pointer_array files;
94 /* Is contained within an lttng_trace_chunk_registry_element? */
95 bool in_registry_element;
96 bool name_overridden;
97 char *name;
98 char *path;
99 /* An unset id means the chunk is anonymous. */
100 LTTNG_OPTIONAL(uint64_t) id;
101
102 /*
103 * The creation and close timestamps are NOT monotonic.
104 * They must not be used in context were monotonicity is required.
105 */
106 LTTNG_OPTIONAL(time_t) timestamp_creation;
107 LTTNG_OPTIONAL(time_t) timestamp_close;
108
109 LTTNG_OPTIONAL(struct chunk_credentials) credentials;
110 struct lttng_directory_handle *session_output_directory;
111 struct lttng_directory_handle *chunk_directory;
112 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type) close_command;
113 /*
114 * fd_tracker instance through which file descriptors should be
115 * created/closed.
116 *
117 * An fd_tracker always outlives any trace chunk; there is no
118 * need to perform any reference counting of that object.
119 */
120 struct fd_tracker *fd_tracker;
121};
122
123namespace {
124/* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
125struct lttng_trace_chunk_registry_element {
126 struct lttng_trace_chunk chunk;
127 uint64_t session_id;
128 /* Weak and only set when added. */
129 struct lttng_trace_chunk_registry *registry;
130 struct cds_lfht_node trace_chunk_registry_ht_node;
131 /* call_rcu delayed reclaim. */
132 struct rcu_head rcu_node;
133};
134} /* namespace */
135
136struct lttng_trace_chunk_registry {
137 struct cds_lfht *ht;
138};
139
140namespace {
141struct fs_handle_untracked {
142 struct fs_handle parent;
143 int fd;
144 struct {
145 struct lttng_directory_handle *directory_handle;
146 char *path;
147 } location;
148};
149} /* namespace */
150
151static int fs_handle_untracked_get_fd(struct fs_handle *handle);
152static void fs_handle_untracked_put_fd(struct fs_handle *handle);
153static int fs_handle_untracked_unlink(struct fs_handle *handle);
154static int fs_handle_untracked_close(struct fs_handle *handle);
155
156static const char *lttng_trace_chunk_command_type_str(lttng_trace_chunk_command_type type)
157{
158 switch (type) {
159 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
160 return "move to completed chunk folder";
161 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
162 return "no operation";
163 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
164 return "delete";
165 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX:
166 abort();
167 }
168
169 abort();
170};
171
172static chunk_command close_command_get_post_release_func(lttng_trace_chunk_command_type type)
173{
174 switch (type) {
175 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
176 return lttng_trace_chunk_move_to_completed_post_release;
177 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
178 return lttng_trace_chunk_no_operation;
179 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
180 return lttng_trace_chunk_delete_post_release;
181 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX:
182 abort();
183 }
184
185 abort();
186};
187
188static struct fs_handle *fs_handle_untracked_create(struct lttng_directory_handle *directory_handle,
189 const char *path,
190 int fd)
191{
192 struct fs_handle_untracked *handle = nullptr;
193 bool reference_acquired;
194 char *path_copy = strdup(path);
195
196 LTTNG_ASSERT(fd >= 0);
197 if (!path_copy) {
198 PERROR("Failed to copy file path while creating untracked filesystem handle");
199 goto end;
200 }
201
202 handle = zmalloc<fs_handle_untracked>();
203 if (!handle) {
204 PERROR("Failed to allocate untracked filesystem handle");
205 goto end;
206 }
207
208 handle->parent = (typeof(handle->parent)){
209 .get_fd = fs_handle_untracked_get_fd,
210 .put_fd = fs_handle_untracked_put_fd,
211 .unlink = fs_handle_untracked_unlink,
212 .close = fs_handle_untracked_close,
213 };
214
215 handle->fd = fd;
216 reference_acquired = lttng_directory_handle_get(directory_handle);
217 LTTNG_ASSERT(reference_acquired);
218 handle->location.directory_handle = directory_handle;
219 /* Ownership is transferred. */
220 handle->location.path = path_copy;
221 path_copy = nullptr;
222end:
223 free(path_copy);
224 return handle ? &handle->parent : nullptr;
225}
226
227static int fs_handle_untracked_get_fd(struct fs_handle *_handle)
228{
229 struct fs_handle_untracked *handle =
230 lttng::utils::container_of(_handle, &fs_handle_untracked::parent);
231
232 return handle->fd;
233}
234
235static void fs_handle_untracked_put_fd(struct fs_handle *_handle __attribute__((unused)))
236{
237 /* no-op. */
238}
239
240static int fs_handle_untracked_unlink(struct fs_handle *_handle)
241{
242 struct fs_handle_untracked *handle =
243 lttng::utils::container_of(_handle, &fs_handle_untracked::parent);
244
245 return lttng_directory_handle_unlink_file(handle->location.directory_handle,
246 handle->location.path);
247}
248
249static void fs_handle_untracked_destroy(struct fs_handle_untracked *handle)
250{
251 lttng_directory_handle_put(handle->location.directory_handle);
252 free(handle->location.path);
253 free(handle);
254}
255
256static int fs_handle_untracked_close(struct fs_handle *_handle)
257{
258 struct fs_handle_untracked *handle =
259 lttng::utils::container_of(_handle, &fs_handle_untracked::parent);
260 int ret = close(handle->fd);
261
262 fs_handle_untracked_destroy(handle);
263 return ret;
264}
265
266static bool
267lttng_trace_chunk_registry_element_equals(const struct lttng_trace_chunk_registry_element *a,
268 const struct lttng_trace_chunk_registry_element *b)
269{
270 if (a->session_id != b->session_id) {
271 goto not_equal;
272 }
273 if (a->chunk.id.is_set != b->chunk.id.is_set) {
274 goto not_equal;
275 }
276 if (a->chunk.id.is_set && a->chunk.id.value != b->chunk.id.value) {
277 goto not_equal;
278 }
279 return true;
280not_equal:
281 return false;
282}
283
284static int lttng_trace_chunk_registry_element_match(struct cds_lfht_node *node, const void *key)
285{
286 const struct lttng_trace_chunk_registry_element *element_a, *element_b;
287
288 element_a = (const struct lttng_trace_chunk_registry_element *) key;
289 element_b = caa_container_of(node, typeof(*element_b), trace_chunk_registry_ht_node);
290 return lttng_trace_chunk_registry_element_equals(element_a, element_b);
291}
292
293static unsigned long
294lttng_trace_chunk_registry_element_hash(const struct lttng_trace_chunk_registry_element *element)
295{
296 unsigned long hash = hash_key_u64(&element->session_id, lttng_ht_seed);
297
298 if (element->chunk.id.is_set) {
299 hash ^= hash_key_u64(&element->chunk.id.value, lttng_ht_seed);
300 }
301
302 return hash;
303}
304
305static char *
306generate_chunk_name(uint64_t chunk_id, time_t creation_timestamp, const time_t *close_timestamp)
307{
308 int ret = 0;
309 char *new_name = nullptr;
310 char start_datetime[ISO8601_STR_LEN] = {};
311 /* Add 1 for a '-' prefix. */
312 char end_datetime_suffix[ISO8601_STR_LEN + 1] = {};
313
314 ret = time_to_iso8601_str(creation_timestamp, start_datetime, sizeof(start_datetime));
315 if (ret) {
316 ERR("Failed to format trace chunk start date time");
317 goto error;
318 }
319 if (close_timestamp) {
320 *end_datetime_suffix = '-';
321 ret = time_to_iso8601_str(
322 *close_timestamp, end_datetime_suffix + 1, sizeof(end_datetime_suffix) - 1);
323 if (ret) {
324 ERR("Failed to format trace chunk end date time");
325 goto error;
326 }
327 }
328 new_name = calloc<char>(GENERATED_CHUNK_NAME_LEN);
329 if (!new_name) {
330 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
331 goto error;
332 }
333 ret = snprintf(new_name,
334 GENERATED_CHUNK_NAME_LEN,
335 "%s%s-%" PRIu64,
336 start_datetime,
337 end_datetime_suffix,
338 chunk_id);
339 if (ret >= GENERATED_CHUNK_NAME_LEN || ret == -1) {
340 ERR("Failed to format trace chunk name");
341 goto error;
342 }
343
344 return new_name;
345error:
346 free(new_name);
347 return nullptr;
348}
349
350static void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk)
351{
352 urcu_ref_init(&chunk->ref);
353 pthread_mutex_init(&chunk->lock, nullptr);
354 lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free);
355 lttng_dynamic_pointer_array_init(&chunk->files, free);
356}
357
358static void lttng_trace_chunk_fini(struct lttng_trace_chunk *chunk)
359{
360 if (chunk->session_output_directory) {
361 lttng_directory_handle_put(chunk->session_output_directory);
362 chunk->session_output_directory = nullptr;
363 }
364 if (chunk->chunk_directory) {
365 lttng_directory_handle_put(chunk->chunk_directory);
366 chunk->chunk_directory = nullptr;
367 }
368 free(chunk->name);
369 chunk->name = nullptr;
370 free(chunk->path);
371 chunk->path = nullptr;
372 lttng_dynamic_pointer_array_reset(&chunk->top_level_directories);
373 lttng_dynamic_pointer_array_reset(&chunk->files);
374 pthread_mutex_destroy(&chunk->lock);
375}
376
377static struct lttng_trace_chunk *lttng_trace_chunk_allocate()
378{
379 struct lttng_trace_chunk *chunk = nullptr;
380
381 chunk = zmalloc<lttng_trace_chunk>();
382 if (!chunk) {
383 ERR("Failed to allocate trace chunk");
384 goto end;
385 }
386 lttng_trace_chunk_init(chunk);
387end:
388 return chunk;
389}
390
391struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous()
392{
393 DBG("Creating anonymous trace chunk");
394 return lttng_trace_chunk_allocate();
395}
396
397struct lttng_trace_chunk *
398lttng_trace_chunk_create(uint64_t chunk_id, time_t chunk_creation_time, const char *path)
399{
400 struct lttng_trace_chunk *chunk;
401 char chunk_creation_datetime_buf[16] = {};
402 const char *chunk_creation_datetime_str = "(formatting error)";
403 struct tm timeinfo_buf, *timeinfo;
404
405 timeinfo = localtime_r(&chunk_creation_time, &timeinfo_buf);
406 if (timeinfo) {
407 size_t strftime_ret;
408
409 /* Don't fail because of this; it is only used for logging. */
410 strftime_ret = strftime(chunk_creation_datetime_buf,
411 sizeof(chunk_creation_datetime_buf),
412 "%Y%m%d-%H%M%S",
413 timeinfo);
414 if (strftime_ret) {
415 chunk_creation_datetime_str = chunk_creation_datetime_buf;
416 }
417 }
418
419 DBG("Creating trace chunk: chunk_id = %" PRIu64 ", creation time = %s",
420 chunk_id,
421 chunk_creation_datetime_str);
422 chunk = lttng_trace_chunk_allocate();
423 if (!chunk) {
424 goto end;
425 }
426
427 LTTNG_OPTIONAL_SET(&chunk->id, chunk_id);
428 LTTNG_OPTIONAL_SET(&chunk->timestamp_creation, chunk_creation_time);
429 if (chunk_id != 0) {
430 chunk->name = generate_chunk_name(chunk_id, chunk_creation_time, nullptr);
431 if (!chunk->name) {
432 ERR("Failed to allocate trace chunk name storage");
433 goto error;
434 }
435 }
436 if (path) {
437 chunk->path = strdup(path);
438 if (!chunk->path) {
439 goto error;
440 }
441 } else {
442 if (chunk->name) {
443 chunk->path = strdup(chunk->name);
444 if (!chunk->path) {
445 goto error;
446 }
447 }
448 }
449
450 DBG("Chunk name set to \"%s\"", chunk->name ?: "(none)");
451end:
452 return chunk;
453error:
454 lttng_trace_chunk_put(chunk);
455 return nullptr;
456}
457
458void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk *chunk,
459 struct fd_tracker *fd_tracker)
460{
461 LTTNG_ASSERT(!chunk->session_output_directory);
462 LTTNG_ASSERT(!chunk->chunk_directory);
463 LTTNG_ASSERT(lttng_dynamic_pointer_array_get_count(&chunk->files) == 0);
464 chunk->fd_tracker = fd_tracker;
465}
466
467struct lttng_trace_chunk *lttng_trace_chunk_copy(struct lttng_trace_chunk *source_chunk)
468{
469 struct lttng_trace_chunk *new_chunk = lttng_trace_chunk_allocate();
470
471 if (!new_chunk) {
472 goto end;
473 }
474
475 pthread_mutex_lock(&source_chunk->lock);
476 /*
477 * A new chunk is always a user; it shall create no new trace
478 * subdirectories.
479 */
480 new_chunk->mode = (typeof(new_chunk->mode)){
481 .is_set = true,
482 .value = TRACE_CHUNK_MODE_USER,
483 };
484 /*
485 * top_level_directories is not copied as it is never used
486 * by _user_ mode chunks.
487 */
488 /* The new chunk is not part of a registry (yet, at least). */
489 new_chunk->in_registry_element = false;
490 new_chunk->name_overridden = source_chunk->name_overridden;
491 if (source_chunk->name) {
492 new_chunk->name = strdup(source_chunk->name);
493 if (!new_chunk->name) {
494 ERR("Failed to copy source trace chunk name in %s()", __FUNCTION__);
495 goto error_unlock;
496 }
497 }
498 if (source_chunk->path) {
499 new_chunk->path = strdup(source_chunk->path);
500 if (!new_chunk->path) {
501 ERR("Failed to copy source trace chunk path in %s()", __FUNCTION__);
502 }
503 }
504 new_chunk->id = source_chunk->id;
505 new_chunk->timestamp_creation = source_chunk->timestamp_creation;
506 new_chunk->timestamp_close = source_chunk->timestamp_close;
507 new_chunk->credentials = source_chunk->credentials;
508 if (source_chunk->session_output_directory) {
509 const bool reference_acquired =
510 lttng_directory_handle_get(source_chunk->session_output_directory);
511
512 LTTNG_ASSERT(reference_acquired);
513 new_chunk->session_output_directory = source_chunk->session_output_directory;
514 }
515 if (source_chunk->chunk_directory) {
516 const bool reference_acquired =
517 lttng_directory_handle_get(source_chunk->chunk_directory);
518
519 LTTNG_ASSERT(reference_acquired);
520 new_chunk->chunk_directory = source_chunk->chunk_directory;
521 }
522 new_chunk->close_command = source_chunk->close_command;
523 new_chunk->fd_tracker = source_chunk->fd_tracker;
524 pthread_mutex_unlock(&source_chunk->lock);
525end:
526 return new_chunk;
527error_unlock:
528 pthread_mutex_unlock(&source_chunk->lock);
529 lttng_trace_chunk_put(new_chunk);
530 return nullptr;
531}
532
533enum lttng_trace_chunk_status lttng_trace_chunk_get_id(struct lttng_trace_chunk *chunk,
534 uint64_t *id)
535{
536 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
537
538 pthread_mutex_lock(&chunk->lock);
539 if (chunk->id.is_set) {
540 *id = chunk->id.value;
541 } else {
542 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
543 }
544 pthread_mutex_unlock(&chunk->lock);
545 return status;
546}
547
548enum lttng_trace_chunk_status
549lttng_trace_chunk_get_creation_timestamp(struct lttng_trace_chunk *chunk, time_t *creation_ts)
550
551{
552 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
553
554 pthread_mutex_lock(&chunk->lock);
555 if (chunk->timestamp_creation.is_set) {
556 *creation_ts = chunk->timestamp_creation.value;
557 } else {
558 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
559 }
560 pthread_mutex_unlock(&chunk->lock);
561 return status;
562}
563
564enum lttng_trace_chunk_status lttng_trace_chunk_get_close_timestamp(struct lttng_trace_chunk *chunk,
565 time_t *close_ts)
566{
567 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
568
569 pthread_mutex_lock(&chunk->lock);
570 if (chunk->timestamp_close.is_set) {
571 *close_ts = chunk->timestamp_close.value;
572 } else {
573 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
574 }
575 pthread_mutex_unlock(&chunk->lock);
576 return status;
577}
578
579enum lttng_trace_chunk_status lttng_trace_chunk_set_close_timestamp(struct lttng_trace_chunk *chunk,
580 time_t close_ts)
581{
582 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
583
584 pthread_mutex_lock(&chunk->lock);
585 if (!chunk->timestamp_creation.is_set) {
586 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
587 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
588 goto end;
589 }
590
591 /*
592 * Note: we do not enforce that the closing timestamp be greater or
593 * equal to the begin timestamp. These timestamps are used for
594 * generating the chunk name and should only be used in context where
595 * the monotonicity of time is not important. The source of those
596 * timestamps is NOT monotonic and represent the system calendar time,
597 * also know as the wall time.
598 */
599 if (chunk->timestamp_creation.value > close_ts) {
600 WARN("Set trace chunk close timestamp: close timestamp is before creation timestamp, begin : %ld, close : %ld",
601 chunk->timestamp_creation.value,
602 close_ts);
603 }
604
605 LTTNG_OPTIONAL_SET(&chunk->timestamp_close, close_ts);
606 if (!chunk->name_overridden) {
607 free(chunk->name);
608 chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id),
609 LTTNG_OPTIONAL_GET(chunk->timestamp_creation),
610 &close_ts);
611 if (!chunk->name) {
612 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
613 }
614 }
615end:
616 pthread_mutex_unlock(&chunk->lock);
617 return status;
618}
619
620enum lttng_trace_chunk_status lttng_trace_chunk_get_name(struct lttng_trace_chunk *chunk,
621 const char **name,
622 bool *name_overridden)
623{
624 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
625
626 pthread_mutex_lock(&chunk->lock);
627 if (name_overridden) {
628 *name_overridden = chunk->name_overridden;
629 }
630 if (!chunk->name) {
631 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
632 goto end;
633 }
634 *name = chunk->name;
635end:
636 pthread_mutex_unlock(&chunk->lock);
637 return status;
638}
639
640bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk *chunk)
641{
642 bool name_overridden;
643
644 pthread_mutex_lock(&chunk->lock);
645 name_overridden = chunk->name_overridden;
646 pthread_mutex_unlock(&chunk->lock);
647 return name_overridden;
648}
649
650static bool is_valid_chunk_name(const char *name)
651{
652 size_t len;
653
654 if (!name) {
655 return false;
656 }
657
658 len = lttng_strnlen(name, LTTNG_NAME_MAX);
659 if (len == 0 || len == LTTNG_NAME_MAX) {
660 return false;
661 }
662
663 if (strchr(name, '/') || strchr(name, '.')) {
664 return false;
665 }
666
667 return true;
668}
669
670enum lttng_trace_chunk_status lttng_trace_chunk_override_name(struct lttng_trace_chunk *chunk,
671 const char *name)
672
673{
674 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
675 char *new_name, *new_path;
676
677 DBG("Override trace chunk name from %s to %s", chunk->name, name);
678 if (!is_valid_chunk_name(name)) {
679 ERR("Attempted to set an invalid name on a trace chunk: name = %s", name ?: "NULL");
680 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
681 goto end;
682 }
683
684 pthread_mutex_lock(&chunk->lock);
685 if (!chunk->id.is_set) {
686 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
687 name);
688 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
689 goto end_unlock;
690 }
691
692 new_name = strdup(name);
693 if (!new_name) {
694 ERR("Failed to allocate new trace chunk name");
695 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
696 goto end_unlock;
697 }
698 free(chunk->name);
699 chunk->name = new_name;
700
701 new_path = strdup(name);
702 if (!new_path) {
703 ERR("Failed to allocate new trace chunk path");
704 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
705 goto end_unlock;
706 }
707 free(chunk->path);
708 chunk->path = new_path;
709
710 chunk->name_overridden = true;
711end_unlock:
712 pthread_mutex_unlock(&chunk->lock);
713end:
714 return status;
715}
716
717static enum lttng_trace_chunk_status
718lttng_trace_chunk_rename_path_no_lock(struct lttng_trace_chunk *chunk, const char *path)
719
720{
721 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
722 struct lttng_directory_handle *rename_directory = nullptr;
723 char *new_path, *old_path;
724 int ret;
725
726 if (chunk->name_overridden) {
727 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
728 goto end;
729 }
730
731 old_path = chunk->path;
732 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path, path);
733
734 if ((!old_path && !path) || (old_path && path && !strcmp(old_path, path))) {
735 goto end;
736 }
737 /*
738 * Use chunk name as path if NULL path is specified.
739 */
740 if (!path) {
741 path = chunk->name;
742 }
743
744 /* Renaming from "" to "" is not accepted. */
745 if (path[0] == '\0' && old_path[0] == '\0') {
746 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
747 goto end;
748 }
749
750 /*
751 * If a rename is performed on a chunk for which the chunk_directory
752 * is not set (yet), or the session_output_directory is not set
753 * (interacting with a relay daemon), there is no rename to perform.
754 */
755 if (!chunk->chunk_directory || !chunk->session_output_directory) {
756 goto skip_move;
757 }
758
759 if (old_path && old_path[0] != '\0' && path[0] != '\0') {
760 /* Rename chunk directory. */
761 ret = lttng_directory_handle_rename_as_user(
762 chunk->session_output_directory,
763 old_path,
764 chunk->session_output_directory,
765 path,
766 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
767 nullptr :
768 &chunk->credentials.value.user);
769 if (ret) {
770 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
771 old_path,
772 path);
773 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
774 goto end;
775 }
776 rename_directory = chunk->fd_tracker ?
777 fd_tracker_create_directory_handle_from_handle(
778 chunk->fd_tracker, chunk->session_output_directory, path) :
779 lttng_directory_handle_create_from_handle(path,
780 chunk->session_output_directory);
781 if (!rename_directory) {
782 ERR("Failed to get handle to trace chunk rename directory");
783 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
784 goto end;
785 }
786
787 /* Release old handle. */
788 lttng_directory_handle_put(chunk->chunk_directory);
789 /*
790 * Transfer new handle reference to chunk as the current chunk
791 * handle.
792 */
793 chunk->chunk_directory = rename_directory;
794 rename_directory = nullptr;
795 } else if (old_path && old_path[0] == '\0') {
796 size_t i,
797 count = lttng_dynamic_pointer_array_get_count(
798 &chunk->top_level_directories);
799
800 ret = lttng_directory_handle_create_subdirectory_as_user(
801 chunk->session_output_directory,
802 path,
803 DIR_CREATION_MODE,
804 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
805 nullptr :
806 &chunk->credentials.value.user);
807 if (ret) {
808 PERROR("Failed to create trace chunk rename directory \"%s\"", path);
809 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
810 goto end;
811 }
812
813 rename_directory = lttng_directory_handle_create_from_handle(
814 path, chunk->session_output_directory);
815 if (!rename_directory) {
816 ERR("Failed to get handle to trace chunk rename directory");
817 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
818 goto end;
819 }
820
821 /* Move toplevel directories. */
822 for (i = 0; i < count; i++) {
823 const char *top_level_name =
824 (const char *) lttng_dynamic_pointer_array_get_pointer(
825 &chunk->top_level_directories, i);
826
827 ret = lttng_directory_handle_rename_as_user(
828 chunk->chunk_directory,
829 top_level_name,
830 rename_directory,
831 top_level_name,
832 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
833 nullptr :
834 &chunk->credentials.value.user);
835 if (ret) {
836 PERROR("Failed to move \"%s\" to trace chunk rename directory",
837 top_level_name);
838 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
839 goto end;
840 }
841 }
842 /* Release old handle. */
843 lttng_directory_handle_put(chunk->chunk_directory);
844 /*
845 * Transfer new handle reference to chunk as the current chunk
846 * handle.
847 */
848 chunk->chunk_directory = rename_directory;
849 rename_directory = nullptr;
850 } else if (old_path) {
851 size_t i,
852 count = lttng_dynamic_pointer_array_get_count(
853 &chunk->top_level_directories);
854 const bool reference_acquired =
855 lttng_directory_handle_get(chunk->session_output_directory);
856
857 LTTNG_ASSERT(reference_acquired);
858 rename_directory = chunk->session_output_directory;
859
860 /* Move toplevel directories. */
861 for (i = 0; i < count; i++) {
862 const char *top_level_name =
863 (const char *) lttng_dynamic_pointer_array_get_pointer(
864 &chunk->top_level_directories, i);
865
866 ret = lttng_directory_handle_rename_as_user(
867 chunk->chunk_directory,
868 top_level_name,
869 rename_directory,
870 top_level_name,
871 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
872 nullptr :
873 &chunk->credentials.value.user);
874 if (ret) {
875 PERROR("Failed to move \"%s\" to trace chunk rename directory",
876 top_level_name);
877 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
878 goto end;
879 }
880 }
881 /* Release old handle. */
882 lttng_directory_handle_put(chunk->chunk_directory);
883 /*
884 * Transfer new handle reference to chunk as the current chunk
885 * handle.
886 */
887 chunk->chunk_directory = rename_directory;
888 rename_directory = nullptr;
889
890 /* Remove old directory. */
891 status = (lttng_trace_chunk_status) lttng_directory_handle_remove_subdirectory(
892 chunk->session_output_directory, old_path);
893 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
894 ERR("Error removing subdirectory '%s' file when deleting chunk", old_path);
895 goto end;
896 }
897 } else {
898 /* Unexpected !old_path && !path. */
899 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
900 goto end;
901 }
902
903skip_move:
904 new_path = strdup(path);
905 if (!new_path) {
906 ERR("Failed to allocate new trace chunk path");
907 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
908 goto end;
909 }
910 free(chunk->path);
911 chunk->path = new_path;
912end:
913 lttng_directory_handle_put(rename_directory);
914 return status;
915}
916
917enum lttng_trace_chunk_status lttng_trace_chunk_rename_path(struct lttng_trace_chunk *chunk,
918 const char *path)
919
920{
921 enum lttng_trace_chunk_status status;
922
923 pthread_mutex_lock(&chunk->lock);
924 status = lttng_trace_chunk_rename_path_no_lock(chunk, path);
925 pthread_mutex_unlock(&chunk->lock);
926
927 return status;
928}
929
930enum lttng_trace_chunk_status
931lttng_trace_chunk_get_credentials(struct lttng_trace_chunk *chunk,
932 struct lttng_credentials *credentials)
933{
934 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
935
936 pthread_mutex_lock(&chunk->lock);
937 if (chunk->credentials.is_set) {
938 if (chunk->credentials.value.use_current_user) {
939 LTTNG_OPTIONAL_SET(&credentials->uid, geteuid());
940 LTTNG_OPTIONAL_SET(&credentials->gid, getegid());
941 } else {
942 *credentials = chunk->credentials.value.user;
943 }
944 } else {
945 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
946 }
947 pthread_mutex_unlock(&chunk->lock);
948 return status;
949}
950
951enum lttng_trace_chunk_status
952lttng_trace_chunk_set_credentials(struct lttng_trace_chunk *chunk,
953 const struct lttng_credentials *user_credentials)
954{
955 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
956 const struct chunk_credentials credentials = {
957 .use_current_user = false,
958 .user = *user_credentials,
959 };
960
961 pthread_mutex_lock(&chunk->lock);
962 if (chunk->credentials.is_set) {
963 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
964 goto end;
965 }
966 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
967end:
968 pthread_mutex_unlock(&chunk->lock);
969 return status;
970}
971
972enum lttng_trace_chunk_status
973lttng_trace_chunk_set_credentials_current_user(struct lttng_trace_chunk *chunk)
974{
975 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
976 const struct chunk_credentials credentials = {
977 .use_current_user = true,
978 .user = {},
979 };
980
981 pthread_mutex_lock(&chunk->lock);
982 if (chunk->credentials.is_set) {
983 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
984 goto end;
985 }
986 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
987end:
988 pthread_mutex_unlock(&chunk->lock);
989 return status;
990}
991
992enum lttng_trace_chunk_status
993lttng_trace_chunk_set_as_owner(struct lttng_trace_chunk *chunk,
994 struct lttng_directory_handle *session_output_directory)
995{
996 int ret;
997 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
998 struct lttng_directory_handle *chunk_directory_handle = nullptr;
999 bool reference_acquired;
1000
1001 pthread_mutex_lock(&chunk->lock);
1002 if (chunk->mode.is_set) {
1003 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1004 goto end;
1005 }
1006 if (!chunk->credentials.is_set) {
1007 /*
1008 * Fatal error, credentials must be set before a
1009 * directory is created.
1010 */
1011 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
1012 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1013 goto end;
1014 }
1015 if (chunk->path && chunk->path[0] != '\0') {
1016 ret = lttng_directory_handle_create_subdirectory_as_user(
1017 session_output_directory,
1018 chunk->path,
1019 DIR_CREATION_MODE,
1020 !chunk->credentials.value.use_current_user ?
1021 &chunk->credentials.value.user :
1022 nullptr);
1023 if (ret) {
1024 PERROR("Failed to create chunk output directory \"%s\"", chunk->path);
1025 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1026 goto end;
1027 }
1028 chunk_directory_handle = chunk->fd_tracker ?
1029 fd_tracker_create_directory_handle_from_handle(
1030 chunk->fd_tracker, session_output_directory, chunk->path) :
1031 lttng_directory_handle_create_from_handle(chunk->path,
1032 session_output_directory);
1033 if (!chunk_directory_handle) {
1034 /* The function already logs on all error paths. */
1035 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1036 goto end;
1037 }
1038 } else {
1039 /*
1040 * A nameless chunk does not need its own output directory.
1041 * The session's output directory will be used.
1042 */
1043 reference_acquired = lttng_directory_handle_get(session_output_directory);
1044
1045 LTTNG_ASSERT(reference_acquired);
1046 chunk_directory_handle = session_output_directory;
1047 }
1048 chunk->chunk_directory = chunk_directory_handle;
1049 chunk_directory_handle = nullptr;
1050 reference_acquired = lttng_directory_handle_get(session_output_directory);
1051 LTTNG_ASSERT(reference_acquired);
1052 chunk->session_output_directory = session_output_directory;
1053 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER);
1054end:
1055 pthread_mutex_unlock(&chunk->lock);
1056 return status;
1057}
1058
1059enum lttng_trace_chunk_status
1060lttng_trace_chunk_set_as_user(struct lttng_trace_chunk *chunk,
1061 struct lttng_directory_handle *chunk_directory)
1062{
1063 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1064 bool reference_acquired;
1065
1066 pthread_mutex_lock(&chunk->lock);
1067 if (chunk->mode.is_set) {
1068 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1069 goto end;
1070 }
1071 if (!chunk->credentials.is_set) {
1072 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
1073 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1074 goto end;
1075 }
1076 reference_acquired = lttng_directory_handle_get(chunk_directory);
1077 LTTNG_ASSERT(reference_acquired);
1078 chunk->chunk_directory = chunk_directory;
1079 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER);
1080end:
1081 pthread_mutex_unlock(&chunk->lock);
1082 return status;
1083}
1084
1085enum lttng_trace_chunk_status
1086lttng_trace_chunk_get_session_output_directory_handle(struct lttng_trace_chunk *chunk,
1087 struct lttng_directory_handle **handle)
1088{
1089 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1090
1091 pthread_mutex_lock(&chunk->lock);
1092 if (!chunk->session_output_directory) {
1093 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1094 *handle = nullptr;
1095 goto end;
1096 } else {
1097 const bool reference_acquired =
1098 lttng_directory_handle_get(chunk->session_output_directory);
1099
1100 LTTNG_ASSERT(reference_acquired);
1101 *handle = chunk->session_output_directory;
1102 }
1103end:
1104 pthread_mutex_unlock(&chunk->lock);
1105 return status;
1106}
1107
1108enum lttng_trace_chunk_status
1109lttng_trace_chunk_borrow_chunk_directory_handle(struct lttng_trace_chunk *chunk,
1110 const struct lttng_directory_handle **handle)
1111{
1112 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1113
1114 pthread_mutex_lock(&chunk->lock);
1115 if (!chunk->chunk_directory) {
1116 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1117 goto end;
1118 }
1119
1120 *handle = chunk->chunk_directory;
1121end:
1122 pthread_mutex_unlock(&chunk->lock);
1123 return status;
1124}
1125
1126/* Add a top-level directory to the trace chunk if it was previously unknown. */
1127static int add_top_level_directory_unique(struct lttng_trace_chunk *chunk, const char *new_path)
1128{
1129 int ret = 0;
1130 bool found = false;
1131 size_t i, count = lttng_dynamic_pointer_array_get_count(&chunk->top_level_directories);
1132 const char *new_path_separator_pos = strchr(new_path, '/');
1133 const ptrdiff_t new_path_top_level_len =
1134 new_path_separator_pos ? new_path_separator_pos - new_path : strlen(new_path);
1135
1136 for (i = 0; i < count; i++) {
1137 const char *path = (const char *) lttng_dynamic_pointer_array_get_pointer(
1138 &chunk->top_level_directories, i);
1139 const ptrdiff_t path_top_level_len = strlen(path);
1140
1141 if (path_top_level_len != new_path_top_level_len) {
1142 continue;
1143 }
1144 if (!strncmp(path, new_path, path_top_level_len)) {
1145 found = true;
1146 break;
1147 }
1148 }
1149
1150 if (!found) {
1151 char *copy = lttng_strndup(new_path, new_path_top_level_len);
1152
1153 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1154 new_path,
1155 chunk->name ?: "(unnamed)");
1156 if (!copy) {
1157 PERROR("Failed to copy path");
1158 ret = -1;
1159 goto end;
1160 }
1161 ret = lttng_dynamic_pointer_array_add_pointer(&chunk->top_level_directories, copy);
1162 if (ret) {
1163 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1164 free(copy);
1165 goto end;
1166 }
1167 }
1168end:
1169 return ret;
1170}
1171
1172enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory(struct lttng_trace_chunk *chunk,
1173 const char *path)
1174{
1175 int ret;
1176 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1177
1178 DBG("Creating trace chunk subdirectory \"%s\"", path);
1179 pthread_mutex_lock(&chunk->lock);
1180 if (!chunk->credentials.is_set) {
1181 /*
1182 * Fatal error, credentials must be set before a
1183 * directory is created.
1184 */
1185 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1186 path);
1187 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1188 goto end;
1189 }
1190 if (!chunk->mode.is_set || chunk->mode.value != TRACE_CHUNK_MODE_OWNER) {
1191 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1192 path);
1193 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1194 goto end;
1195 }
1196 if (!chunk->chunk_directory) {
1197 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1198 path);
1199 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1200 goto end;
1201 }
1202 if (*path == '/') {
1203 ERR("Refusing to create absolute trace chunk directory \"%s\"", path);
1204 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
1205 goto end;
1206 }
1207 ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
1208 chunk->chunk_directory,
1209 path,
1210 DIR_CREATION_MODE,
1211 chunk->credentials.value.use_current_user ? nullptr :
1212 &chunk->credentials.value.user);
1213 if (ret) {
1214 PERROR("Failed to create trace chunk subdirectory \"%s\"", path);
1215 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1216 goto end;
1217 }
1218 ret = add_top_level_directory_unique(chunk, path);
1219 if (ret) {
1220 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1221 goto end;
1222 }
1223end:
1224 pthread_mutex_unlock(&chunk->lock);
1225 return status;
1226}
1227
1228/*
1229 * TODO: Implement O(1) lookup.
1230 */
1231static bool
1232lttng_trace_chunk_find_file(struct lttng_trace_chunk *chunk, const char *path, size_t *index)
1233{
1234 size_t i, count;
1235
1236 count = lttng_dynamic_pointer_array_get_count(&chunk->files);
1237 for (i = 0; i < count; i++) {
1238 const char *iter_path =
1239 (const char *) lttng_dynamic_pointer_array_get_pointer(&chunk->files, i);
1240 if (!strcmp(iter_path, path)) {
1241 if (index) {
1242 *index = i;
1243 }
1244 return true;
1245 }
1246 }
1247 return false;
1248}
1249
1250static enum lttng_trace_chunk_status lttng_trace_chunk_add_file(struct lttng_trace_chunk *chunk,
1251 const char *path)
1252{
1253 char *copy;
1254 int ret;
1255 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1256
1257 if (lttng_trace_chunk_find_file(chunk, path, nullptr)) {
1258 return LTTNG_TRACE_CHUNK_STATUS_OK;
1259 }
1260 DBG("Adding new file \"%s\" to trace chunk \"%s\"", path, chunk->name ?: "(unnamed)");
1261 copy = strdup(path);
1262 if (!copy) {
1263 PERROR("Failed to copy path");
1264 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1265 goto end;
1266 }
1267 ret = lttng_dynamic_pointer_array_add_pointer(&chunk->files, copy);
1268 if (ret) {
1269 ERR("Allocation failure while adding file to a trace chunk");
1270 free(copy);
1271 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1272 goto end;
1273 }
1274end:
1275 return status;
1276}
1277
1278static void lttng_trace_chunk_remove_file(struct lttng_trace_chunk *chunk, const char *path)
1279{
1280 size_t index;
1281 bool found;
1282 int ret;
1283
1284 found = lttng_trace_chunk_find_file(chunk, path, &index);
1285 if (!found) {
1286 return;
1287 }
1288 ret = lttng_dynamic_pointer_array_remove_pointer(&chunk->files, index);
1289 LTTNG_ASSERT(!ret);
1290}
1291
1292static enum lttng_trace_chunk_status
1293_lttng_trace_chunk_open_fs_handle_locked(struct lttng_trace_chunk *chunk,
1294 const char *file_path,
1295 int flags,
1296 mode_t mode,
1297 struct fs_handle **out_handle,
1298 bool expect_no_file)
1299{
1300 int ret;
1301 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1302
1303 DBG("Opening trace chunk file \"%s\"", file_path);
1304 if (!chunk->credentials.is_set) {
1305 /*
1306 * Fatal error, credentials must be set before a
1307 * file is created.
1308 */
1309 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1310 file_path);
1311 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1312 goto end;
1313 }
1314 if (!chunk->chunk_directory) {
1315 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1316 file_path);
1317 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1318 goto end;
1319 }
1320 status = lttng_trace_chunk_add_file(chunk, file_path);
1321 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1322 goto end;
1323 }
1324 if (chunk->fd_tracker) {
1325 LTTNG_ASSERT(chunk->credentials.value.use_current_user);
1326 *out_handle = fd_tracker_open_fs_handle(
1327 chunk->fd_tracker, chunk->chunk_directory, file_path, flags, &mode);
1328 ret = *out_handle ? 0 : -1;
1329 } else {
1330 ret = lttng_directory_handle_open_file_as_user(
1331 chunk->chunk_directory,
1332 file_path,
1333 flags,
1334 mode,
1335 chunk->credentials.value.use_current_user ? nullptr :
1336 &chunk->credentials.value.user);
1337 if (ret >= 0) {
1338 *out_handle =
1339 fs_handle_untracked_create(chunk->chunk_directory, file_path, ret);
1340 if (!*out_handle) {
1341 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1342 goto end;
1343 }
1344 }
1345 }
1346 if (ret < 0) {
1347 if (errno == ENOENT && expect_no_file) {
1348 status = LTTNG_TRACE_CHUNK_STATUS_NO_FILE;
1349 } else {
1350 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1351 file_path,
1352 flags,
1353 (int) mode);
1354 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1355 }
1356 lttng_trace_chunk_remove_file(chunk, file_path);
1357 goto end;
1358 }
1359end:
1360 return status;
1361}
1362
1363enum lttng_trace_chunk_status lttng_trace_chunk_open_fs_handle(struct lttng_trace_chunk *chunk,
1364 const char *file_path,
1365 int flags,
1366 mode_t mode,
1367 struct fs_handle **out_handle,
1368 bool expect_no_file)
1369{
1370 enum lttng_trace_chunk_status status;
1371
1372 pthread_mutex_lock(&chunk->lock);
1373 status = _lttng_trace_chunk_open_fs_handle_locked(
1374 chunk, file_path, flags, mode, out_handle, expect_no_file);
1375 pthread_mutex_unlock(&chunk->lock);
1376 return status;
1377}
1378
1379enum lttng_trace_chunk_status lttng_trace_chunk_open_file(struct lttng_trace_chunk *chunk,
1380 const char *file_path,
1381 int flags,
1382 mode_t mode,
1383 int *out_fd,
1384 bool expect_no_file)
1385{
1386 enum lttng_trace_chunk_status status;
1387 struct fs_handle *fs_handle;
1388
1389 pthread_mutex_lock(&chunk->lock);
1390 /*
1391 * Using this method is never valid when an fd_tracker is being
1392 * used since the resulting file descriptor would not be tracked.
1393 */
1394 LTTNG_ASSERT(!chunk->fd_tracker);
1395 status = _lttng_trace_chunk_open_fs_handle_locked(
1396 chunk, file_path, flags, mode, &fs_handle, expect_no_file);
1397 pthread_mutex_unlock(&chunk->lock);
1398
1399 if (status == LTTNG_TRACE_CHUNK_STATUS_OK) {
1400 *out_fd = fs_handle_get_fd(fs_handle);
1401 /*
1402 * Does not close the fd; we just "unbox" it from the fs_handle.
1403 */
1404 fs_handle_untracked_destroy(
1405 lttng::utils::container_of(fs_handle, &fs_handle_untracked::parent));
1406 }
1407
1408 return status;
1409}
1410
1411int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk, const char *file_path)
1412{
1413 int ret;
1414 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1415
1416 DBG("Unlinking trace chunk file \"%s\"", file_path);
1417 pthread_mutex_lock(&chunk->lock);
1418 if (!chunk->credentials.is_set) {
1419 /*
1420 * Fatal error, credentials must be set before a
1421 * file is unlinked.
1422 */
1423 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1424 file_path);
1425 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1426 goto end;
1427 }
1428 if (!chunk->chunk_directory) {
1429 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1430 file_path);
1431 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1432 goto end;
1433 }
1434 ret = lttng_directory_handle_unlink_file_as_user(chunk->chunk_directory,
1435 file_path,
1436 chunk->credentials.value.use_current_user ?
1437 nullptr :
1438 &chunk->credentials.value.user);
1439 if (ret < 0) {
1440 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1441 goto end;
1442 }
1443 lttng_trace_chunk_remove_file(chunk, file_path);
1444end:
1445 pthread_mutex_unlock(&chunk->lock);
1446 return status;
1447}
1448
1449static int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk *chunk,
1450 const char *path)
1451{
1452 int ret;
1453 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1454
1455 DBG("Recursively removing trace chunk directory \"%s\"", path);
1456 pthread_mutex_lock(&chunk->lock);
1457 if (!chunk->credentials.is_set) {
1458 /*
1459 * Fatal error, credentials must be set before a
1460 * directory is removed.
1461 */
1462 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1463 path);
1464 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1465 goto end;
1466 }
1467 if (!chunk->chunk_directory) {
1468 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1469 path);
1470 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1471 goto end;
1472 }
1473 ret = lttng_directory_handle_remove_subdirectory_recursive_as_user(
1474 chunk->chunk_directory,
1475 path,
1476 chunk->credentials.value.use_current_user ? nullptr :
1477 &chunk->credentials.value.user,
1478 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
1479 if (ret < 0) {
1480 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1481 goto end;
1482 }
1483end:
1484 pthread_mutex_unlock(&chunk->lock);
1485 return status;
1486}
1487
1488static int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk *trace_chunk)
1489{
1490 int ret = 0;
1491 char *archived_chunk_name = nullptr;
1492 const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id);
1493 const time_t creation_timestamp = LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation);
1494 const time_t close_timestamp = LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close);
1495 struct lttng_directory_handle *archived_chunks_directory = nullptr;
1496 enum lttng_trace_chunk_status status;
1497
1498 if (!trace_chunk->mode.is_set || trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER ||
1499 !trace_chunk->session_output_directory) {
1500 /*
1501 * This command doesn't need to run if the output is remote
1502 * or if the trace chunk is not owned by this process.
1503 */
1504 goto end;
1505 }
1506
1507 LTTNG_ASSERT(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
1508 LTTNG_ASSERT(!trace_chunk->name_overridden);
1509 LTTNG_ASSERT(trace_chunk->path);
1510
1511 archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp, &close_timestamp);
1512 if (!archived_chunk_name) {
1513 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1514 ret = -1;
1515 goto end;
1516 }
1517
1518 ret = lttng_directory_handle_create_subdirectory_as_user(
1519 trace_chunk->session_output_directory,
1520 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
1521 DIR_CREATION_MODE,
1522 !trace_chunk->credentials.value.use_current_user ?
1523 &trace_chunk->credentials.value.user :
1524 nullptr);
1525 if (ret) {
1526 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1527 "\" directory for archived trace chunks");
1528 goto end;
1529 }
1530
1531 archived_chunks_directory = trace_chunk->fd_tracker ?
1532 fd_tracker_create_directory_handle_from_handle(
1533 trace_chunk->fd_tracker,
1534 trace_chunk->session_output_directory,
1535 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY) :
1536 lttng_directory_handle_create_from_handle(DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
1537 trace_chunk->session_output_directory);
1538 if (!archived_chunks_directory) {
1539 PERROR("Failed to get handle to archived trace chunks directory");
1540 ret = -1;
1541 goto end;
1542 }
1543
1544 /*
1545 * Make sure chunk is renamed to old directory if not already done by
1546 * the creation of the next chunk. This happens if a rotation is
1547 * performed while tracing is stopped.
1548 */
1549 if (!trace_chunk->path || strcmp(trace_chunk->path, DEFAULT_CHUNK_TMP_OLD_DIRECTORY) != 0) {
1550 status = lttng_trace_chunk_rename_path_no_lock(trace_chunk,
1551 DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
1552 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1553 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
1554 ret = -1;
1555 goto end;
1556 }
1557 }
1558
1559 ret = lttng_directory_handle_rename_as_user(
1560 trace_chunk->session_output_directory,
1561 trace_chunk->path,
1562 archived_chunks_directory,
1563 archived_chunk_name,
1564 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
1565 nullptr :
1566 &trace_chunk->credentials.value.user);
1567 if (ret) {
1568 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1569 trace_chunk->path,
1570 archived_chunk_name);
1571 }
1572
1573end:
1574 lttng_directory_handle_put(archived_chunks_directory);
1575 free(archived_chunk_name);
1576 return ret;
1577}
1578
1579static int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk
1580 __attribute__((unused)))
1581{
1582 return 0;
1583}
1584
1585static int lttng_trace_chunk_delete_post_release_user(struct lttng_trace_chunk *trace_chunk)
1586{
1587 int ret = 0;
1588
1589 DBG("Trace chunk \"delete\" close command post-release (User)");
1590
1591 /* Unlink all files. */
1592 while (lttng_dynamic_pointer_array_get_count(&trace_chunk->files) != 0) {
1593 enum lttng_trace_chunk_status status;
1594 const char *path;
1595
1596 /* Remove first. */
1597 path = (const char *) lttng_dynamic_pointer_array_get_pointer(&trace_chunk->files,
1598 0);
1599 DBG("Unlink file: %s", path);
1600 status =
1601 (lttng_trace_chunk_status) lttng_trace_chunk_unlink_file(trace_chunk, path);
1602 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1603 ERR("Error unlinking file '%s' when deleting chunk", path);
1604 ret = -1;
1605 goto end;
1606 }
1607 }
1608end:
1609 return ret;
1610}
1611
1612static int lttng_trace_chunk_delete_post_release_owner(struct lttng_trace_chunk *trace_chunk)
1613{
1614 enum lttng_trace_chunk_status status;
1615 size_t i, count;
1616 int ret = 0;
1617
1618 ret = lttng_trace_chunk_delete_post_release_user(trace_chunk);
1619 if (ret) {
1620 goto end;
1621 }
1622
1623 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1624
1625 LTTNG_ASSERT(trace_chunk->session_output_directory);
1626 LTTNG_ASSERT(trace_chunk->chunk_directory);
1627
1628 /* Remove empty directories. */
1629 count = lttng_dynamic_pointer_array_get_count(&trace_chunk->top_level_directories);
1630
1631 for (i = 0; i < count; i++) {
1632 const char *top_level_name = (const char *) lttng_dynamic_pointer_array_get_pointer(
1633 &trace_chunk->top_level_directories, i);
1634
1635 status = (lttng_trace_chunk_status) lttng_trace_chunk_remove_subdirectory_recursive(
1636 trace_chunk, top_level_name);
1637 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1638 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1639 top_level_name);
1640 ret = -1;
1641 break;
1642 }
1643 }
1644 if (!ret) {
1645 lttng_directory_handle_put(trace_chunk->chunk_directory);
1646 trace_chunk->chunk_directory = nullptr;
1647
1648 if (trace_chunk->path && trace_chunk->path[0] != '\0') {
1649 status = (lttng_trace_chunk_status)
1650 lttng_directory_handle_remove_subdirectory(
1651 trace_chunk->session_output_directory, trace_chunk->path);
1652 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1653 ERR("Error removing subdirectory '%s' file when deleting chunk",
1654 trace_chunk->path);
1655 ret = -1;
1656 }
1657 }
1658 }
1659 free(trace_chunk->path);
1660 trace_chunk->path = nullptr;
1661end:
1662 return ret;
1663}
1664
1665/*
1666 * For local files, session and consumer daemons all run the delete hook. The
1667 * consumer daemons have the list of files to unlink, and technically the
1668 * session daemon is the owner of the chunk. Unlink all files owned by each
1669 * consumer daemon.
1670 */
1671static int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk *trace_chunk)
1672{
1673 if (!trace_chunk->chunk_directory) {
1674 return 0;
1675 }
1676
1677 if (trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER) {
1678 return lttng_trace_chunk_delete_post_release_owner(trace_chunk);
1679 } else {
1680 return lttng_trace_chunk_delete_post_release_user(trace_chunk);
1681 }
1682}
1683
1684enum lttng_trace_chunk_status
1685lttng_trace_chunk_get_close_command(struct lttng_trace_chunk *chunk,
1686 enum lttng_trace_chunk_command_type *command_type)
1687{
1688 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1689
1690 pthread_mutex_lock(&chunk->lock);
1691 if (chunk->close_command.is_set) {
1692 *command_type = chunk->close_command.value;
1693 status = LTTNG_TRACE_CHUNK_STATUS_OK;
1694 } else {
1695 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1696 }
1697 pthread_mutex_unlock(&chunk->lock);
1698 return status;
1699}
1700
1701enum lttng_trace_chunk_status
1702lttng_trace_chunk_set_close_command(struct lttng_trace_chunk *chunk,
1703 enum lttng_trace_chunk_command_type close_command)
1704{
1705 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1706
1707 if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
1708 close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
1709 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
1710 goto end;
1711 }
1712
1713 pthread_mutex_lock(&chunk->lock);
1714 if (chunk->close_command.is_set) {
1715 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1716 lttng_trace_chunk_command_type_str(chunk->close_command.value),
1717 lttng_trace_chunk_command_type_str(close_command));
1718 } else {
1719 DBG("Setting trace chunk close command to \"%s\"",
1720 lttng_trace_chunk_command_type_str(close_command));
1721 }
1722 /*
1723 * Unset close command for no-op for backward compatibility with relayd
1724 * 2.11.
1725 */
1726 if (close_command != LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) {
1727 LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
1728 } else {
1729 LTTNG_OPTIONAL_UNSET(&chunk->close_command);
1730 }
1731 pthread_mutex_unlock(&chunk->lock);
1732end:
1733 return status;
1734}
1735
1736const char *lttng_trace_chunk_command_type_get_name(enum lttng_trace_chunk_command_type command)
1737{
1738 switch (command) {
1739 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
1740 return "move to completed trace chunk folder";
1741 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
1742 return "no operation";
1743 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
1744 return "delete";
1745 default:
1746 abort();
1747 }
1748}
1749
1750bool lttng_trace_chunk_ids_equal(const struct lttng_trace_chunk *chunk_a,
1751 const struct lttng_trace_chunk *chunk_b)
1752{
1753 bool equal = false;
1754
1755 if (chunk_a == chunk_b) {
1756 equal = true;
1757 goto end;
1758 }
1759
1760 if (!!chunk_a ^ !!chunk_b) {
1761 goto end;
1762 }
1763
1764 if (chunk_a->id.is_set ^ chunk_a->id.is_set) {
1765 /* One id is set and not the other, thus they are not equal. */
1766 goto end;
1767 }
1768
1769 if (!chunk_a->id.is_set) {
1770 /* Both ids are unset. */
1771 equal = true;
1772 } else {
1773 equal = chunk_a->id.value == chunk_b->id.value;
1774 }
1775
1776end:
1777 return equal;
1778}
1779
1780bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
1781{
1782 return urcu_ref_get_unless_zero(&chunk->ref);
1783}
1784
1785static void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
1786{
1787 struct lttng_trace_chunk_registry_element *element =
1788 lttng::utils::container_of(node, &lttng_trace_chunk_registry_element::rcu_node);
1789
1790 free(element);
1791}
1792
1793static void lttng_trace_chunk_release(struct urcu_ref *ref)
1794{
1795 struct lttng_trace_chunk *chunk = lttng::utils::container_of(ref, &lttng_trace_chunk::ref);
1796
1797 if (chunk->close_command.is_set) {
1798 chunk_command func =
1799 close_command_get_post_release_func(chunk->close_command.value);
1800
1801 if (func(chunk)) {
1802 ERR("Trace chunk post-release command %s has failed.",
1803 lttng_trace_chunk_command_type_str(chunk->close_command.value));
1804 }
1805 }
1806
1807 if (chunk->in_registry_element) {
1808 struct lttng_trace_chunk_registry_element *element;
1809
1810 /*
1811 * Release internal chunk attributes immediately and
1812 * only use the deferred `call_rcu` work to reclaim the
1813 * storage.
1814 *
1815 * This ensures that file handles are released as soon as
1816 * possible which works around a problem we encounter with PRAM fs
1817 * mounts (and possibly other non-POSIX compliant file systems):
1818 * directories that contain files which are open can't be
1819 * rmdir().
1820 *
1821 * This means that the recording of a snapshot could be
1822 * completed, but that it would be impossible for the user to
1823 * delete it until the deferred clean-up released the file
1824 * handles to its contents.
1825 */
1826 lttng_trace_chunk_fini(chunk);
1827
1828 element = lttng::utils::container_of(chunk,
1829 &lttng_trace_chunk_registry_element::chunk);
1830 if (element->registry) {
1831 lttng::urcu::read_lock_guard read_lock;
1832 cds_lfht_del(element->registry->ht, &element->trace_chunk_registry_ht_node);
1833 call_rcu(&element->rcu_node, free_lttng_trace_chunk_registry_element);
1834 } else {
1835 /* Never published, can be free'd immediately. */
1836 free_lttng_trace_chunk_registry_element(&element->rcu_node);
1837 }
1838 } else {
1839 /* Not RCU-protected, free immediately. */
1840 lttng_trace_chunk_fini(chunk);
1841 free(chunk);
1842 }
1843}
1844
1845void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
1846{
1847 if (!chunk) {
1848 return;
1849 }
1850 LTTNG_ASSERT(chunk->ref.refcount);
1851 urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
1852}
1853
1854struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create()
1855{
1856 struct lttng_trace_chunk_registry *registry;
1857
1858 registry = zmalloc<lttng_trace_chunk_registry>();
1859 if (!registry) {
1860 goto end;
1861 }
1862
1863 registry->ht = cds_lfht_new(
1864 DEFAULT_HT_SIZE, 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, nullptr);
1865 if (!registry->ht) {
1866 goto error;
1867 }
1868end:
1869 return registry;
1870error:
1871 lttng_trace_chunk_registry_destroy(registry);
1872 return nullptr;
1873}
1874
1875void lttng_trace_chunk_registry_destroy(struct lttng_trace_chunk_registry *registry)
1876{
1877 if (!registry) {
1878 return;
1879 }
1880 if (registry->ht) {
1881 int ret = cds_lfht_destroy(registry->ht, nullptr);
1882 LTTNG_ASSERT(!ret);
1883 }
1884 free(registry);
1885}
1886
1887static struct lttng_trace_chunk_registry_element *
1888lttng_trace_chunk_registry_element_create_from_chunk(struct lttng_trace_chunk *chunk,
1889 uint64_t session_id)
1890{
1891 struct lttng_trace_chunk_registry_element *element =
1892 zmalloc<lttng_trace_chunk_registry_element>();
1893
1894 if (!element) {
1895 goto end;
1896 }
1897 cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
1898 element->session_id = session_id;
1899
1900 element->chunk = *chunk;
1901 lttng_trace_chunk_init(&element->chunk);
1902 if (chunk->session_output_directory) {
1903 /* Transferred ownership. */
1904 element->chunk.session_output_directory = chunk->session_output_directory;
1905 chunk->session_output_directory = nullptr;
1906 }
1907 if (chunk->chunk_directory) {
1908 /* Transferred ownership. */
1909 element->chunk.chunk_directory = chunk->chunk_directory;
1910 chunk->chunk_directory = nullptr;
1911 }
1912 /*
1913 * The original chunk becomes invalid; the name and path attributes are
1914 * transferred to the new chunk instance.
1915 */
1916 chunk->name = nullptr;
1917 chunk->path = nullptr;
1918 element->chunk.fd_tracker = chunk->fd_tracker;
1919 element->chunk.in_registry_element = true;
1920end:
1921 return element;
1922}
1923
1924struct lttng_trace_chunk *
1925lttng_trace_chunk_registry_publish_chunk(struct lttng_trace_chunk_registry *registry,
1926 uint64_t session_id,
1927 struct lttng_trace_chunk *chunk)
1928{
1929 bool unused;
1930
1931 return lttng_trace_chunk_registry_publish_chunk(registry, session_id, chunk, &unused);
1932}
1933
1934struct lttng_trace_chunk *
1935lttng_trace_chunk_registry_publish_chunk(struct lttng_trace_chunk_registry *registry,
1936 uint64_t session_id,
1937 struct lttng_trace_chunk *chunk,
1938 bool *previously_published)
1939{
1940 struct lttng_trace_chunk_registry_element *element;
1941 unsigned long element_hash;
1942
1943 pthread_mutex_lock(&chunk->lock);
1944 element = lttng_trace_chunk_registry_element_create_from_chunk(chunk, session_id);
1945 pthread_mutex_unlock(&chunk->lock);
1946
1947 lttng::urcu::read_lock_guard read_lock;
1948 if (!element) {
1949 goto end;
1950 }
1951 /*
1952 * chunk is now invalid, the only valid operation is a 'put' from the
1953 * caller.
1954 */
1955 chunk = nullptr;
1956 element_hash = lttng_trace_chunk_registry_element_hash(element);
1957
1958 while (true) {
1959 struct cds_lfht_node *published_node;
1960 struct lttng_trace_chunk *published_chunk;
1961 struct lttng_trace_chunk_registry_element *published_element;
1962
1963 published_node = cds_lfht_add_unique(registry->ht,
1964 element_hash,
1965 lttng_trace_chunk_registry_element_match,
1966 element,
1967 &element->trace_chunk_registry_ht_node);
1968 if (published_node == &element->trace_chunk_registry_ht_node) {
1969 /* Successfully published the new element. */
1970 element->registry = registry;
1971 /* Acquire a reference for the caller. */
1972 if (lttng_trace_chunk_get(&element->chunk)) {
1973 *previously_published = false;
1974 break;
1975 } else {
1976 /*
1977 * Another thread concurrently unpublished the
1978 * trace chunk. This is currently unexpected.
1979 *
1980 * Re-attempt to publish.
1981 */
1982 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1983 continue;
1984 }
1985 }
1986
1987 /*
1988 * An equivalent trace chunk was published before this trace
1989 * chunk. Attempt to acquire a reference to the one that was
1990 * already published and release the reference to the copy we
1991 * created if successful.
1992 */
1993 published_element = lttng::utils::container_of(
1994 published_node,
1995 &lttng_trace_chunk_registry_element::trace_chunk_registry_ht_node);
1996 published_chunk = &published_element->chunk;
1997 if (lttng_trace_chunk_get(published_chunk)) {
1998 lttng_trace_chunk_put(&element->chunk);
1999 element = published_element;
2000 *previously_published = true;
2001 break;
2002 }
2003 /*
2004 * A reference to the previously published trace chunk could not
2005 * be acquired. Hence, retry to publish our copy of the trace
2006 * chunk.
2007 */
2008 }
2009end:
2010 return element ? &element->chunk : nullptr;
2011}
2012
2013/*
2014 * Note that the caller must be registered as an RCU thread.
2015 * However, it does not need to hold the RCU read lock. The RCU read lock is
2016 * acquired to perform the look-up in the registry's hash table and held until
2017 * after a reference to the "found" trace chunk is acquired.
2018 *
2019 * IOW, holding a reference guarantees the existence of the object for the
2020 * caller.
2021 */
2022static struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
2023 const struct lttng_trace_chunk_registry *registry, uint64_t session_id, uint64_t *chunk_id)
2024{
2025 lttng_trace_chunk_registry_element target_element{};
2026
2027 target_element.chunk.id.is_set = !!chunk_id;
2028 target_element.chunk.id.value = chunk_id ? *chunk_id : 0;
2029 target_element.session_id = session_id;
2030
2031 const unsigned long element_hash = lttng_trace_chunk_registry_element_hash(&target_element);
2032 struct cds_lfht_node *published_node;
2033 struct lttng_trace_chunk_registry_element *published_element;
2034 struct lttng_trace_chunk *published_chunk = nullptr;
2035 struct cds_lfht_iter iter;
2036
2037 lttng::urcu::read_lock_guard read_lock;
2038 cds_lfht_lookup(registry->ht,
2039 element_hash,
2040 lttng_trace_chunk_registry_element_match,
2041 &target_element,
2042 &iter);
2043 published_node = cds_lfht_iter_get_node(&iter);
2044 if (!published_node) {
2045 goto end;
2046 }
2047
2048 published_element = lttng::utils::container_of(
2049 published_node, &lttng_trace_chunk_registry_element::trace_chunk_registry_ht_node);
2050 if (lttng_trace_chunk_get(&published_element->chunk)) {
2051 published_chunk = &published_element->chunk;
2052 }
2053end:
2054 return published_chunk;
2055}
2056
2057struct lttng_trace_chunk *lttng_trace_chunk_registry_find_chunk(
2058 const struct lttng_trace_chunk_registry *registry, uint64_t session_id, uint64_t chunk_id)
2059{
2060 return _lttng_trace_chunk_registry_find_chunk(registry, session_id, &chunk_id);
2061}
2062
2063int lttng_trace_chunk_registry_chunk_exists(const struct lttng_trace_chunk_registry *registry,
2064 uint64_t session_id,
2065 uint64_t chunk_id,
2066 bool *chunk_exists)
2067{
2068 int ret = 0;
2069 lttng_trace_chunk_registry_element target_element;
2070
2071 target_element.chunk.id.is_set = true;
2072 target_element.chunk.id.value = chunk_id;
2073 target_element.session_id = session_id;
2074
2075 const unsigned long element_hash = lttng_trace_chunk_registry_element_hash(&target_element);
2076 struct cds_lfht_node *published_node;
2077 struct cds_lfht_iter iter;
2078
2079 lttng::urcu::read_lock_guard read_lock;
2080 cds_lfht_lookup(registry->ht,
2081 element_hash,
2082 lttng_trace_chunk_registry_element_match,
2083 &target_element,
2084 &iter);
2085 published_node = cds_lfht_iter_get_node(&iter);
2086 if (!published_node) {
2087 *chunk_exists = false;
2088 goto end;
2089 }
2090
2091 *chunk_exists = !cds_lfht_is_node_deleted(published_node);
2092end:
2093 return ret;
2094}
2095
2096struct lttng_trace_chunk *
2097lttng_trace_chunk_registry_find_anonymous_chunk(const struct lttng_trace_chunk_registry *registry,
2098 uint64_t session_id)
2099{
2100 return _lttng_trace_chunk_registry_find_chunk(registry, session_id, nullptr);
2101}
2102
2103unsigned int
2104lttng_trace_chunk_registry_put_each_chunk(const struct lttng_trace_chunk_registry *registry)
2105{
2106 struct cds_lfht_iter iter;
2107 struct lttng_trace_chunk_registry_element *chunk_element;
2108 unsigned int trace_chunks_left = 0;
2109
2110 DBG("Releasing trace chunk registry to all trace chunks");
2111
2112 {
2113 lttng::urcu::read_lock_guard read_lock;
2114
2115 cds_lfht_for_each_entry (
2116 registry->ht, &iter, chunk_element, trace_chunk_registry_ht_node) {
2117 const char *chunk_id_str = "none";
2118 char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)];
2119
2120 pthread_mutex_lock(&chunk_element->chunk.lock);
2121 if (chunk_element->chunk.id.is_set) {
2122 int fmt_ret;
2123
2124 fmt_ret = snprintf(chunk_id_buf,
2125 sizeof(chunk_id_buf),
2126 "%" PRIu64,
2127 chunk_element->chunk.id.value);
2128 if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) {
2129 chunk_id_str = "formatting error";
2130 } else {
2131 chunk_id_str = chunk_id_buf;
2132 }
2133 }
2134
2135 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
2136 "chunk_id = %s, name = \"%s\", status = %s",
2137 chunk_element->session_id,
2138 chunk_id_str,
2139 chunk_element->chunk.name ?: "none",
2140 chunk_element->chunk.close_command.is_set ? "open" : "closed");
2141 pthread_mutex_unlock(&chunk_element->chunk.lock);
2142 lttng_trace_chunk_put(&chunk_element->chunk);
2143 trace_chunks_left++;
2144 }
2145 }
2146
2147 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left, __FUNCTION__);
2148
2149 return trace_chunks_left;
2150}
This page took 0.030325 seconds and 4 git commands to generate.