relayd: replace uses of block FDs by the fs_handle interface
[lttng-tools.git] / src / common / trace-chunk.c
1 /*
2 * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, version 2.1 only,
6 * as published by the Free Software Foundation.
7 *
8 * This library is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18 #include <common/compat/directory-handle.h>
19 #include <common/credentials.h>
20 #include <common/defaults.h>
21 #include <common/dynamic-array.h>
22 #include <common/error.h>
23 #include <common/fd-tracker/fd-tracker.h>
24 #include <common/fs-handle-internal.h>
25 #include <common/hashtable/hashtable.h>
26 #include <common/hashtable/utils.h>
27 #include <common/optional.h>
28 #include <common/string-utils/format.h>
29 #include <common/time.h>
30 #include <common/trace-chunk-registry.h>
31 #include <common/trace-chunk.h>
32 #include <common/utils.h>
33 #include <lttng/constant.h>
34
35 #include <inttypes.h>
36 #include <pthread.h>
37 #include <stdio.h>
38 #include <sys/stat.h>
39 #include <urcu/rculfhash.h>
40 #include <urcu/ref.h>
41
42 /*
43 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
44 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
45 */
46 #define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
47 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
48
49 enum trace_chunk_mode {
50 TRACE_CHUNK_MODE_USER,
51 TRACE_CHUNK_MODE_OWNER,
52 };
53
54 /*
55 * Callback to invoke on release of a trace chunk. Note that there is no
56 * need to 'lock' the trace chunk during the execution of these callbacks
57 * since only one thread may access a chunk during its destruction (the last
58 * to release its reference to the chunk).
59 */
60 typedef int (*chunk_command)(struct lttng_trace_chunk *trace_chunk);
61
62 /* Move a completed trace chunk to the 'completed' trace archive folder. */
63 static
64 int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk *trace_chunk);
65 /* Empty callback. */
66 static
67 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk);
68 /* Unlink old chunk files. */
69 static
70 int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk *trace_chunk);
71 static
72 enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock(
73 struct lttng_trace_chunk *chunk, const char *path);
74
75 struct chunk_credentials {
76 bool use_current_user;
77 struct lttng_credentials user;
78 };
79
80 /*
81 * NOTE: Make sure to update:
82 * - lttng_trace_chunk_copy(),
83 * - lttng_trace_chunk_registry_element_create_from_chunk()
84 * if you modify this structure.
85 */
86 struct lttng_trace_chunk {
87 pthread_mutex_t lock;
88 struct urcu_ref ref;
89 LTTNG_OPTIONAL(enum trace_chunk_mode) mode;
90 /*
91 * First-level directories created within the trace chunk.
92 * Elements are of type 'char *'.
93 *
94 * Only used by _owner_ mode chunks.
95 */
96 struct lttng_dynamic_pointer_array top_level_directories;
97 /*
98 * All files contained within the trace chunk.
99 * Array of paths (char *).
100 */
101 struct lttng_dynamic_pointer_array files;
102 /* Is contained within an lttng_trace_chunk_registry_element? */
103 bool in_registry_element;
104 bool name_overridden;
105 char *name;
106 char *path;
107 /* An unset id means the chunk is anonymous. */
108 LTTNG_OPTIONAL(uint64_t) id;
109 LTTNG_OPTIONAL(time_t) timestamp_creation;
110 LTTNG_OPTIONAL(time_t) timestamp_close;
111 LTTNG_OPTIONAL(struct chunk_credentials) credentials;
112 struct lttng_directory_handle *session_output_directory;
113 struct lttng_directory_handle *chunk_directory;
114 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type) close_command;
115 /*
116 * fd_tracker instance through which file descriptors should be
117 * created/closed.
118 *
119 * An fd_tracker always outlives any trace chunk; there is no
120 * need to perform any reference counting of that object.
121 */
122 struct fd_tracker *fd_tracker;
123 };
124
125 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
126 struct lttng_trace_chunk_registry_element {
127 struct lttng_trace_chunk chunk;
128 uint64_t session_id;
129 /* Weak and only set when added. */
130 struct lttng_trace_chunk_registry *registry;
131 struct cds_lfht_node trace_chunk_registry_ht_node;
132 /* call_rcu delayed reclaim. */
133 struct rcu_head rcu_node;
134 };
135
136 struct lttng_trace_chunk_registry {
137 struct cds_lfht *ht;
138 };
139
140 struct fs_handle_untracked {
141 struct fs_handle parent;
142 int fd;
143 struct {
144 struct lttng_directory_handle *directory_handle;
145 char *path;
146 } location;
147 };
148
149 static
150 int fs_handle_untracked_get_fd(struct fs_handle *handle);
151 static
152 void fs_handle_untracked_put_fd(struct fs_handle *handle);
153 static
154 int fs_handle_untracked_unlink(struct fs_handle *handle);
155 static
156 int fs_handle_untracked_close(struct fs_handle *handle);
157
158 static const
159 char *close_command_names[] = {
160 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
161 "move to completed chunk folder",
162 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION] =
163 "no operation",
164 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE] =
165 "delete",
166 };
167
168 static const
169 chunk_command close_command_post_release_funcs[] = {
170 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
171 lttng_trace_chunk_move_to_completed_post_release,
172 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION] =
173 lttng_trace_chunk_no_operation,
174 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE] =
175 lttng_trace_chunk_delete_post_release,
176 };
177
178 static
179 struct fs_handle *fs_handle_untracked_create(
180 struct lttng_directory_handle *directory_handle,
181 const char *path,
182 int fd)
183 {
184 struct fs_handle_untracked *handle = NULL;
185 bool reference_acquired;
186 char *path_copy = strdup(path);
187
188 assert(fd >= 0);
189 if (!path_copy) {
190 PERROR("Failed to copy file path while creating untracked filesystem handle");
191 goto end;
192 }
193
194 handle = zmalloc(sizeof(typeof(*handle)));
195 if (!handle) {
196 PERROR("Failed to allocate untracked filesystem handle");
197 goto end;
198 }
199
200 handle->parent = (typeof(handle->parent)) {
201 .get_fd = fs_handle_untracked_get_fd,
202 .put_fd = fs_handle_untracked_put_fd,
203 .unlink = fs_handle_untracked_unlink,
204 .close = fs_handle_untracked_close,
205 };
206
207 handle->fd = fd;
208 reference_acquired = lttng_directory_handle_get(directory_handle);
209 assert(reference_acquired);
210 handle->location.directory_handle = directory_handle;
211 /* Ownership is transferred. */
212 handle->location.path = path_copy;
213 path_copy = NULL;
214 end:
215 free(path_copy);
216 return handle ? &handle->parent : NULL;
217 }
218
219 static
220 int fs_handle_untracked_get_fd(struct fs_handle *_handle)
221 {
222 struct fs_handle_untracked *handle = container_of(
223 _handle, struct fs_handle_untracked, parent);
224
225 return handle->fd;
226 }
227
228 static
229 void fs_handle_untracked_put_fd(struct fs_handle *_handle)
230 {
231 /* no-op. */
232 }
233
234 static
235 int fs_handle_untracked_unlink(struct fs_handle *_handle)
236 {
237 struct fs_handle_untracked *handle = container_of(
238 _handle, struct fs_handle_untracked, parent);
239
240 return lttng_directory_handle_unlink_file(
241 handle->location.directory_handle,
242 handle->location.path);
243 }
244
245 static
246 void fs_handle_untracked_destroy(struct fs_handle_untracked *handle)
247 {
248 lttng_directory_handle_put(handle->location.directory_handle);
249 free(handle->location.path);
250 free(handle);
251 }
252
253 static
254 int fs_handle_untracked_close(struct fs_handle *_handle)
255 {
256 struct fs_handle_untracked *handle = container_of(
257 _handle, struct fs_handle_untracked, parent);
258 int ret = close(handle->fd);
259
260 fs_handle_untracked_destroy(handle);
261 return ret;
262 }
263
264 static
265 bool lttng_trace_chunk_registry_element_equals(
266 const struct lttng_trace_chunk_registry_element *a,
267 const struct lttng_trace_chunk_registry_element *b)
268 {
269 if (a->session_id != b->session_id) {
270 goto not_equal;
271 }
272 if (a->chunk.id.is_set != b->chunk.id.is_set) {
273 goto not_equal;
274 }
275 if (a->chunk.id.is_set && a->chunk.id.value != b->chunk.id.value) {
276 goto not_equal;
277 }
278 return true;
279 not_equal:
280 return false;
281 }
282
283 static
284 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node *node,
285 const void *key)
286 {
287 const struct lttng_trace_chunk_registry_element *element_a, *element_b;
288
289 element_a = (const struct lttng_trace_chunk_registry_element *) key;
290 element_b = caa_container_of(node, typeof(*element_b),
291 trace_chunk_registry_ht_node);
292 return lttng_trace_chunk_registry_element_equals(element_a, element_b);
293 }
294
295 static
296 unsigned long lttng_trace_chunk_registry_element_hash(
297 const struct lttng_trace_chunk_registry_element *element)
298 {
299 unsigned long hash = hash_key_u64(&element->session_id,
300 lttng_ht_seed);
301
302 if (element->chunk.id.is_set) {
303 hash ^= hash_key_u64(&element->chunk.id.value, lttng_ht_seed);
304 }
305
306 return hash;
307 }
308
309 static
310 char *generate_chunk_name(uint64_t chunk_id, time_t creation_timestamp,
311 const time_t *close_timestamp)
312 {
313 int ret = 0;
314 char *new_name= NULL;
315 char start_datetime[ISO8601_STR_LEN] = {};
316 /* Add 1 for a '-' prefix. */
317 char end_datetime_suffix[ISO8601_STR_LEN + 1] = {};
318
319 ret = time_to_iso8601_str(
320 creation_timestamp,
321 start_datetime, sizeof(start_datetime));
322 if (ret) {
323 ERR("Failed to format trace chunk start date time");
324 goto error;
325 }
326 if (close_timestamp) {
327 *end_datetime_suffix = '-';
328 ret = time_to_iso8601_str(
329 *close_timestamp,
330 end_datetime_suffix + 1,
331 sizeof(end_datetime_suffix) - 1);
332 if (ret) {
333 ERR("Failed to format trace chunk end date time");
334 goto error;
335 }
336 }
337 new_name = zmalloc(GENERATED_CHUNK_NAME_LEN);
338 if (!new_name) {
339 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
340 goto error;
341 }
342 ret = snprintf(new_name, GENERATED_CHUNK_NAME_LEN, "%s%s-%" PRIu64,
343 start_datetime, end_datetime_suffix, chunk_id);
344 if (ret >= GENERATED_CHUNK_NAME_LEN || ret == -1) {
345 ERR("Failed to format trace chunk name");
346 goto error;
347 }
348
349 return new_name;
350 error:
351 free(new_name);
352 return NULL;
353 }
354
355 static
356 void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk)
357 {
358 urcu_ref_init(&chunk->ref);
359 pthread_mutex_init(&chunk->lock, NULL);
360 lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free);
361 lttng_dynamic_pointer_array_init(&chunk->files, free);
362 }
363
364 static
365 void lttng_trace_chunk_fini(struct lttng_trace_chunk *chunk)
366 {
367 if (chunk->session_output_directory) {
368 lttng_directory_handle_put(
369 chunk->session_output_directory);
370 chunk->session_output_directory = NULL;
371 }
372 if (chunk->chunk_directory) {
373 lttng_directory_handle_put(chunk->chunk_directory);
374 chunk->chunk_directory = NULL;
375 }
376 free(chunk->name);
377 chunk->name = NULL;
378 free(chunk->path);
379 chunk->path = NULL;
380 lttng_dynamic_pointer_array_reset(&chunk->top_level_directories);
381 lttng_dynamic_pointer_array_reset(&chunk->files);
382 pthread_mutex_destroy(&chunk->lock);
383 }
384
385 static
386 struct lttng_trace_chunk *lttng_trace_chunk_allocate(void)
387 {
388 struct lttng_trace_chunk *chunk = NULL;
389
390 chunk = zmalloc(sizeof(*chunk));
391 if (!chunk) {
392 ERR("Failed to allocate trace chunk");
393 goto end;
394 }
395 lttng_trace_chunk_init(chunk);
396 end:
397 return chunk;
398 }
399
400 LTTNG_HIDDEN
401 struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous(void)
402 {
403 DBG("Creating anonymous trace chunk");
404 return lttng_trace_chunk_allocate();
405 }
406
407 LTTNG_HIDDEN
408 struct lttng_trace_chunk *lttng_trace_chunk_create(
409 uint64_t chunk_id, time_t chunk_creation_time, const char *path)
410 {
411 struct lttng_trace_chunk *chunk;
412 char chunk_creation_datetime_buf[16] = {};
413 const char *chunk_creation_datetime_str = "(formatting error)";
414 struct tm timeinfo_buf, *timeinfo;
415
416 timeinfo = localtime_r(&chunk_creation_time, &timeinfo_buf);
417 if (timeinfo) {
418 size_t strftime_ret;
419
420 /* Don't fail because of this; it is only used for logging. */
421 strftime_ret = strftime(chunk_creation_datetime_buf,
422 sizeof(chunk_creation_datetime_buf),
423 "%Y%m%d-%H%M%S", timeinfo);
424 if (strftime_ret) {
425 chunk_creation_datetime_str =
426 chunk_creation_datetime_buf;
427 }
428 }
429
430 DBG("Creating trace chunk: chunk_id = %" PRIu64 ", creation time = %s",
431 chunk_id, chunk_creation_datetime_str);
432 chunk = lttng_trace_chunk_allocate();
433 if (!chunk) {
434 goto end;
435 }
436
437 LTTNG_OPTIONAL_SET(&chunk->id, chunk_id);
438 LTTNG_OPTIONAL_SET(&chunk->timestamp_creation, chunk_creation_time);
439 if (chunk_id != 0) {
440 chunk->name = generate_chunk_name(chunk_id,
441 chunk_creation_time, NULL);
442 if (!chunk->name) {
443 ERR("Failed to allocate trace chunk name storage");
444 goto error;
445 }
446 }
447 if (path) {
448 chunk->path = strdup(path);
449 if (!chunk->path) {
450 goto error;
451 }
452 } else {
453 if (chunk->name) {
454 chunk->path = strdup(chunk->name);
455 if (!chunk->path) {
456 goto error;
457 }
458 }
459 }
460
461 DBG("Chunk name set to \"%s\"", chunk->name ? : "(none)");
462 end:
463 return chunk;
464 error:
465 lttng_trace_chunk_put(chunk);
466 return NULL;
467 }
468
469 LTTNG_HIDDEN
470 void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk *chunk,
471 struct fd_tracker *fd_tracker)
472 {
473 assert(!chunk->session_output_directory);
474 assert(!chunk->chunk_directory);
475 assert(lttng_dynamic_pointer_array_get_count(&chunk->files) == 0);
476 chunk->fd_tracker = fd_tracker;
477 }
478
479 LTTNG_HIDDEN
480 struct lttng_trace_chunk *lttng_trace_chunk_copy(
481 struct lttng_trace_chunk *source_chunk)
482 {
483 struct lttng_trace_chunk *new_chunk = lttng_trace_chunk_allocate();
484
485 if (!new_chunk) {
486 goto end;
487 }
488
489 pthread_mutex_lock(&source_chunk->lock);
490 /*
491 * A new chunk is always a user; it shall create no new trace
492 * subdirectories.
493 */
494 new_chunk->mode = (typeof(new_chunk->mode)) {
495 .is_set = true,
496 .value = TRACE_CHUNK_MODE_USER,
497 };
498 /*
499 * top_level_directories is not copied as it is never used
500 * by _user_ mode chunks.
501 */
502 /* The new chunk is not part of a registry (yet, at least). */
503 new_chunk->in_registry_element = false;
504 new_chunk->name_overridden = source_chunk->name_overridden;
505 if (source_chunk->name) {
506 new_chunk->name = strdup(source_chunk->name);
507 if (!new_chunk->name) {
508 ERR("Failed to copy source trace chunk name in %s()",
509 __FUNCTION__);
510 goto error_unlock;
511 }
512 }
513 if (source_chunk->path) {
514 new_chunk->path = strdup(source_chunk->path);
515 if (!new_chunk->path) {
516 ERR("Failed to copy source trace chunk path in %s()",
517 __FUNCTION__);
518 }
519 }
520 new_chunk->id = source_chunk->id;
521 new_chunk->timestamp_creation = source_chunk->timestamp_creation;
522 new_chunk->timestamp_close = source_chunk->timestamp_close;
523 new_chunk->credentials = source_chunk->credentials;
524 if (source_chunk->session_output_directory) {
525 const bool reference_acquired = lttng_directory_handle_get(
526 source_chunk->session_output_directory);
527
528 assert(reference_acquired);
529 new_chunk->session_output_directory =
530 source_chunk->session_output_directory;
531 }
532 if (source_chunk->chunk_directory) {
533 const bool reference_acquired = lttng_directory_handle_get(
534 source_chunk->chunk_directory);
535
536 assert(reference_acquired);
537 new_chunk->chunk_directory = source_chunk->chunk_directory;
538 }
539 new_chunk->close_command = source_chunk->close_command;
540 new_chunk->fd_tracker = source_chunk->fd_tracker;
541 pthread_mutex_unlock(&source_chunk->lock);
542 end:
543 return new_chunk;
544 error_unlock:
545 pthread_mutex_unlock(&source_chunk->lock);
546 lttng_trace_chunk_put(new_chunk);
547 return NULL;
548 }
549
550 LTTNG_HIDDEN
551 enum lttng_trace_chunk_status lttng_trace_chunk_get_id(
552 struct lttng_trace_chunk *chunk, uint64_t *id)
553 {
554 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
555
556 pthread_mutex_lock(&chunk->lock);
557 if (chunk->id.is_set) {
558 *id = chunk->id.value;
559 } else {
560 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
561 }
562 pthread_mutex_unlock(&chunk->lock);
563 return status;
564 }
565
566 LTTNG_HIDDEN
567 enum lttng_trace_chunk_status lttng_trace_chunk_get_creation_timestamp(
568 struct lttng_trace_chunk *chunk, time_t *creation_ts)
569
570 {
571 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
572
573 pthread_mutex_lock(&chunk->lock);
574 if (chunk->timestamp_creation.is_set) {
575 *creation_ts = chunk->timestamp_creation.value;
576 } else {
577 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
578 }
579 pthread_mutex_unlock(&chunk->lock);
580 return status;
581 }
582
583 LTTNG_HIDDEN
584 enum lttng_trace_chunk_status lttng_trace_chunk_get_close_timestamp(
585 struct lttng_trace_chunk *chunk, time_t *close_ts)
586 {
587 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
588
589 pthread_mutex_lock(&chunk->lock);
590 if (chunk->timestamp_close.is_set) {
591 *close_ts = chunk->timestamp_close.value;
592 } else {
593 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
594 }
595 pthread_mutex_unlock(&chunk->lock);
596 return status;
597 }
598
599 LTTNG_HIDDEN
600 enum lttng_trace_chunk_status lttng_trace_chunk_set_close_timestamp(
601 struct lttng_trace_chunk *chunk, time_t close_ts)
602 {
603 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
604
605 pthread_mutex_lock(&chunk->lock);
606 if (!chunk->timestamp_creation.is_set) {
607 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
608 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
609 goto end;
610 }
611 if (chunk->timestamp_creation.value > close_ts) {
612 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
613 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
614 goto end;
615 }
616 LTTNG_OPTIONAL_SET(&chunk->timestamp_close, close_ts);
617 if (!chunk->name_overridden) {
618 free(chunk->name);
619 chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id),
620 LTTNG_OPTIONAL_GET(chunk->timestamp_creation),
621 &close_ts);
622 if (!chunk->name) {
623 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
624 }
625 }
626 end:
627 pthread_mutex_unlock(&chunk->lock);
628 return status;
629 }
630
631 LTTNG_HIDDEN
632 enum lttng_trace_chunk_status lttng_trace_chunk_get_name(
633 struct lttng_trace_chunk *chunk, const char **name,
634 bool *name_overridden)
635 {
636 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
637
638 pthread_mutex_lock(&chunk->lock);
639 if (name_overridden) {
640 *name_overridden = chunk->name_overridden;
641 }
642 if (!chunk->name) {
643 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
644 goto end;
645 }
646 *name = chunk->name;
647 end:
648 pthread_mutex_unlock(&chunk->lock);
649 return status;
650 }
651
652 LTTNG_HIDDEN
653 bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk *chunk)
654 {
655 bool name_overridden;
656
657 pthread_mutex_lock(&chunk->lock);
658 name_overridden = chunk->name_overridden;
659 pthread_mutex_unlock(&chunk->lock);
660 return name_overridden;
661 }
662
663 static
664 bool is_valid_chunk_name(const char *name)
665 {
666 size_t len;
667
668 if (!name) {
669 return false;
670 }
671
672 len = lttng_strnlen(name, LTTNG_NAME_MAX);
673 if (len == 0 || len == LTTNG_NAME_MAX) {
674 return false;
675 }
676
677 if (strchr(name, '/') || strchr(name, '.')) {
678 return false;
679 }
680
681 return true;
682 }
683
684 LTTNG_HIDDEN
685 enum lttng_trace_chunk_status lttng_trace_chunk_override_name(
686 struct lttng_trace_chunk *chunk, const char *name)
687
688 {
689 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
690 char *new_name, *new_path;
691
692 DBG("Override trace chunk name from %s to %s", chunk->name, name);
693 if (!is_valid_chunk_name(name)) {
694 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
695 name ? : "NULL");
696 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
697 goto end;
698 }
699
700 pthread_mutex_lock(&chunk->lock);
701 if (!chunk->id.is_set) {
702 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
703 name);
704 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
705 goto end_unlock;
706 }
707
708 new_name = strdup(name);
709 if (!new_name) {
710 ERR("Failed to allocate new trace chunk name");
711 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
712 goto end_unlock;
713 }
714 free(chunk->name);
715 chunk->name = new_name;
716
717 new_path = strdup(name);
718 if (!new_path) {
719 ERR("Failed to allocate new trace chunk path");
720 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
721 goto end_unlock;
722 }
723 free(chunk->path);
724 chunk->path = new_path;
725
726 chunk->name_overridden = true;
727 end_unlock:
728 pthread_mutex_unlock(&chunk->lock);
729 end:
730 return status;
731 }
732
733 static
734 enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock(
735 struct lttng_trace_chunk *chunk, const char *path)
736
737 {
738 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
739 struct lttng_directory_handle *rename_directory = NULL;
740 char *new_path, *old_path;
741 int ret;
742
743 if (chunk->name_overridden) {
744 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
745 goto end;
746 }
747
748 old_path = chunk->path;
749 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path, path);
750
751 if ((!old_path && !path) ||
752 (old_path && path && !strcmp(old_path, path))) {
753 goto end;
754 }
755 /*
756 * Use chunk name as path if NULL path is specified.
757 */
758 if (!path) {
759 path = chunk->name;
760 }
761
762 /* Renaming from "" to "" is not accepted. */
763 if (path[0] == '\0' && old_path[0] == '\0') {
764 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
765 goto end;
766 }
767
768 /*
769 * If a rename is performed on a chunk for which the chunk_directory
770 * is not set (yet), or the session_output_directory is not set
771 * (interacting with a relay daemon), there is no rename to perform.
772 */
773 if (!chunk->chunk_directory ||
774 !chunk->session_output_directory) {
775 goto skip_move;
776 }
777
778 if (old_path[0] != '\0' && path[0] != '\0') {
779 /* Rename chunk directory. */
780 ret = lttng_directory_handle_rename_as_user(
781 chunk->session_output_directory,
782 old_path,
783 chunk->session_output_directory,
784 path,
785 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
786 NULL :
787 &chunk->credentials.value.user);
788 if (ret) {
789 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
790 old_path, path);
791 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
792 goto end;
793 }
794 rename_directory = lttng_directory_handle_create_from_handle(
795 path,
796 chunk->session_output_directory);
797 if (!rename_directory) {
798 ERR("Failed to get handle to trace chunk rename directory");
799 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
800 goto end;
801 }
802
803 /* Release old handle. */
804 lttng_directory_handle_put(chunk->chunk_directory);
805 /*
806 * Transfer new handle reference to chunk as the current chunk
807 * handle.
808 */
809 chunk->chunk_directory = rename_directory;
810 rename_directory = NULL;
811 } else if (old_path[0] == '\0') {
812 size_t i, count = lttng_dynamic_pointer_array_get_count(
813 &chunk->top_level_directories);
814
815 ret = lttng_directory_handle_create_subdirectory_as_user(
816 chunk->session_output_directory,
817 path,
818 DIR_CREATION_MODE,
819 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
820 NULL :
821 &chunk->credentials.value.user);
822 if (ret) {
823 PERROR("Failed to create trace chunk rename directory \"%s\"",
824 path);
825 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
826 goto end;
827 }
828
829 rename_directory = lttng_directory_handle_create_from_handle(
830 path, chunk->session_output_directory);
831 if (!rename_directory) {
832 ERR("Failed to get handle to trace chunk rename directory");
833 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
834 goto end;
835 }
836
837 /* Move toplevel directories. */
838 for (i = 0; i < count; i++) {
839 const char *top_level_name =
840 lttng_dynamic_pointer_array_get_pointer(
841 &chunk->top_level_directories, i);
842
843 ret = lttng_directory_handle_rename_as_user(
844 chunk->chunk_directory,
845 top_level_name,
846 rename_directory,
847 top_level_name,
848 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
849 NULL :
850 &chunk->credentials.value.user);
851 if (ret) {
852 PERROR("Failed to move \"%s\" to trace chunk rename directory",
853 top_level_name);
854 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
855 goto end;
856 }
857 }
858 /* Release old handle. */
859 lttng_directory_handle_put(chunk->chunk_directory);
860 /*
861 * Transfer new handle reference to chunk as the current chunk
862 * handle.
863 */
864 chunk->chunk_directory = rename_directory;
865 rename_directory = NULL;
866 } else {
867 size_t i, count = lttng_dynamic_pointer_array_get_count(
868 &chunk->top_level_directories);
869 const bool reference_acquired = lttng_directory_handle_get(
870 chunk->session_output_directory);
871
872 assert(reference_acquired);
873 rename_directory = chunk->session_output_directory;
874
875 /* Move toplevel directories. */
876 for (i = 0; i < count; i++) {
877 const char *top_level_name =
878 lttng_dynamic_pointer_array_get_pointer(
879 &chunk->top_level_directories, i);
880
881 ret = lttng_directory_handle_rename_as_user(
882 chunk->chunk_directory,
883 top_level_name,
884 rename_directory,
885 top_level_name,
886 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
887 NULL :
888 &chunk->credentials.value.user);
889 if (ret) {
890 PERROR("Failed to move \"%s\" to trace chunk rename directory",
891 top_level_name);
892 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
893 goto end;
894 }
895 }
896 /* Release old handle. */
897 lttng_directory_handle_put(chunk->chunk_directory);
898 /*
899 * Transfer new handle reference to chunk as the current chunk
900 * handle.
901 */
902 chunk->chunk_directory = rename_directory;
903 rename_directory = NULL;
904
905 /* Remove old directory. */
906 status = lttng_directory_handle_remove_subdirectory(
907 chunk->session_output_directory,
908 old_path);
909 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
910 ERR("Error removing subdirectory '%s' file when deleting chunk",
911 old_path);
912 ret = -1;
913 goto end;
914 }
915 }
916
917 skip_move:
918 if (path) {
919 new_path = strdup(path);
920 if (!new_path) {
921 ERR("Failed to allocate new trace chunk path");
922 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
923 goto end;
924 }
925 } else {
926 new_path = NULL;
927 }
928 free(chunk->path);
929 chunk->path = new_path;
930 end:
931 lttng_directory_handle_put(rename_directory);
932 return status;
933 }
934
935 LTTNG_HIDDEN
936 enum lttng_trace_chunk_status lttng_trace_chunk_rename_path(
937 struct lttng_trace_chunk *chunk, const char *path)
938
939 {
940 enum lttng_trace_chunk_status status;
941
942 pthread_mutex_lock(&chunk->lock);
943 status = lttng_trace_chunk_rename_path_no_lock(chunk, path);
944 pthread_mutex_unlock(&chunk->lock);
945
946 return status;
947 }
948
949 LTTNG_HIDDEN
950 enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials(
951 struct lttng_trace_chunk *chunk,
952 struct lttng_credentials *credentials)
953 {
954 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
955
956 pthread_mutex_lock(&chunk->lock);
957 if (chunk->credentials.is_set) {
958 if (chunk->credentials.value.use_current_user) {
959 credentials->uid = geteuid();
960 credentials->gid = getegid();
961 } else {
962 *credentials = chunk->credentials.value.user;
963 }
964 } else {
965 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
966 }
967 pthread_mutex_unlock(&chunk->lock);
968 return status;
969 }
970
971 LTTNG_HIDDEN
972 enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials(
973 struct lttng_trace_chunk *chunk,
974 const struct lttng_credentials *user_credentials)
975 {
976 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
977 const struct chunk_credentials credentials = {
978 .user = *user_credentials,
979 .use_current_user = false,
980 };
981
982 pthread_mutex_lock(&chunk->lock);
983 if (chunk->credentials.is_set) {
984 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
985 goto end;
986 }
987 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
988 end:
989 pthread_mutex_unlock(&chunk->lock);
990 return status;
991 }
992
993 LTTNG_HIDDEN
994 enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials_current_user(
995 struct lttng_trace_chunk *chunk)
996 {
997 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
998 const struct chunk_credentials credentials = {
999 .use_current_user = true,
1000 };
1001
1002 pthread_mutex_lock(&chunk->lock);
1003 if (chunk->credentials.is_set) {
1004 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1005 goto end;
1006 }
1007 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
1008 end:
1009 pthread_mutex_unlock(&chunk->lock);
1010 return status;
1011 }
1012
1013
1014 LTTNG_HIDDEN
1015 enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner(
1016 struct lttng_trace_chunk *chunk,
1017 struct lttng_directory_handle *session_output_directory)
1018 {
1019 int ret;
1020 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1021 struct lttng_directory_handle *chunk_directory_handle = NULL;
1022 bool reference_acquired;
1023
1024 pthread_mutex_lock(&chunk->lock);
1025 if (chunk->mode.is_set) {
1026 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1027 goto end;
1028 }
1029 if (!chunk->credentials.is_set) {
1030 /*
1031 * Fatal error, credentials must be set before a
1032 * directory is created.
1033 */
1034 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
1035 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1036 goto end;
1037 }
1038 if (chunk->path[0] != '\0') {
1039 ret = lttng_directory_handle_create_subdirectory_as_user(
1040 session_output_directory,
1041 chunk->path,
1042 DIR_CREATION_MODE,
1043 !chunk->credentials.value.use_current_user ?
1044 &chunk->credentials.value.user : NULL);
1045 if (ret) {
1046 PERROR("Failed to create chunk output directory \"%s\"",
1047 chunk->path);
1048 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1049 goto end;
1050 }
1051 chunk_directory_handle =
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 LTTNG_HIDDEN
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 = lttng_directory_handle_create_from_handle(
1586 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
1587 trace_chunk->session_output_directory);
1588 if (!archived_chunks_directory) {
1589 PERROR("Failed to get handle to archived trace chunks directory");
1590 ret = -1;
1591 goto end;
1592 }
1593
1594 /*
1595 * Make sure chunk is renamed to old directory if not already done by
1596 * the creation of the next chunk. This happens if a rotation is
1597 * performed while tracing is stopped.
1598 */
1599 if (!trace_chunk->path || strcmp(trace_chunk->path,
1600 DEFAULT_CHUNK_TMP_OLD_DIRECTORY)) {
1601 status = lttng_trace_chunk_rename_path_no_lock(trace_chunk,
1602 DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
1603 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1604 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
1605 ret = -1;
1606 goto end;
1607 }
1608 }
1609
1610 ret = lttng_directory_handle_rename_as_user(
1611 trace_chunk->session_output_directory,
1612 trace_chunk->path,
1613 archived_chunks_directory,
1614 archived_chunk_name,
1615 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
1616 NULL :
1617 &trace_chunk->credentials.value.user);
1618 if (ret) {
1619 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1620 trace_chunk->path,
1621 archived_chunk_name);
1622 }
1623
1624 end:
1625 lttng_directory_handle_put(archived_chunks_directory);
1626 free(archived_chunk_name);
1627 return ret;
1628 }
1629
1630 static
1631 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk)
1632 {
1633 return 0;
1634 }
1635
1636 static
1637 int lttng_trace_chunk_delete_post_release_user(
1638 struct lttng_trace_chunk *trace_chunk)
1639 {
1640 int ret = 0;
1641
1642 DBG("Trace chunk \"delete\" close command post-release (User)");
1643
1644 /* Unlink all files. */
1645 while (lttng_dynamic_pointer_array_get_count(&trace_chunk->files) != 0) {
1646 enum lttng_trace_chunk_status status;
1647 const char *path;
1648
1649 /* Remove first. */
1650 path = lttng_dynamic_pointer_array_get_pointer(
1651 &trace_chunk->files, 0);
1652 DBG("Unlink file: %s", path);
1653 status = lttng_trace_chunk_unlink_file(trace_chunk, path);
1654 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1655 ERR("Error unlinking file '%s' when deleting chunk", path);
1656 ret = -1;
1657 goto end;
1658 }
1659 }
1660 end:
1661 return ret;
1662 }
1663
1664 static
1665 int lttng_trace_chunk_delete_post_release_owner(
1666 struct lttng_trace_chunk *trace_chunk)
1667 {
1668 enum lttng_trace_chunk_status status;
1669 size_t i, count;
1670 int ret = 0;
1671
1672 ret = lttng_trace_chunk_delete_post_release_user(trace_chunk);
1673 if (ret) {
1674 goto end;
1675 }
1676
1677 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1678
1679 assert(trace_chunk->session_output_directory);
1680 assert(trace_chunk->chunk_directory);
1681
1682 /* Remove empty directories. */
1683 count = lttng_dynamic_pointer_array_get_count(
1684 &trace_chunk->top_level_directories);
1685
1686 for (i = 0; i < count; i++) {
1687 const char *top_level_name =
1688 lttng_dynamic_pointer_array_get_pointer(
1689 &trace_chunk->top_level_directories, i);
1690
1691 status = lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk, top_level_name);
1692 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1693 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1694 top_level_name);
1695 ret = -1;
1696 break;
1697 }
1698 }
1699 if (!ret) {
1700 lttng_directory_handle_put(trace_chunk->chunk_directory);
1701 trace_chunk->chunk_directory = NULL;
1702
1703 if (trace_chunk->path && trace_chunk->path[0] != '\0') {
1704 status = lttng_directory_handle_remove_subdirectory(
1705 trace_chunk->session_output_directory,
1706 trace_chunk->path);
1707 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1708 ERR("Error removing subdirectory '%s' file when deleting chunk",
1709 trace_chunk->path);
1710 ret = -1;
1711 }
1712 }
1713 }
1714 free(trace_chunk->path);
1715 trace_chunk->path = NULL;
1716 end:
1717 return ret;
1718 }
1719
1720 /*
1721 * For local files, session and consumer daemons all run the delete hook. The
1722 * consumer daemons have the list of files to unlink, and technically the
1723 * session daemon is the owner of the chunk. Unlink all files owned by each
1724 * consumer daemon.
1725 */
1726 static
1727 int lttng_trace_chunk_delete_post_release(
1728 struct lttng_trace_chunk *trace_chunk)
1729 {
1730 if (!trace_chunk->chunk_directory) {
1731 return 0;
1732 }
1733
1734 if (trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER) {
1735 return lttng_trace_chunk_delete_post_release_owner(trace_chunk);
1736 } else {
1737 return lttng_trace_chunk_delete_post_release_user(trace_chunk);
1738 }
1739 }
1740
1741 LTTNG_HIDDEN
1742 enum lttng_trace_chunk_status lttng_trace_chunk_get_close_command(
1743 struct lttng_trace_chunk *chunk,
1744 enum lttng_trace_chunk_command_type *command_type)
1745 {
1746 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1747
1748 pthread_mutex_lock(&chunk->lock);
1749 if (chunk->close_command.is_set) {
1750 *command_type = chunk->close_command.value;
1751 status = LTTNG_TRACE_CHUNK_STATUS_OK;
1752 } else {
1753 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1754 }
1755 pthread_mutex_unlock(&chunk->lock);
1756 return status;
1757 }
1758
1759 LTTNG_HIDDEN
1760 enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
1761 struct lttng_trace_chunk *chunk,
1762 enum lttng_trace_chunk_command_type close_command)
1763 {
1764 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1765
1766 if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
1767 close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
1768 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
1769 goto end;
1770 }
1771
1772 pthread_mutex_lock(&chunk->lock);
1773 if (chunk->close_command.is_set) {
1774 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1775 close_command_names[chunk->close_command.value],
1776 close_command_names[close_command]);
1777 } else {
1778 DBG("Setting trace chunk close command to \"%s\"",
1779 close_command_names[close_command]);
1780 }
1781 /*
1782 * Unset close command for no-op for backward compatibility with relayd
1783 * 2.11.
1784 */
1785 if (close_command != LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) {
1786 LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
1787 } else {
1788 LTTNG_OPTIONAL_UNSET(&chunk->close_command);
1789 }
1790 pthread_mutex_unlock(&chunk->lock);
1791 end:
1792 return status;
1793 }
1794
1795 LTTNG_HIDDEN
1796 const char *lttng_trace_chunk_command_type_get_name(
1797 enum lttng_trace_chunk_command_type command)
1798 {
1799 switch (command) {
1800 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
1801 return "move to completed trace chunk folder";
1802 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
1803 return "no operation";
1804 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
1805 return "delete";
1806 default:
1807 abort();
1808 }
1809 }
1810
1811 LTTNG_HIDDEN
1812 bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
1813 {
1814 return urcu_ref_get_unless_zero(&chunk->ref);
1815 }
1816
1817 static
1818 void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
1819 {
1820 struct lttng_trace_chunk_registry_element *element =
1821 container_of(node, typeof(*element), rcu_node);
1822
1823 lttng_trace_chunk_fini(&element->chunk);
1824 free(element);
1825 }
1826
1827 static
1828 void lttng_trace_chunk_release(struct urcu_ref *ref)
1829 {
1830 struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk),
1831 ref);
1832
1833 if (chunk->close_command.is_set) {
1834 if (close_command_post_release_funcs[
1835 chunk->close_command.value](chunk)) {
1836 ERR("Trace chunk post-release command %s has failed.",
1837 close_command_names[chunk->close_command.value]);
1838 }
1839 }
1840
1841 if (chunk->in_registry_element) {
1842 struct lttng_trace_chunk_registry_element *element;
1843
1844 element = container_of(chunk, typeof(*element), chunk);
1845 if (element->registry) {
1846 rcu_read_lock();
1847 cds_lfht_del(element->registry->ht,
1848 &element->trace_chunk_registry_ht_node);
1849 rcu_read_unlock();
1850 call_rcu(&element->rcu_node,
1851 free_lttng_trace_chunk_registry_element);
1852 } else {
1853 /* Never published, can be free'd immediately. */
1854 free_lttng_trace_chunk_registry_element(
1855 &element->rcu_node);
1856 }
1857 } else {
1858 /* Not RCU-protected, free immediately. */
1859 lttng_trace_chunk_fini(chunk);
1860 free(chunk);
1861 }
1862 }
1863
1864 LTTNG_HIDDEN
1865 void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
1866 {
1867 if (!chunk) {
1868 return;
1869 }
1870 assert(chunk->ref.refcount);
1871 urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
1872 }
1873
1874 LTTNG_HIDDEN
1875 struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void)
1876 {
1877 struct lttng_trace_chunk_registry *registry;
1878
1879 registry = zmalloc(sizeof(*registry));
1880 if (!registry) {
1881 goto end;
1882 }
1883
1884 registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
1885 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
1886 if (!registry->ht) {
1887 goto error;
1888 }
1889 end:
1890 return registry;
1891 error:
1892 lttng_trace_chunk_registry_destroy(registry);
1893 return NULL;
1894 }
1895
1896 LTTNG_HIDDEN
1897 void lttng_trace_chunk_registry_destroy(
1898 struct lttng_trace_chunk_registry *registry)
1899 {
1900 if (!registry) {
1901 return;
1902 }
1903 if (registry->ht) {
1904 int ret = cds_lfht_destroy(registry->ht, NULL);
1905 assert(!ret);
1906 }
1907 free(registry);
1908 }
1909
1910 static
1911 struct lttng_trace_chunk_registry_element *
1912 lttng_trace_chunk_registry_element_create_from_chunk(
1913 struct lttng_trace_chunk *chunk, uint64_t session_id)
1914 {
1915 struct lttng_trace_chunk_registry_element *element =
1916 zmalloc(sizeof(*element));
1917
1918 if (!element) {
1919 goto end;
1920 }
1921 cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
1922 element->session_id = session_id;
1923
1924 element->chunk = *chunk;
1925 lttng_trace_chunk_init(&element->chunk);
1926 if (chunk->session_output_directory) {
1927 /* Transferred ownership. */
1928 element->chunk.session_output_directory =
1929 chunk->session_output_directory;
1930 chunk->session_output_directory = NULL;
1931 }
1932 if (chunk->chunk_directory) {
1933 /* Transferred ownership. */
1934 element->chunk.chunk_directory = chunk->chunk_directory;
1935 chunk->chunk_directory = NULL;
1936 }
1937 /*
1938 * The original chunk becomes invalid; the name and path attributes are
1939 * transferred to the new chunk instance.
1940 */
1941 chunk->name = NULL;
1942 chunk->path = NULL;
1943 element->chunk.fd_tracker = chunk->fd_tracker;
1944 element->chunk.in_registry_element = true;
1945 end:
1946 return element;
1947 }
1948
1949 LTTNG_HIDDEN
1950 struct lttng_trace_chunk *
1951 lttng_trace_chunk_registry_publish_chunk(
1952 struct lttng_trace_chunk_registry *registry,
1953 uint64_t session_id, struct lttng_trace_chunk *chunk)
1954 {
1955 struct lttng_trace_chunk_registry_element *element;
1956 unsigned long element_hash;
1957
1958 pthread_mutex_lock(&chunk->lock);
1959 element = lttng_trace_chunk_registry_element_create_from_chunk(chunk,
1960 session_id);
1961 pthread_mutex_unlock(&chunk->lock);
1962 if (!element) {
1963 goto end;
1964 }
1965 /*
1966 * chunk is now invalid, the only valid operation is a 'put' from the
1967 * caller.
1968 */
1969 chunk = NULL;
1970 element_hash = lttng_trace_chunk_registry_element_hash(element);
1971
1972 rcu_read_lock();
1973 while (1) {
1974 struct cds_lfht_node *published_node;
1975 struct lttng_trace_chunk *published_chunk;
1976 struct lttng_trace_chunk_registry_element *published_element;
1977
1978 published_node = cds_lfht_add_unique(registry->ht,
1979 element_hash,
1980 lttng_trace_chunk_registry_element_match,
1981 element,
1982 &element->trace_chunk_registry_ht_node);
1983 if (published_node == &element->trace_chunk_registry_ht_node) {
1984 /* Successfully published the new element. */
1985 element->registry = registry;
1986 /* Acquire a reference for the caller. */
1987 if (lttng_trace_chunk_get(&element->chunk)) {
1988 break;
1989 } else {
1990 /*
1991 * Another thread concurrently unpublished the
1992 * trace chunk. This is currently unexpected.
1993 *
1994 * Re-attempt to publish.
1995 */
1996 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1997 continue;
1998 }
1999 }
2000
2001 /*
2002 * An equivalent trace chunk was published before this trace
2003 * chunk. Attempt to acquire a reference to the one that was
2004 * already published and release the reference to the copy we
2005 * created if successful.
2006 */
2007 published_element = container_of(published_node,
2008 typeof(*published_element),
2009 trace_chunk_registry_ht_node);
2010 published_chunk = &published_element->chunk;
2011 if (lttng_trace_chunk_get(published_chunk)) {
2012 lttng_trace_chunk_put(&element->chunk);
2013 element = published_element;
2014 break;
2015 }
2016 /*
2017 * A reference to the previously published trace chunk could not
2018 * be acquired. Hence, retry to publish our copy of the trace
2019 * chunk.
2020 */
2021 }
2022 rcu_read_unlock();
2023 end:
2024 return element ? &element->chunk : NULL;
2025 }
2026
2027 /*
2028 * Note that the caller must be registered as an RCU thread.
2029 * However, it does not need to hold the RCU read lock. The RCU read lock is
2030 * acquired to perform the look-up in the registry's hash table and held until
2031 * after a reference to the "found" trace chunk is acquired.
2032 *
2033 * IOW, holding a reference guarantees the existence of the object for the
2034 * caller.
2035 */
2036 static
2037 struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
2038 const struct lttng_trace_chunk_registry *registry,
2039 uint64_t session_id, uint64_t *chunk_id)
2040 {
2041 const struct lttng_trace_chunk_registry_element target_element = {
2042 .chunk.id.is_set = !!chunk_id,
2043 .chunk.id.value = chunk_id ? *chunk_id : 0,
2044 .session_id = session_id,
2045 };
2046 const unsigned long element_hash =
2047 lttng_trace_chunk_registry_element_hash(
2048 &target_element);
2049 struct cds_lfht_node *published_node;
2050 struct lttng_trace_chunk_registry_element *published_element;
2051 struct lttng_trace_chunk *published_chunk = NULL;
2052 struct cds_lfht_iter iter;
2053
2054 rcu_read_lock();
2055 cds_lfht_lookup(registry->ht,
2056 element_hash,
2057 lttng_trace_chunk_registry_element_match,
2058 &target_element,
2059 &iter);
2060 published_node = cds_lfht_iter_get_node(&iter);
2061 if (!published_node) {
2062 goto end;
2063 }
2064
2065 published_element = container_of(published_node,
2066 typeof(*published_element),
2067 trace_chunk_registry_ht_node);
2068 if (lttng_trace_chunk_get(&published_element->chunk)) {
2069 published_chunk = &published_element->chunk;
2070 }
2071 end:
2072 rcu_read_unlock();
2073 return published_chunk;
2074 }
2075
2076 LTTNG_HIDDEN
2077 struct lttng_trace_chunk *
2078 lttng_trace_chunk_registry_find_chunk(
2079 const struct lttng_trace_chunk_registry *registry,
2080 uint64_t session_id, uint64_t chunk_id)
2081 {
2082 return _lttng_trace_chunk_registry_find_chunk(registry,
2083 session_id, &chunk_id);
2084 }
2085
2086 LTTNG_HIDDEN
2087 int lttng_trace_chunk_registry_chunk_exists(
2088 const struct lttng_trace_chunk_registry *registry,
2089 uint64_t session_id, uint64_t chunk_id, bool *chunk_exists)
2090 {
2091 int ret = 0;
2092 const struct lttng_trace_chunk_registry_element target_element = {
2093 .chunk.id.is_set = true,
2094 .chunk.id.value = chunk_id,
2095 .session_id = session_id,
2096 };
2097 const unsigned long element_hash =
2098 lttng_trace_chunk_registry_element_hash(
2099 &target_element);
2100 struct cds_lfht_node *published_node;
2101 struct cds_lfht_iter iter;
2102
2103 rcu_read_lock();
2104 cds_lfht_lookup(registry->ht,
2105 element_hash,
2106 lttng_trace_chunk_registry_element_match,
2107 &target_element,
2108 &iter);
2109 published_node = cds_lfht_iter_get_node(&iter);
2110 if (!published_node) {
2111 *chunk_exists = false;
2112 goto end;
2113 }
2114
2115 *chunk_exists = !cds_lfht_is_node_deleted(published_node);
2116 end:
2117 rcu_read_unlock();
2118 return ret;
2119 }
2120
2121 LTTNG_HIDDEN
2122 struct lttng_trace_chunk *
2123 lttng_trace_chunk_registry_find_anonymous_chunk(
2124 const struct lttng_trace_chunk_registry *registry,
2125 uint64_t session_id)
2126 {
2127 return _lttng_trace_chunk_registry_find_chunk(registry,
2128 session_id, NULL);
2129 }
2130
2131 LTTNG_HIDDEN
2132 unsigned int lttng_trace_chunk_registry_put_each_chunk(
2133 const struct lttng_trace_chunk_registry *registry)
2134 {
2135 struct cds_lfht_iter iter;
2136 struct lttng_trace_chunk_registry_element *chunk_element;
2137 unsigned int trace_chunks_left = 0;
2138
2139 DBG("Releasing trace chunk registry to all trace chunks");
2140 rcu_read_lock();
2141 cds_lfht_for_each_entry(registry->ht,
2142 &iter, chunk_element, trace_chunk_registry_ht_node) {
2143 const char *chunk_id_str = "none";
2144 char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)];
2145
2146 pthread_mutex_lock(&chunk_element->chunk.lock);
2147 if (chunk_element->chunk.id.is_set) {
2148 int fmt_ret;
2149
2150 fmt_ret = snprintf(chunk_id_buf, sizeof(chunk_id_buf),
2151 "%" PRIu64,
2152 chunk_element->chunk.id.value);
2153 if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) {
2154 chunk_id_str = "formatting error";
2155 } else {
2156 chunk_id_str = chunk_id_buf;
2157 }
2158 }
2159
2160 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
2161 "chunk_id = %s, name = \"%s\", status = %s",
2162 chunk_element->session_id,
2163 chunk_id_str,
2164 chunk_element->chunk.name ? : "none",
2165 chunk_element->chunk.close_command.is_set ?
2166 "open" : "closed");
2167 pthread_mutex_unlock(&chunk_element->chunk.lock);
2168 lttng_trace_chunk_put(&chunk_element->chunk);
2169 trace_chunks_left++;
2170 }
2171 rcu_read_unlock();
2172 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left,
2173 __FUNCTION__);
2174
2175 return trace_chunks_left;
2176 }
This page took 0.091269 seconds and 6 git commands to generate.