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