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