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