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