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