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