Fix: trace-chunk: dereference after NULL check
[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 ret = -1;
909 goto end;
910 }
911 } else {
912 /* Unexpected !old_path && !path. */
913 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
914 goto end;
915 }
916
917 skip_move:
918 new_path = strdup(path);
919 if (!new_path) {
920 ERR("Failed to allocate new trace chunk path");
921 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
922 goto end;
923 }
924 free(chunk->path);
925 chunk->path = new_path;
926 end:
927 lttng_directory_handle_put(rename_directory);
928 return status;
929 }
930
931 LTTNG_HIDDEN
932 enum lttng_trace_chunk_status lttng_trace_chunk_rename_path(
933 struct lttng_trace_chunk *chunk, const char *path)
934
935 {
936 enum lttng_trace_chunk_status status;
937
938 pthread_mutex_lock(&chunk->lock);
939 status = lttng_trace_chunk_rename_path_no_lock(chunk, path);
940 pthread_mutex_unlock(&chunk->lock);
941
942 return status;
943 }
944
945 LTTNG_HIDDEN
946 enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials(
947 struct lttng_trace_chunk *chunk,
948 struct lttng_credentials *credentials)
949 {
950 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
951
952 pthread_mutex_lock(&chunk->lock);
953 if (chunk->credentials.is_set) {
954 if (chunk->credentials.value.use_current_user) {
955 credentials->uid = geteuid();
956 credentials->gid = getegid();
957 } else {
958 *credentials = chunk->credentials.value.user;
959 }
960 } else {
961 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
962 }
963 pthread_mutex_unlock(&chunk->lock);
964 return status;
965 }
966
967 LTTNG_HIDDEN
968 enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials(
969 struct lttng_trace_chunk *chunk,
970 const struct lttng_credentials *user_credentials)
971 {
972 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
973 const struct chunk_credentials credentials = {
974 .user = *user_credentials,
975 .use_current_user = false,
976 };
977
978 pthread_mutex_lock(&chunk->lock);
979 if (chunk->credentials.is_set) {
980 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
981 goto end;
982 }
983 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
984 end:
985 pthread_mutex_unlock(&chunk->lock);
986 return status;
987 }
988
989 LTTNG_HIDDEN
990 enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials_current_user(
991 struct lttng_trace_chunk *chunk)
992 {
993 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
994 const struct chunk_credentials credentials = {
995 .use_current_user = true,
996 };
997
998 pthread_mutex_lock(&chunk->lock);
999 if (chunk->credentials.is_set) {
1000 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1001 goto end;
1002 }
1003 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
1004 end:
1005 pthread_mutex_unlock(&chunk->lock);
1006 return status;
1007 }
1008
1009
1010 LTTNG_HIDDEN
1011 enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner(
1012 struct lttng_trace_chunk *chunk,
1013 struct lttng_directory_handle *session_output_directory)
1014 {
1015 int ret;
1016 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1017 struct lttng_directory_handle *chunk_directory_handle = NULL;
1018 bool reference_acquired;
1019
1020 pthread_mutex_lock(&chunk->lock);
1021 if (chunk->mode.is_set) {
1022 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1023 goto end;
1024 }
1025 if (!chunk->credentials.is_set) {
1026 /*
1027 * Fatal error, credentials must be set before a
1028 * directory is created.
1029 */
1030 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
1031 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1032 goto end;
1033 }
1034 if (chunk->path[0] != '\0') {
1035 ret = lttng_directory_handle_create_subdirectory_as_user(
1036 session_output_directory,
1037 chunk->path,
1038 DIR_CREATION_MODE,
1039 !chunk->credentials.value.use_current_user ?
1040 &chunk->credentials.value.user : NULL);
1041 if (ret) {
1042 PERROR("Failed to create chunk output directory \"%s\"",
1043 chunk->path);
1044 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1045 goto end;
1046 }
1047 chunk_directory_handle =
1048 chunk->fd_tracker ?
1049 fd_tracker_create_directory_handle_from_handle(
1050 chunk->fd_tracker,
1051 session_output_directory,
1052 chunk->path) :
1053 lttng_directory_handle_create_from_handle(
1054 chunk->path,
1055 session_output_directory);
1056 if (!chunk_directory_handle) {
1057 /* The function already logs on all error paths. */
1058 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1059 goto end;
1060 }
1061 } else {
1062 /*
1063 * A nameless chunk does not need its own output directory.
1064 * The session's output directory will be used.
1065 */
1066 const bool reference_acquired =
1067 lttng_directory_handle_get(
1068 session_output_directory);
1069
1070 assert(reference_acquired);
1071 chunk_directory_handle = session_output_directory;
1072 }
1073 chunk->chunk_directory = chunk_directory_handle;
1074 chunk_directory_handle = NULL;
1075 reference_acquired = lttng_directory_handle_get(
1076 session_output_directory);
1077 assert(reference_acquired);
1078 chunk->session_output_directory = session_output_directory;
1079 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER);
1080 end:
1081 pthread_mutex_unlock(&chunk->lock);
1082 return status;
1083 }
1084
1085 LTTNG_HIDDEN
1086 enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user(
1087 struct lttng_trace_chunk *chunk,
1088 struct lttng_directory_handle *chunk_directory)
1089 {
1090 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1091 bool reference_acquired;
1092
1093 pthread_mutex_lock(&chunk->lock);
1094 if (chunk->mode.is_set) {
1095 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1096 goto end;
1097 }
1098 if (!chunk->credentials.is_set) {
1099 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
1100 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1101 goto end;
1102 }
1103 reference_acquired = lttng_directory_handle_get(chunk_directory);
1104 assert(reference_acquired);
1105 chunk->chunk_directory = chunk_directory;
1106 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER);
1107 end:
1108 pthread_mutex_unlock(&chunk->lock);
1109 return status;
1110 }
1111
1112 LTTNG_HIDDEN
1113 enum lttng_trace_chunk_status
1114 lttng_trace_chunk_get_session_output_directory_handle(
1115 struct lttng_trace_chunk *chunk,
1116 struct lttng_directory_handle **handle)
1117 {
1118 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1119
1120 pthread_mutex_lock(&chunk->lock);
1121 if (!chunk->session_output_directory) {
1122 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1123 *handle = NULL;
1124 goto end;
1125 } else {
1126 const bool reference_acquired = lttng_directory_handle_get(
1127 chunk->session_output_directory);
1128
1129 assert(reference_acquired);
1130 *handle = chunk->session_output_directory;
1131 }
1132 end:
1133 pthread_mutex_unlock(&chunk->lock);
1134 return status;
1135 }
1136
1137 LTTNG_HIDDEN
1138 enum lttng_trace_chunk_status lttng_trace_chunk_borrow_chunk_directory_handle(
1139 struct lttng_trace_chunk *chunk,
1140 const struct lttng_directory_handle **handle)
1141 {
1142 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1143
1144 pthread_mutex_lock(&chunk->lock);
1145 if (!chunk->chunk_directory) {
1146 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1147 goto end;
1148 }
1149
1150 *handle = chunk->chunk_directory;
1151 end:
1152 pthread_mutex_unlock(&chunk->lock);
1153 return status;
1154 }
1155
1156 /* Add a top-level directory to the trace chunk if it was previously unknown. */
1157 static
1158 int add_top_level_directory_unique(struct lttng_trace_chunk *chunk,
1159 const char *new_path)
1160 {
1161 int ret = 0;
1162 bool found = false;
1163 size_t i, count = lttng_dynamic_pointer_array_get_count(
1164 &chunk->top_level_directories);
1165 const char *new_path_separator_pos = strchr(new_path, '/');
1166 const ptrdiff_t new_path_top_level_len = new_path_separator_pos ?
1167 new_path_separator_pos - new_path : strlen(new_path);
1168
1169 for (i = 0; i < count; i++) {
1170 const char *path = lttng_dynamic_pointer_array_get_pointer(
1171 &chunk->top_level_directories, i);
1172 const ptrdiff_t path_top_level_len = strlen(path);
1173
1174 if (path_top_level_len != new_path_top_level_len) {
1175 continue;
1176 }
1177 if (!strncmp(path, new_path, path_top_level_len)) {
1178 found = true;
1179 break;
1180 }
1181 }
1182
1183 if (!found) {
1184 char *copy = lttng_strndup(new_path, new_path_top_level_len);
1185
1186 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1187 new_path, chunk->name ? : "(unnamed)");
1188 if (!copy) {
1189 PERROR("Failed to copy path");
1190 ret = -1;
1191 goto end;
1192 }
1193 ret = lttng_dynamic_pointer_array_add_pointer(
1194 &chunk->top_level_directories, copy);
1195 if (ret) {
1196 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1197 free(copy);
1198 goto end;
1199 }
1200 }
1201 end:
1202 return ret;
1203 }
1204
1205 LTTNG_HIDDEN
1206 enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory(
1207 struct lttng_trace_chunk *chunk,
1208 const char *path)
1209 {
1210 int ret;
1211 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1212
1213 DBG("Creating trace chunk subdirectory \"%s\"", path);
1214 pthread_mutex_lock(&chunk->lock);
1215 if (!chunk->credentials.is_set) {
1216 /*
1217 * Fatal error, credentials must be set before a
1218 * directory is created.
1219 */
1220 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1221 path);
1222 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1223 goto end;
1224 }
1225 if (!chunk->mode.is_set ||
1226 chunk->mode.value != TRACE_CHUNK_MODE_OWNER) {
1227 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1228 path);
1229 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1230 goto end;
1231 }
1232 if (!chunk->chunk_directory) {
1233 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1234 path);
1235 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1236 goto end;
1237 }
1238 if (*path == '/') {
1239 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1240 path);
1241 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
1242 goto end;
1243 }
1244 ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
1245 chunk->chunk_directory, path,
1246 DIR_CREATION_MODE,
1247 chunk->credentials.value.use_current_user ?
1248 NULL : &chunk->credentials.value.user);
1249 if (ret) {
1250 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1251 path);
1252 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1253 goto end;
1254 }
1255 ret = add_top_level_directory_unique(chunk, path);
1256 if (ret) {
1257 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1258 goto end;
1259 }
1260 end:
1261 pthread_mutex_unlock(&chunk->lock);
1262 return status;
1263 }
1264
1265 /*
1266 * TODO: Implement O(1) lookup.
1267 */
1268 static
1269 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk *chunk,
1270 const char *path, size_t *index)
1271 {
1272 size_t i, count;
1273
1274 count = lttng_dynamic_pointer_array_get_count(&chunk->files);
1275 for (i = 0; i < count; i++) {
1276 const char *iter_path =
1277 lttng_dynamic_pointer_array_get_pointer(
1278 &chunk->files, i);
1279 if (!strcmp(iter_path, path)) {
1280 if (index) {
1281 *index = i;
1282 }
1283 return true;
1284 }
1285 }
1286 return false;
1287 }
1288
1289 static
1290 enum lttng_trace_chunk_status lttng_trace_chunk_add_file(
1291 struct lttng_trace_chunk *chunk,
1292 const char *path)
1293 {
1294 char *copy;
1295 int ret;
1296 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1297
1298 if (lttng_trace_chunk_find_file(chunk, path, NULL)) {
1299 return LTTNG_TRACE_CHUNK_STATUS_OK;
1300 }
1301 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1302 path, chunk->name ? : "(unnamed)");
1303 copy = strdup(path);
1304 if (!copy) {
1305 PERROR("Failed to copy path");
1306 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1307 goto end;
1308 }
1309 ret = lttng_dynamic_pointer_array_add_pointer(
1310 &chunk->files, copy);
1311 if (ret) {
1312 ERR("Allocation failure while adding file to a trace chunk");
1313 free(copy);
1314 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1315 goto end;
1316 }
1317 end:
1318 return status;
1319 }
1320
1321 static
1322 void lttng_trace_chunk_remove_file(
1323 struct lttng_trace_chunk *chunk,
1324 const char *path)
1325 {
1326 size_t index;
1327 bool found;
1328 int ret;
1329
1330 found = lttng_trace_chunk_find_file(chunk, path, &index);
1331 if (!found) {
1332 return;
1333 }
1334 ret = lttng_dynamic_pointer_array_remove_pointer(
1335 &chunk->files, index);
1336 assert(!ret);
1337 }
1338
1339 static
1340 enum lttng_trace_chunk_status _lttng_trace_chunk_open_fs_handle_locked(
1341 struct lttng_trace_chunk *chunk,
1342 const char *file_path,
1343 int flags,
1344 mode_t mode,
1345 struct fs_handle **out_handle,
1346 bool expect_no_file)
1347 {
1348 int ret;
1349 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1350
1351 DBG("Opening trace chunk file \"%s\"", file_path);
1352 if (!chunk->credentials.is_set) {
1353 /*
1354 * Fatal error, credentials must be set before a
1355 * file is created.
1356 */
1357 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1358 file_path);
1359 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1360 goto end;
1361 }
1362 if (!chunk->chunk_directory) {
1363 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1364 file_path);
1365 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1366 goto end;
1367 }
1368 status = lttng_trace_chunk_add_file(chunk, file_path);
1369 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1370 goto end;
1371 }
1372 if (chunk->fd_tracker) {
1373 assert(chunk->credentials.value.use_current_user);
1374 *out_handle = fd_tracker_open_fs_handle(chunk->fd_tracker,
1375 chunk->chunk_directory, file_path, flags, &mode);
1376 ret = *out_handle ? 0 : -1;
1377 } else {
1378 ret = lttng_directory_handle_open_file_as_user(
1379 chunk->chunk_directory, file_path, flags, mode,
1380 chunk->credentials.value.use_current_user ?
1381 NULL :
1382 &chunk->credentials.value.user);
1383 if (ret >= 0) {
1384 *out_handle = fs_handle_untracked_create(
1385 chunk->chunk_directory, file_path, ret);
1386 if (!*out_handle) {
1387 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1388 goto end;
1389 }
1390 }
1391 }
1392 if (ret < 0) {
1393 if (errno == ENOENT && expect_no_file) {
1394 status = LTTNG_TRACE_CHUNK_STATUS_NO_FILE;
1395 } else {
1396 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1397 file_path, flags, (int) mode);
1398 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1399 }
1400 lttng_trace_chunk_remove_file(chunk, file_path);
1401 goto end;
1402 }
1403 end:
1404 return status;
1405 }
1406
1407 LTTNG_HIDDEN
1408 enum lttng_trace_chunk_status lttng_trace_chunk_open_fs_handle(
1409 struct lttng_trace_chunk *chunk,
1410 const char *file_path,
1411 int flags,
1412 mode_t mode,
1413 struct fs_handle **out_handle,
1414 bool expect_no_file)
1415 {
1416 enum lttng_trace_chunk_status status;
1417
1418 pthread_mutex_lock(&chunk->lock);
1419 status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path,
1420 flags, mode, out_handle, expect_no_file);
1421 pthread_mutex_unlock(&chunk->lock);
1422 return status;
1423 }
1424
1425 LTTNG_HIDDEN
1426 enum lttng_trace_chunk_status lttng_trace_chunk_open_file(
1427 struct lttng_trace_chunk *chunk,
1428 const char *file_path,
1429 int flags,
1430 mode_t mode,
1431 int *out_fd,
1432 bool expect_no_file)
1433 {
1434 enum lttng_trace_chunk_status status;
1435 struct fs_handle *fs_handle;
1436
1437 pthread_mutex_lock(&chunk->lock);
1438 /*
1439 * Using this method is never valid when an fd_tracker is being
1440 * used since the resulting file descriptor would not be tracked.
1441 */
1442 assert(!chunk->fd_tracker);
1443 status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path,
1444 flags, mode, &fs_handle, expect_no_file);
1445 pthread_mutex_unlock(&chunk->lock);
1446
1447 if (status == LTTNG_TRACE_CHUNK_STATUS_OK) {
1448 *out_fd = fs_handle_get_fd(fs_handle);
1449 /*
1450 * Does not close the fd; we just "unbox" it from the fs_handle.
1451 */
1452 fs_handle_untracked_destroy(container_of(
1453 fs_handle, struct fs_handle_untracked, parent));
1454 }
1455
1456 return status;
1457 }
1458
1459 LTTNG_HIDDEN
1460 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk,
1461 const char *file_path)
1462 {
1463 int ret;
1464 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1465
1466 DBG("Unlinking trace chunk file \"%s\"", file_path);
1467 pthread_mutex_lock(&chunk->lock);
1468 if (!chunk->credentials.is_set) {
1469 /*
1470 * Fatal error, credentials must be set before a
1471 * file is unlinked.
1472 */
1473 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1474 file_path);
1475 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1476 goto end;
1477 }
1478 if (!chunk->chunk_directory) {
1479 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1480 file_path);
1481 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1482 goto end;
1483 }
1484 ret = lttng_directory_handle_unlink_file_as_user(
1485 chunk->chunk_directory, file_path,
1486 chunk->credentials.value.use_current_user ?
1487 NULL : &chunk->credentials.value.user);
1488 if (ret < 0) {
1489 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1490 goto end;
1491 }
1492 lttng_trace_chunk_remove_file(chunk, file_path);
1493 end:
1494 pthread_mutex_unlock(&chunk->lock);
1495 return status;
1496 }
1497
1498 LTTNG_HIDDEN
1499 int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk *chunk,
1500 const char *path)
1501 {
1502 int ret;
1503 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1504
1505 DBG("Recursively removing trace chunk directory \"%s\"", path);
1506 pthread_mutex_lock(&chunk->lock);
1507 if (!chunk->credentials.is_set) {
1508 /*
1509 * Fatal error, credentials must be set before a
1510 * directory is removed.
1511 */
1512 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1513 path);
1514 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1515 goto end;
1516 }
1517 if (!chunk->chunk_directory) {
1518 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1519 path);
1520 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1521 goto end;
1522 }
1523 ret = lttng_directory_handle_remove_subdirectory_recursive_as_user(
1524 chunk->chunk_directory, path,
1525 chunk->credentials.value.use_current_user ?
1526 NULL : &chunk->credentials.value.user,
1527 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
1528 if (ret < 0) {
1529 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1530 goto end;
1531 }
1532 end:
1533 pthread_mutex_unlock(&chunk->lock);
1534 return status;
1535 }
1536
1537 static
1538 int lttng_trace_chunk_move_to_completed_post_release(
1539 struct lttng_trace_chunk *trace_chunk)
1540 {
1541 int ret = 0;
1542 char *archived_chunk_name = NULL;
1543 const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id);
1544 const time_t creation_timestamp =
1545 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation);
1546 const time_t close_timestamp =
1547 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close);
1548 struct lttng_directory_handle *archived_chunks_directory = NULL;
1549 enum lttng_trace_chunk_status status;
1550
1551 if (!trace_chunk->mode.is_set ||
1552 trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER ||
1553 !trace_chunk->session_output_directory) {
1554 /*
1555 * This command doesn't need to run if the output is remote
1556 * or if the trace chunk is not owned by this process.
1557 */
1558 goto end;
1559 }
1560
1561 assert(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
1562 assert(!trace_chunk->name_overridden);
1563 assert(trace_chunk->path);
1564
1565 archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp,
1566 &close_timestamp);
1567 if (!archived_chunk_name) {
1568 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1569 ret = -1;
1570 goto end;
1571 }
1572
1573 ret = lttng_directory_handle_create_subdirectory_as_user(
1574 trace_chunk->session_output_directory,
1575 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
1576 DIR_CREATION_MODE,
1577 !trace_chunk->credentials.value.use_current_user ?
1578 &trace_chunk->credentials.value.user :
1579 NULL);
1580 if (ret) {
1581 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1582 "\" directory for archived trace chunks");
1583 goto end;
1584 }
1585
1586 archived_chunks_directory = trace_chunk->fd_tracker ?
1587 fd_tracker_create_directory_handle_from_handle(
1588 trace_chunk->fd_tracker,
1589 trace_chunk->session_output_directory,
1590 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY) :
1591 lttng_directory_handle_create_from_handle(
1592 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
1593 trace_chunk->session_output_directory);
1594 if (!archived_chunks_directory) {
1595 PERROR("Failed to get handle to archived trace chunks directory");
1596 ret = -1;
1597 goto end;
1598 }
1599
1600 /*
1601 * Make sure chunk is renamed to old directory if not already done by
1602 * the creation of the next chunk. This happens if a rotation is
1603 * performed while tracing is stopped.
1604 */
1605 if (!trace_chunk->path || strcmp(trace_chunk->path,
1606 DEFAULT_CHUNK_TMP_OLD_DIRECTORY)) {
1607 status = lttng_trace_chunk_rename_path_no_lock(trace_chunk,
1608 DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
1609 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1610 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
1611 ret = -1;
1612 goto end;
1613 }
1614 }
1615
1616 ret = lttng_directory_handle_rename_as_user(
1617 trace_chunk->session_output_directory,
1618 trace_chunk->path,
1619 archived_chunks_directory,
1620 archived_chunk_name,
1621 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
1622 NULL :
1623 &trace_chunk->credentials.value.user);
1624 if (ret) {
1625 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1626 trace_chunk->path,
1627 archived_chunk_name);
1628 }
1629
1630 end:
1631 lttng_directory_handle_put(archived_chunks_directory);
1632 free(archived_chunk_name);
1633 return ret;
1634 }
1635
1636 static
1637 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk)
1638 {
1639 return 0;
1640 }
1641
1642 static
1643 int lttng_trace_chunk_delete_post_release_user(
1644 struct lttng_trace_chunk *trace_chunk)
1645 {
1646 int ret = 0;
1647
1648 DBG("Trace chunk \"delete\" close command post-release (User)");
1649
1650 /* Unlink all files. */
1651 while (lttng_dynamic_pointer_array_get_count(&trace_chunk->files) != 0) {
1652 enum lttng_trace_chunk_status status;
1653 const char *path;
1654
1655 /* Remove first. */
1656 path = lttng_dynamic_pointer_array_get_pointer(
1657 &trace_chunk->files, 0);
1658 DBG("Unlink file: %s", path);
1659 status = lttng_trace_chunk_unlink_file(trace_chunk, path);
1660 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1661 ERR("Error unlinking file '%s' when deleting chunk", path);
1662 ret = -1;
1663 goto end;
1664 }
1665 }
1666 end:
1667 return ret;
1668 }
1669
1670 static
1671 int lttng_trace_chunk_delete_post_release_owner(
1672 struct lttng_trace_chunk *trace_chunk)
1673 {
1674 enum lttng_trace_chunk_status status;
1675 size_t i, count;
1676 int ret = 0;
1677
1678 ret = lttng_trace_chunk_delete_post_release_user(trace_chunk);
1679 if (ret) {
1680 goto end;
1681 }
1682
1683 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1684
1685 assert(trace_chunk->session_output_directory);
1686 assert(trace_chunk->chunk_directory);
1687
1688 /* Remove empty directories. */
1689 count = lttng_dynamic_pointer_array_get_count(
1690 &trace_chunk->top_level_directories);
1691
1692 for (i = 0; i < count; i++) {
1693 const char *top_level_name =
1694 lttng_dynamic_pointer_array_get_pointer(
1695 &trace_chunk->top_level_directories, i);
1696
1697 status = lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk, top_level_name);
1698 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1699 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1700 top_level_name);
1701 ret = -1;
1702 break;
1703 }
1704 }
1705 if (!ret) {
1706 lttng_directory_handle_put(trace_chunk->chunk_directory);
1707 trace_chunk->chunk_directory = NULL;
1708
1709 if (trace_chunk->path && trace_chunk->path[0] != '\0') {
1710 status = lttng_directory_handle_remove_subdirectory(
1711 trace_chunk->session_output_directory,
1712 trace_chunk->path);
1713 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1714 ERR("Error removing subdirectory '%s' file when deleting chunk",
1715 trace_chunk->path);
1716 ret = -1;
1717 }
1718 }
1719 }
1720 free(trace_chunk->path);
1721 trace_chunk->path = NULL;
1722 end:
1723 return ret;
1724 }
1725
1726 /*
1727 * For local files, session and consumer daemons all run the delete hook. The
1728 * consumer daemons have the list of files to unlink, and technically the
1729 * session daemon is the owner of the chunk. Unlink all files owned by each
1730 * consumer daemon.
1731 */
1732 static
1733 int lttng_trace_chunk_delete_post_release(
1734 struct lttng_trace_chunk *trace_chunk)
1735 {
1736 if (!trace_chunk->chunk_directory) {
1737 return 0;
1738 }
1739
1740 if (trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER) {
1741 return lttng_trace_chunk_delete_post_release_owner(trace_chunk);
1742 } else {
1743 return lttng_trace_chunk_delete_post_release_user(trace_chunk);
1744 }
1745 }
1746
1747 LTTNG_HIDDEN
1748 enum lttng_trace_chunk_status lttng_trace_chunk_get_close_command(
1749 struct lttng_trace_chunk *chunk,
1750 enum lttng_trace_chunk_command_type *command_type)
1751 {
1752 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1753
1754 pthread_mutex_lock(&chunk->lock);
1755 if (chunk->close_command.is_set) {
1756 *command_type = chunk->close_command.value;
1757 status = LTTNG_TRACE_CHUNK_STATUS_OK;
1758 } else {
1759 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1760 }
1761 pthread_mutex_unlock(&chunk->lock);
1762 return status;
1763 }
1764
1765 LTTNG_HIDDEN
1766 enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
1767 struct lttng_trace_chunk *chunk,
1768 enum lttng_trace_chunk_command_type close_command)
1769 {
1770 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1771
1772 if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
1773 close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
1774 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
1775 goto end;
1776 }
1777
1778 pthread_mutex_lock(&chunk->lock);
1779 if (chunk->close_command.is_set) {
1780 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1781 close_command_names[chunk->close_command.value],
1782 close_command_names[close_command]);
1783 } else {
1784 DBG("Setting trace chunk close command to \"%s\"",
1785 close_command_names[close_command]);
1786 }
1787 /*
1788 * Unset close command for no-op for backward compatibility with relayd
1789 * 2.11.
1790 */
1791 if (close_command != LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) {
1792 LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
1793 } else {
1794 LTTNG_OPTIONAL_UNSET(&chunk->close_command);
1795 }
1796 pthread_mutex_unlock(&chunk->lock);
1797 end:
1798 return status;
1799 }
1800
1801 LTTNG_HIDDEN
1802 const char *lttng_trace_chunk_command_type_get_name(
1803 enum lttng_trace_chunk_command_type command)
1804 {
1805 switch (command) {
1806 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
1807 return "move to completed trace chunk folder";
1808 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
1809 return "no operation";
1810 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
1811 return "delete";
1812 default:
1813 abort();
1814 }
1815 }
1816
1817 LTTNG_HIDDEN
1818 bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
1819 {
1820 return urcu_ref_get_unless_zero(&chunk->ref);
1821 }
1822
1823 static
1824 void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
1825 {
1826 struct lttng_trace_chunk_registry_element *element =
1827 container_of(node, typeof(*element), rcu_node);
1828
1829 lttng_trace_chunk_fini(&element->chunk);
1830 free(element);
1831 }
1832
1833 static
1834 void lttng_trace_chunk_release(struct urcu_ref *ref)
1835 {
1836 struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk),
1837 ref);
1838
1839 if (chunk->close_command.is_set) {
1840 if (close_command_post_release_funcs[
1841 chunk->close_command.value](chunk)) {
1842 ERR("Trace chunk post-release command %s has failed.",
1843 close_command_names[chunk->close_command.value]);
1844 }
1845 }
1846
1847 if (chunk->in_registry_element) {
1848 struct lttng_trace_chunk_registry_element *element;
1849
1850 element = container_of(chunk, typeof(*element), chunk);
1851 if (element->registry) {
1852 rcu_read_lock();
1853 cds_lfht_del(element->registry->ht,
1854 &element->trace_chunk_registry_ht_node);
1855 rcu_read_unlock();
1856 call_rcu(&element->rcu_node,
1857 free_lttng_trace_chunk_registry_element);
1858 } else {
1859 /* Never published, can be free'd immediately. */
1860 free_lttng_trace_chunk_registry_element(
1861 &element->rcu_node);
1862 }
1863 } else {
1864 /* Not RCU-protected, free immediately. */
1865 lttng_trace_chunk_fini(chunk);
1866 free(chunk);
1867 }
1868 }
1869
1870 LTTNG_HIDDEN
1871 void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
1872 {
1873 if (!chunk) {
1874 return;
1875 }
1876 assert(chunk->ref.refcount);
1877 urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
1878 }
1879
1880 LTTNG_HIDDEN
1881 struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void)
1882 {
1883 struct lttng_trace_chunk_registry *registry;
1884
1885 registry = zmalloc(sizeof(*registry));
1886 if (!registry) {
1887 goto end;
1888 }
1889
1890 registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
1891 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
1892 if (!registry->ht) {
1893 goto error;
1894 }
1895 end:
1896 return registry;
1897 error:
1898 lttng_trace_chunk_registry_destroy(registry);
1899 return NULL;
1900 }
1901
1902 LTTNG_HIDDEN
1903 void lttng_trace_chunk_registry_destroy(
1904 struct lttng_trace_chunk_registry *registry)
1905 {
1906 if (!registry) {
1907 return;
1908 }
1909 if (registry->ht) {
1910 int ret = cds_lfht_destroy(registry->ht, NULL);
1911 assert(!ret);
1912 }
1913 free(registry);
1914 }
1915
1916 static
1917 struct lttng_trace_chunk_registry_element *
1918 lttng_trace_chunk_registry_element_create_from_chunk(
1919 struct lttng_trace_chunk *chunk, uint64_t session_id)
1920 {
1921 struct lttng_trace_chunk_registry_element *element =
1922 zmalloc(sizeof(*element));
1923
1924 if (!element) {
1925 goto end;
1926 }
1927 cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
1928 element->session_id = session_id;
1929
1930 element->chunk = *chunk;
1931 lttng_trace_chunk_init(&element->chunk);
1932 if (chunk->session_output_directory) {
1933 /* Transferred ownership. */
1934 element->chunk.session_output_directory =
1935 chunk->session_output_directory;
1936 chunk->session_output_directory = NULL;
1937 }
1938 if (chunk->chunk_directory) {
1939 /* Transferred ownership. */
1940 element->chunk.chunk_directory = chunk->chunk_directory;
1941 chunk->chunk_directory = NULL;
1942 }
1943 /*
1944 * The original chunk becomes invalid; the name and path attributes are
1945 * transferred to the new chunk instance.
1946 */
1947 chunk->name = NULL;
1948 chunk->path = NULL;
1949 element->chunk.fd_tracker = chunk->fd_tracker;
1950 element->chunk.in_registry_element = true;
1951 end:
1952 return element;
1953 }
1954
1955 LTTNG_HIDDEN
1956 struct lttng_trace_chunk *
1957 lttng_trace_chunk_registry_publish_chunk(
1958 struct lttng_trace_chunk_registry *registry,
1959 uint64_t session_id, struct lttng_trace_chunk *chunk)
1960 {
1961 struct lttng_trace_chunk_registry_element *element;
1962 unsigned long element_hash;
1963
1964 pthread_mutex_lock(&chunk->lock);
1965 element = lttng_trace_chunk_registry_element_create_from_chunk(chunk,
1966 session_id);
1967 pthread_mutex_unlock(&chunk->lock);
1968 if (!element) {
1969 goto end;
1970 }
1971 /*
1972 * chunk is now invalid, the only valid operation is a 'put' from the
1973 * caller.
1974 */
1975 chunk = NULL;
1976 element_hash = lttng_trace_chunk_registry_element_hash(element);
1977
1978 rcu_read_lock();
1979 while (1) {
1980 struct cds_lfht_node *published_node;
1981 struct lttng_trace_chunk *published_chunk;
1982 struct lttng_trace_chunk_registry_element *published_element;
1983
1984 published_node = cds_lfht_add_unique(registry->ht,
1985 element_hash,
1986 lttng_trace_chunk_registry_element_match,
1987 element,
1988 &element->trace_chunk_registry_ht_node);
1989 if (published_node == &element->trace_chunk_registry_ht_node) {
1990 /* Successfully published the new element. */
1991 element->registry = registry;
1992 /* Acquire a reference for the caller. */
1993 if (lttng_trace_chunk_get(&element->chunk)) {
1994 break;
1995 } else {
1996 /*
1997 * Another thread concurrently unpublished the
1998 * trace chunk. This is currently unexpected.
1999 *
2000 * Re-attempt to publish.
2001 */
2002 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
2003 continue;
2004 }
2005 }
2006
2007 /*
2008 * An equivalent trace chunk was published before this trace
2009 * chunk. Attempt to acquire a reference to the one that was
2010 * already published and release the reference to the copy we
2011 * created if successful.
2012 */
2013 published_element = container_of(published_node,
2014 typeof(*published_element),
2015 trace_chunk_registry_ht_node);
2016 published_chunk = &published_element->chunk;
2017 if (lttng_trace_chunk_get(published_chunk)) {
2018 lttng_trace_chunk_put(&element->chunk);
2019 element = published_element;
2020 break;
2021 }
2022 /*
2023 * A reference to the previously published trace chunk could not
2024 * be acquired. Hence, retry to publish our copy of the trace
2025 * chunk.
2026 */
2027 }
2028 rcu_read_unlock();
2029 end:
2030 return element ? &element->chunk : NULL;
2031 }
2032
2033 /*
2034 * Note that the caller must be registered as an RCU thread.
2035 * However, it does not need to hold the RCU read lock. The RCU read lock is
2036 * acquired to perform the look-up in the registry's hash table and held until
2037 * after a reference to the "found" trace chunk is acquired.
2038 *
2039 * IOW, holding a reference guarantees the existence of the object for the
2040 * caller.
2041 */
2042 static
2043 struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
2044 const struct lttng_trace_chunk_registry *registry,
2045 uint64_t session_id, uint64_t *chunk_id)
2046 {
2047 const struct lttng_trace_chunk_registry_element target_element = {
2048 .chunk.id.is_set = !!chunk_id,
2049 .chunk.id.value = chunk_id ? *chunk_id : 0,
2050 .session_id = session_id,
2051 };
2052 const unsigned long element_hash =
2053 lttng_trace_chunk_registry_element_hash(
2054 &target_element);
2055 struct cds_lfht_node *published_node;
2056 struct lttng_trace_chunk_registry_element *published_element;
2057 struct lttng_trace_chunk *published_chunk = NULL;
2058 struct cds_lfht_iter iter;
2059
2060 rcu_read_lock();
2061 cds_lfht_lookup(registry->ht,
2062 element_hash,
2063 lttng_trace_chunk_registry_element_match,
2064 &target_element,
2065 &iter);
2066 published_node = cds_lfht_iter_get_node(&iter);
2067 if (!published_node) {
2068 goto end;
2069 }
2070
2071 published_element = container_of(published_node,
2072 typeof(*published_element),
2073 trace_chunk_registry_ht_node);
2074 if (lttng_trace_chunk_get(&published_element->chunk)) {
2075 published_chunk = &published_element->chunk;
2076 }
2077 end:
2078 rcu_read_unlock();
2079 return published_chunk;
2080 }
2081
2082 LTTNG_HIDDEN
2083 struct lttng_trace_chunk *
2084 lttng_trace_chunk_registry_find_chunk(
2085 const struct lttng_trace_chunk_registry *registry,
2086 uint64_t session_id, uint64_t chunk_id)
2087 {
2088 return _lttng_trace_chunk_registry_find_chunk(registry,
2089 session_id, &chunk_id);
2090 }
2091
2092 LTTNG_HIDDEN
2093 int lttng_trace_chunk_registry_chunk_exists(
2094 const struct lttng_trace_chunk_registry *registry,
2095 uint64_t session_id, uint64_t chunk_id, bool *chunk_exists)
2096 {
2097 int ret = 0;
2098 const struct lttng_trace_chunk_registry_element target_element = {
2099 .chunk.id.is_set = true,
2100 .chunk.id.value = chunk_id,
2101 .session_id = session_id,
2102 };
2103 const unsigned long element_hash =
2104 lttng_trace_chunk_registry_element_hash(
2105 &target_element);
2106 struct cds_lfht_node *published_node;
2107 struct cds_lfht_iter iter;
2108
2109 rcu_read_lock();
2110 cds_lfht_lookup(registry->ht,
2111 element_hash,
2112 lttng_trace_chunk_registry_element_match,
2113 &target_element,
2114 &iter);
2115 published_node = cds_lfht_iter_get_node(&iter);
2116 if (!published_node) {
2117 *chunk_exists = false;
2118 goto end;
2119 }
2120
2121 *chunk_exists = !cds_lfht_is_node_deleted(published_node);
2122 end:
2123 rcu_read_unlock();
2124 return ret;
2125 }
2126
2127 LTTNG_HIDDEN
2128 struct lttng_trace_chunk *
2129 lttng_trace_chunk_registry_find_anonymous_chunk(
2130 const struct lttng_trace_chunk_registry *registry,
2131 uint64_t session_id)
2132 {
2133 return _lttng_trace_chunk_registry_find_chunk(registry,
2134 session_id, NULL);
2135 }
2136
2137 LTTNG_HIDDEN
2138 unsigned int lttng_trace_chunk_registry_put_each_chunk(
2139 const struct lttng_trace_chunk_registry *registry)
2140 {
2141 struct cds_lfht_iter iter;
2142 struct lttng_trace_chunk_registry_element *chunk_element;
2143 unsigned int trace_chunks_left = 0;
2144
2145 DBG("Releasing trace chunk registry to all trace chunks");
2146 rcu_read_lock();
2147 cds_lfht_for_each_entry(registry->ht,
2148 &iter, chunk_element, trace_chunk_registry_ht_node) {
2149 const char *chunk_id_str = "none";
2150 char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)];
2151
2152 pthread_mutex_lock(&chunk_element->chunk.lock);
2153 if (chunk_element->chunk.id.is_set) {
2154 int fmt_ret;
2155
2156 fmt_ret = snprintf(chunk_id_buf, sizeof(chunk_id_buf),
2157 "%" PRIu64,
2158 chunk_element->chunk.id.value);
2159 if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) {
2160 chunk_id_str = "formatting error";
2161 } else {
2162 chunk_id_str = chunk_id_buf;
2163 }
2164 }
2165
2166 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
2167 "chunk_id = %s, name = \"%s\", status = %s",
2168 chunk_element->session_id,
2169 chunk_id_str,
2170 chunk_element->chunk.name ? : "none",
2171 chunk_element->chunk.close_command.is_set ?
2172 "open" : "closed");
2173 pthread_mutex_unlock(&chunk_element->chunk.lock);
2174 lttng_trace_chunk_put(&chunk_element->chunk);
2175 trace_chunks_left++;
2176 }
2177 rcu_read_unlock();
2178 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left,
2179 __FUNCTION__);
2180
2181 return trace_chunks_left;
2182 }
This page took 0.14775 seconds and 4 git commands to generate.