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