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