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