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