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