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