common: compile libcommon as C++
[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
2c5ff4e4
JG
8#include <common/compat/directory-handle.h>
9#include <common/credentials.h>
10#include <common/defaults.h>
11#include <common/dynamic-array.h>
b2621f79
JG
12#include <common/error.h>
13#include <common/fd-tracker/fd-tracker.h>
dd95933f 14#include <common/fd-tracker/utils.h>
ac497a37 15#include <common/fs-handle.h>
8bb66c3c 16#include <common/fs-handle-internal.h>
b2621f79
JG
17#include <common/hashtable/hashtable.h>
18#include <common/hashtable/utils.h>
19#include <common/optional.h>
20#include <common/string-utils/format.h>
21#include <common/time.h>
22#include <common/trace-chunk-registry.h>
23#include <common/trace-chunk.h>
24#include <common/utils.h>
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
SM
173static
174const chunk_command close_command_get_post_release_func(
175 lttng_trace_chunk_command_type type) {
176 switch (type) {
177 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
178 return lttng_trace_chunk_move_to_completed_post_release;
179 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
180 return lttng_trace_chunk_no_operation;
181 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
182 return lttng_trace_chunk_delete_post_release;
183 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX:
184 abort();
185 }
186
187 abort();
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
a6bc4ca9 206 handle = (fs_handle_untracked *) zmalloc(sizeof(typeof(*handle)));
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
241void fs_handle_untracked_put_fd(struct fs_handle *_handle)
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 }
a6bc4ca9 349 new_name = (char *) zmalloc(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
a6bc4ca9 402 chunk = (lttng_trace_chunk *) zmalloc(sizeof(*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,
1010 };
1011
1012 pthread_mutex_lock(&chunk->lock);
1013 if (chunk->credentials.is_set) {
1014 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1015 goto end;
1016 }
1017 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
1018end:
1019 pthread_mutex_unlock(&chunk->lock);
1020 return status;
1021}
1022
1023
2c5ff4e4
JG
1024enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner(
1025 struct lttng_trace_chunk *chunk,
1026 struct lttng_directory_handle *session_output_directory)
1027{
1028 int ret;
1029 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
cbf53d23
JG
1030 struct lttng_directory_handle *chunk_directory_handle = NULL;
1031 bool reference_acquired;
2c5ff4e4
JG
1032
1033 pthread_mutex_lock(&chunk->lock);
1034 if (chunk->mode.is_set) {
1035 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1036 goto end;
1037 }
1038 if (!chunk->credentials.is_set) {
1039 /*
1040 * Fatal error, credentials must be set before a
1041 * directory is created.
1042 */
1043 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
1044 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1045 goto end;
1046 }
d7a20fcf 1047 if (chunk->path && chunk->path[0] != '\0') {
2c5ff4e4
JG
1048 ret = lttng_directory_handle_create_subdirectory_as_user(
1049 session_output_directory,
a7ceb342 1050 chunk->path,
2c5ff4e4
JG
1051 DIR_CREATION_MODE,
1052 !chunk->credentials.value.use_current_user ?
1053 &chunk->credentials.value.user : NULL);
1054 if (ret) {
1055 PERROR("Failed to create chunk output directory \"%s\"",
a7ceb342 1056 chunk->path);
2c5ff4e4
JG
1057 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1058 goto end;
1059 }
a7ceb342 1060 chunk_directory_handle =
dd95933f
JG
1061 chunk->fd_tracker ?
1062 fd_tracker_create_directory_handle_from_handle(
1063 chunk->fd_tracker,
1064 session_output_directory,
1065 chunk->path) :
1066 lttng_directory_handle_create_from_handle(
1067 chunk->path,
1068 session_output_directory);
a7ceb342
MD
1069 if (!chunk_directory_handle) {
1070 /* The function already logs on all error paths. */
1071 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1072 goto end;
1073 }
1074 } else {
1075 /*
1076 * A nameless chunk does not need its own output directory.
1077 * The session's output directory will be used.
1078 */
3f5de310
SM
1079 reference_acquired = lttng_directory_handle_get(
1080 session_output_directory);
a7ceb342 1081
a0377dfe 1082 LTTNG_ASSERT(reference_acquired);
a7ceb342 1083 chunk_directory_handle = session_output_directory;
2c5ff4e4 1084 }
cbf53d23
JG
1085 chunk->chunk_directory = chunk_directory_handle;
1086 chunk_directory_handle = NULL;
1087 reference_acquired = lttng_directory_handle_get(
1088 session_output_directory);
a0377dfe 1089 LTTNG_ASSERT(reference_acquired);
cbf53d23 1090 chunk->session_output_directory = session_output_directory;
2c5ff4e4
JG
1091 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER);
1092end:
1093 pthread_mutex_unlock(&chunk->lock);
1094 return status;
1095}
1096
2c5ff4e4
JG
1097enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user(
1098 struct lttng_trace_chunk *chunk,
1099 struct lttng_directory_handle *chunk_directory)
1100{
1101 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
cbf53d23 1102 bool reference_acquired;
2c5ff4e4
JG
1103
1104 pthread_mutex_lock(&chunk->lock);
1105 if (chunk->mode.is_set) {
1106 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1107 goto end;
1108 }
1109 if (!chunk->credentials.is_set) {
1110 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
1111 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1112 goto end;
1113 }
cbf53d23 1114 reference_acquired = lttng_directory_handle_get(chunk_directory);
a0377dfe 1115 LTTNG_ASSERT(reference_acquired);
cbf53d23 1116 chunk->chunk_directory = chunk_directory;
2c5ff4e4
JG
1117 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER);
1118end:
1119 pthread_mutex_unlock(&chunk->lock);
1120 return status;
1121}
1122
7ceefac4
JG
1123enum lttng_trace_chunk_status
1124lttng_trace_chunk_get_session_output_directory_handle(
1125 struct lttng_trace_chunk *chunk,
1126 struct lttng_directory_handle **handle)
1127{
1128 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1129
1130 pthread_mutex_lock(&chunk->lock);
1131 if (!chunk->session_output_directory) {
1132 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1133 *handle = NULL;
1134 goto end;
1135 } else {
1136 const bool reference_acquired = lttng_directory_handle_get(
1137 chunk->session_output_directory);
1138
a0377dfe 1139 LTTNG_ASSERT(reference_acquired);
7ceefac4
JG
1140 *handle = chunk->session_output_directory;
1141 }
1142end:
1143 pthread_mutex_unlock(&chunk->lock);
1144 return status;
1145}
1146
cbf53d23 1147enum lttng_trace_chunk_status lttng_trace_chunk_borrow_chunk_directory_handle(
2c5ff4e4
JG
1148 struct lttng_trace_chunk *chunk,
1149 const struct lttng_directory_handle **handle)
1150{
1151 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1152
1153 pthread_mutex_lock(&chunk->lock);
cbf53d23 1154 if (!chunk->chunk_directory) {
2c5ff4e4
JG
1155 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1156 goto end;
1157 }
1158
cbf53d23 1159 *handle = chunk->chunk_directory;
2c5ff4e4
JG
1160end:
1161 pthread_mutex_unlock(&chunk->lock);
1162 return status;
1163}
1164
1165/* Add a top-level directory to the trace chunk if it was previously unknown. */
1166static
1167int add_top_level_directory_unique(struct lttng_trace_chunk *chunk,
1168 const char *new_path)
1169{
1170 int ret = 0;
1171 bool found = false;
1172 size_t i, count = lttng_dynamic_pointer_array_get_count(
1173 &chunk->top_level_directories);
1174 const char *new_path_separator_pos = strchr(new_path, '/');
1175 const ptrdiff_t new_path_top_level_len = new_path_separator_pos ?
1176 new_path_separator_pos - new_path : strlen(new_path);
1177
1178 for (i = 0; i < count; i++) {
a6bc4ca9 1179 const char *path = (const char *) lttng_dynamic_pointer_array_get_pointer(
2c5ff4e4
JG
1180 &chunk->top_level_directories, i);
1181 const ptrdiff_t path_top_level_len = strlen(path);
1182
1183 if (path_top_level_len != new_path_top_level_len) {
1184 continue;
1185 }
1186 if (!strncmp(path, new_path, path_top_level_len)) {
1187 found = true;
1188 break;
1189 }
1190 }
1191
1192 if (!found) {
c36a763b 1193 char *copy = lttng_strndup(new_path, new_path_top_level_len);
2c5ff4e4
JG
1194
1195 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1196 new_path, chunk->name ? : "(unnamed)");
1197 if (!copy) {
1198 PERROR("Failed to copy path");
1199 ret = -1;
1200 goto end;
1201 }
1202 ret = lttng_dynamic_pointer_array_add_pointer(
1203 &chunk->top_level_directories, copy);
1204 if (ret) {
1205 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1206 free(copy);
1207 goto end;
1208 }
1209 }
1210end:
1211 return ret;
1212}
1213
2c5ff4e4
JG
1214enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory(
1215 struct lttng_trace_chunk *chunk,
1216 const char *path)
1217{
1218 int ret;
1219 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1220
1221 DBG("Creating trace chunk subdirectory \"%s\"", path);
1222 pthread_mutex_lock(&chunk->lock);
1223 if (!chunk->credentials.is_set) {
1224 /*
1225 * Fatal error, credentials must be set before a
1226 * directory is created.
1227 */
1228 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1229 path);
1230 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1231 goto end;
1232 }
1233 if (!chunk->mode.is_set ||
1234 chunk->mode.value != TRACE_CHUNK_MODE_OWNER) {
1235 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1236 path);
1237 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1238 goto end;
1239 }
cbf53d23 1240 if (!chunk->chunk_directory) {
2c5ff4e4
JG
1241 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1242 path);
1243 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1244 goto end;
1245 }
1246 if (*path == '/') {
1247 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1248 path);
1249 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
1250 goto end;
1251 }
1252 ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
cbf53d23 1253 chunk->chunk_directory, path,
2c5ff4e4
JG
1254 DIR_CREATION_MODE,
1255 chunk->credentials.value.use_current_user ?
1256 NULL : &chunk->credentials.value.user);
1257 if (ret) {
1258 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1259 path);
1260 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1261 goto end;
1262 }
1263 ret = add_top_level_directory_unique(chunk, path);
1264 if (ret) {
1265 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1266 goto end;
1267 }
1268end:
1269 pthread_mutex_unlock(&chunk->lock);
1270 return status;
1271}
1272
6cb32e5a
MD
1273/*
1274 * TODO: Implement O(1) lookup.
1275 */
1276static
1277bool lttng_trace_chunk_find_file(struct lttng_trace_chunk *chunk,
1278 const char *path, size_t *index)
1279{
1280 size_t i, count;
1281
1282 count = lttng_dynamic_pointer_array_get_count(&chunk->files);
1283 for (i = 0; i < count; i++) {
1284 const char *iter_path =
a6bc4ca9
SM
1285 (const char *) lttng_dynamic_pointer_array_get_pointer(
1286 &chunk->files, i);
6cb32e5a
MD
1287 if (!strcmp(iter_path, path)) {
1288 if (index) {
1289 *index = i;
1290 }
1291 return true;
1292 }
1293 }
1294 return false;
1295}
1296
1297static
1298enum lttng_trace_chunk_status lttng_trace_chunk_add_file(
1299 struct lttng_trace_chunk *chunk,
1300 const char *path)
1301{
1302 char *copy;
1303 int ret;
1304 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1305
1306 if (lttng_trace_chunk_find_file(chunk, path, NULL)) {
1307 return LTTNG_TRACE_CHUNK_STATUS_OK;
1308 }
1309 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1310 path, chunk->name ? : "(unnamed)");
1311 copy = strdup(path);
1312 if (!copy) {
1313 PERROR("Failed to copy path");
1314 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1315 goto end;
1316 }
1317 ret = lttng_dynamic_pointer_array_add_pointer(
1318 &chunk->files, copy);
1319 if (ret) {
1320 ERR("Allocation failure while adding file to a trace chunk");
1321 free(copy);
1322 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1323 goto end;
1324 }
1325end:
1326 return status;
1327}
1328
1329static
1330void lttng_trace_chunk_remove_file(
1331 struct lttng_trace_chunk *chunk,
1332 const char *path)
1333{
1334 size_t index;
1335 bool found;
1336 int ret;
1337
1338 found = lttng_trace_chunk_find_file(chunk, path, &index);
1339 if (!found) {
1340 return;
1341 }
1342 ret = lttng_dynamic_pointer_array_remove_pointer(
1343 &chunk->files, index);
a0377dfe 1344 LTTNG_ASSERT(!ret);
6cb32e5a
MD
1345}
1346
8bb66c3c
JG
1347static
1348enum lttng_trace_chunk_status _lttng_trace_chunk_open_fs_handle_locked(
1349 struct lttng_trace_chunk *chunk,
1350 const char *file_path,
1351 int flags,
1352 mode_t mode,
1353 struct fs_handle **out_handle,
1354 bool expect_no_file)
2c5ff4e4
JG
1355{
1356 int ret;
1357 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1358
1359 DBG("Opening trace chunk file \"%s\"", file_path);
2c5ff4e4
JG
1360 if (!chunk->credentials.is_set) {
1361 /*
1362 * Fatal error, credentials must be set before a
1363 * file is created.
1364 */
1365 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1366 file_path);
1367 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1368 goto end;
1369 }
cbf53d23 1370 if (!chunk->chunk_directory) {
2c5ff4e4
JG
1371 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1372 file_path);
1373 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1374 goto end;
1375 }
6cb32e5a
MD
1376 status = lttng_trace_chunk_add_file(chunk, file_path);
1377 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1378 goto end;
1379 }
8bb66c3c 1380 if (chunk->fd_tracker) {
a0377dfe 1381 LTTNG_ASSERT(chunk->credentials.value.use_current_user);
8bb66c3c
JG
1382 *out_handle = fd_tracker_open_fs_handle(chunk->fd_tracker,
1383 chunk->chunk_directory, file_path, flags, &mode);
1384 ret = *out_handle ? 0 : -1;
1385 } else {
1386 ret = lttng_directory_handle_open_file_as_user(
1387 chunk->chunk_directory, file_path, flags, mode,
1388 chunk->credentials.value.use_current_user ?
1389 NULL :
1390 &chunk->credentials.value.user);
1391 if (ret >= 0) {
1392 *out_handle = fs_handle_untracked_create(
1393 chunk->chunk_directory, file_path, ret);
1394 if (!*out_handle) {
1395 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1396 goto end;
1397 }
1398 }
1399 }
2c5ff4e4 1400 if (ret < 0) {
3ff5c5db
MD
1401 if (errno == ENOENT && expect_no_file) {
1402 status = LTTNG_TRACE_CHUNK_STATUS_NO_FILE;
1403 } else {
1404 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
d2956687 1405 file_path, flags, (int) mode);
3ff5c5db
MD
1406 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1407 }
6cb32e5a 1408 lttng_trace_chunk_remove_file(chunk, file_path);
2c5ff4e4
JG
1409 goto end;
1410 }
2c5ff4e4 1411end:
8bb66c3c
JG
1412 return status;
1413}
1414
8bb66c3c
JG
1415enum lttng_trace_chunk_status lttng_trace_chunk_open_fs_handle(
1416 struct lttng_trace_chunk *chunk,
1417 const char *file_path,
1418 int flags,
1419 mode_t mode,
1420 struct fs_handle **out_handle,
1421 bool expect_no_file)
1422{
1423 enum lttng_trace_chunk_status status;
1424
1425 pthread_mutex_lock(&chunk->lock);
1426 status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path,
1427 flags, mode, out_handle, expect_no_file);
1428 pthread_mutex_unlock(&chunk->lock);
1429 return status;
1430}
1431
8bb66c3c
JG
1432enum lttng_trace_chunk_status lttng_trace_chunk_open_file(
1433 struct lttng_trace_chunk *chunk,
1434 const char *file_path,
1435 int flags,
1436 mode_t mode,
1437 int *out_fd,
1438 bool expect_no_file)
1439{
1440 enum lttng_trace_chunk_status status;
1441 struct fs_handle *fs_handle;
1442
1443 pthread_mutex_lock(&chunk->lock);
1444 /*
1445 * Using this method is never valid when an fd_tracker is being
1446 * used since the resulting file descriptor would not be tracked.
1447 */
a0377dfe 1448 LTTNG_ASSERT(!chunk->fd_tracker);
8bb66c3c
JG
1449 status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path,
1450 flags, mode, &fs_handle, expect_no_file);
2c5ff4e4 1451 pthread_mutex_unlock(&chunk->lock);
8bb66c3c
JG
1452
1453 if (status == LTTNG_TRACE_CHUNK_STATUS_OK) {
1454 *out_fd = fs_handle_get_fd(fs_handle);
1455 /*
1456 * Does not close the fd; we just "unbox" it from the fs_handle.
1457 */
1458 fs_handle_untracked_destroy(container_of(
1459 fs_handle, struct fs_handle_untracked, parent));
1460 }
1461
2c5ff4e4
JG
1462 return status;
1463}
1464
2c5ff4e4
JG
1465int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk,
1466 const char *file_path)
1467{
1468 int ret;
1469 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1470
1471 DBG("Unlinking trace chunk file \"%s\"", file_path);
1472 pthread_mutex_lock(&chunk->lock);
1473 if (!chunk->credentials.is_set) {
1474 /*
1475 * Fatal error, credentials must be set before a
a7ceb342 1476 * file is unlinked.
2c5ff4e4
JG
1477 */
1478 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1479 file_path);
1480 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1481 goto end;
1482 }
cbf53d23 1483 if (!chunk->chunk_directory) {
2c5ff4e4
JG
1484 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1485 file_path);
1486 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1487 goto end;
1488 }
1489 ret = lttng_directory_handle_unlink_file_as_user(
cbf53d23 1490 chunk->chunk_directory, file_path,
2c5ff4e4
JG
1491 chunk->credentials.value.use_current_user ?
1492 NULL : &chunk->credentials.value.user);
1493 if (ret < 0) {
1494 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1495 goto end;
1496 }
6cb32e5a 1497 lttng_trace_chunk_remove_file(chunk, file_path);
2c5ff4e4
JG
1498end:
1499 pthread_mutex_unlock(&chunk->lock);
1500 return status;
1501}
1502
562f936f 1503static
a7ceb342
MD
1504int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk *chunk,
1505 const char *path)
2c5ff4e4
JG
1506{
1507 int ret;
a7ceb342
MD
1508 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1509
1510 DBG("Recursively removing trace chunk directory \"%s\"", path);
1511 pthread_mutex_lock(&chunk->lock);
1512 if (!chunk->credentials.is_set) {
1513 /*
1514 * Fatal error, credentials must be set before a
1515 * directory is removed.
1516 */
1517 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1518 path);
1519 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1520 goto end;
1521 }
1522 if (!chunk->chunk_directory) {
1523 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1524 path);
1525 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1526 goto end;
1527 }
1528 ret = lttng_directory_handle_remove_subdirectory_recursive_as_user(
1529 chunk->chunk_directory, path,
1530 chunk->credentials.value.use_current_user ?
1531 NULL : &chunk->credentials.value.user,
1532 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
1533 if (ret < 0) {
1534 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1535 goto end;
1536 }
1537end:
1538 pthread_mutex_unlock(&chunk->lock);
1539 return status;
1540}
1541
1542static
1543int lttng_trace_chunk_move_to_completed_post_release(
1544 struct lttng_trace_chunk *trace_chunk)
1545{
1546 int ret = 0;
2c5ff4e4
JG
1547 char *archived_chunk_name = NULL;
1548 const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id);
1549 const time_t creation_timestamp =
1550 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation);
1551 const time_t close_timestamp =
1552 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close);
cbf53d23 1553 struct lttng_directory_handle *archived_chunks_directory = NULL;
a7ceb342 1554 enum lttng_trace_chunk_status status;
2c5ff4e4 1555
bbc4768c
JG
1556 if (!trace_chunk->mode.is_set ||
1557 trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER ||
cbf53d23 1558 !trace_chunk->session_output_directory) {
bbc4768c
JG
1559 /*
1560 * This command doesn't need to run if the output is remote
1561 * or if the trace chunk is not owned by this process.
1562 */
1563 goto end;
1564 }
1565
a0377dfe
FD
1566 LTTNG_ASSERT(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
1567 LTTNG_ASSERT(!trace_chunk->name_overridden);
1568 LTTNG_ASSERT(trace_chunk->path);
2c5ff4e4
JG
1569
1570 archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp,
1571 &close_timestamp);
1572 if (!archived_chunk_name) {
1573 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
a7ceb342 1574 ret = -1;
2c5ff4e4
JG
1575 goto end;
1576 }
1577
1578 ret = lttng_directory_handle_create_subdirectory_as_user(
cbf53d23 1579 trace_chunk->session_output_directory,
420acd90 1580 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
2c5ff4e4
JG
1581 DIR_CREATION_MODE,
1582 !trace_chunk->credentials.value.use_current_user ?
1583 &trace_chunk->credentials.value.user :
1584 NULL);
1585 if (ret) {
1586 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1587 "\" directory for archived trace chunks");
1588 goto end;
1589 }
1590
dd95933f
JG
1591 archived_chunks_directory = trace_chunk->fd_tracker ?
1592 fd_tracker_create_directory_handle_from_handle(
1593 trace_chunk->fd_tracker,
1594 trace_chunk->session_output_directory,
1595 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY) :
1596 lttng_directory_handle_create_from_handle(
1597 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
1598 trace_chunk->session_output_directory);
cbf53d23 1599 if (!archived_chunks_directory) {
2c5ff4e4 1600 PERROR("Failed to get handle to archived trace chunks directory");
a7ceb342 1601 ret = -1;
2c5ff4e4
JG
1602 goto end;
1603 }
2c5ff4e4 1604
a7ceb342
MD
1605 /*
1606 * Make sure chunk is renamed to old directory if not already done by
1607 * the creation of the next chunk. This happens if a rotation is
1608 * performed while tracing is stopped.
1609 */
1610 if (!trace_chunk->path || strcmp(trace_chunk->path,
1611 DEFAULT_CHUNK_TMP_OLD_DIRECTORY)) {
1612 status = lttng_trace_chunk_rename_path_no_lock(trace_chunk,
1613 DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
1614 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1615 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
1616 ret = -1;
1617 goto end;
1618 }
1619 }
1620
9de831f8 1621 ret = lttng_directory_handle_rename_as_user(
cbf53d23 1622 trace_chunk->session_output_directory,
a7ceb342 1623 trace_chunk->path,
cbf53d23 1624 archived_chunks_directory,
9de831f8
JG
1625 archived_chunk_name,
1626 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
1627 NULL :
1628 &trace_chunk->credentials.value.user);
2c5ff4e4
JG
1629 if (ret) {
1630 PERROR("Failed to rename folder \"%s\" to \"%s\"",
a7ceb342
MD
1631 trace_chunk->path,
1632 archived_chunk_name);
2c5ff4e4
JG
1633 }
1634
1635end:
cbf53d23 1636 lttng_directory_handle_put(archived_chunks_directory);
2c5ff4e4 1637 free(archived_chunk_name);
a7ceb342 1638 return ret;
2c5ff4e4
JG
1639}
1640
8ced4811
MD
1641static
1642int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk)
1643{
1644 return 0;
1645}
1646
1647static
1648int lttng_trace_chunk_delete_post_release_user(
1649 struct lttng_trace_chunk *trace_chunk)
1650{
1651 int ret = 0;
1652
1653 DBG("Trace chunk \"delete\" close command post-release (User)");
1654
1655 /* Unlink all files. */
1656 while (lttng_dynamic_pointer_array_get_count(&trace_chunk->files) != 0) {
1657 enum lttng_trace_chunk_status status;
1658 const char *path;
1659
1660 /* Remove first. */
a6bc4ca9
SM
1661 path = (const char *) lttng_dynamic_pointer_array_get_pointer(
1662 &trace_chunk->files, 0);
8ced4811 1663 DBG("Unlink file: %s", path);
a6bc4ca9 1664 status = (lttng_trace_chunk_status) lttng_trace_chunk_unlink_file(trace_chunk, path);
8ced4811
MD
1665 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1666 ERR("Error unlinking file '%s' when deleting chunk", path);
1667 ret = -1;
1668 goto end;
1669 }
1670 }
1671end:
1672 return ret;
1673}
1674
1675static
1676int lttng_trace_chunk_delete_post_release_owner(
1677 struct lttng_trace_chunk *trace_chunk)
1678{
1679 enum lttng_trace_chunk_status status;
1680 size_t i, count;
1681 int ret = 0;
1682
1683 ret = lttng_trace_chunk_delete_post_release_user(trace_chunk);
1684 if (ret) {
1685 goto end;
1686 }
1687
1688 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1689
a0377dfe
FD
1690 LTTNG_ASSERT(trace_chunk->session_output_directory);
1691 LTTNG_ASSERT(trace_chunk->chunk_directory);
8ced4811
MD
1692
1693 /* Remove empty directories. */
1694 count = lttng_dynamic_pointer_array_get_count(
1695 &trace_chunk->top_level_directories);
1696
1697 for (i = 0; i < count; i++) {
1698 const char *top_level_name =
a6bc4ca9
SM
1699 (const char *) lttng_dynamic_pointer_array_get_pointer(
1700 &trace_chunk->top_level_directories, i);
8ced4811 1701
a6bc4ca9 1702 status = (lttng_trace_chunk_status) lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk, top_level_name);
8ced4811
MD
1703 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1704 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1705 top_level_name);
1706 ret = -1;
1707 break;
1708 }
1709 }
1710 if (!ret) {
1711 lttng_directory_handle_put(trace_chunk->chunk_directory);
1712 trace_chunk->chunk_directory = NULL;
1713
1714 if (trace_chunk->path && trace_chunk->path[0] != '\0') {
a6bc4ca9 1715 status = (lttng_trace_chunk_status) lttng_directory_handle_remove_subdirectory(
8ced4811
MD
1716 trace_chunk->session_output_directory,
1717 trace_chunk->path);
1718 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1719 ERR("Error removing subdirectory '%s' file when deleting chunk",
1720 trace_chunk->path);
1721 ret = -1;
1722 }
1723 }
1724 }
1725 free(trace_chunk->path);
1726 trace_chunk->path = NULL;
1727end:
1728 return ret;
1729}
1730
1731/*
1732 * For local files, session and consumer daemons all run the delete hook. The
1733 * consumer daemons have the list of files to unlink, and technically the
1734 * session daemon is the owner of the chunk. Unlink all files owned by each
1735 * consumer daemon.
1736 */
1737static
1738int lttng_trace_chunk_delete_post_release(
1739 struct lttng_trace_chunk *trace_chunk)
1740{
1741 if (!trace_chunk->chunk_directory) {
1742 return 0;
1743 }
1744
1745 if (trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER) {
1746 return lttng_trace_chunk_delete_post_release_owner(trace_chunk);
1747 } else {
1748 return lttng_trace_chunk_delete_post_release_user(trace_chunk);
1749 }
1750}
1751
bbc4768c
JG
1752enum lttng_trace_chunk_status lttng_trace_chunk_get_close_command(
1753 struct lttng_trace_chunk *chunk,
1754 enum lttng_trace_chunk_command_type *command_type)
1755{
1756 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1757
1758 pthread_mutex_lock(&chunk->lock);
1759 if (chunk->close_command.is_set) {
1760 *command_type = chunk->close_command.value;
1761 status = LTTNG_TRACE_CHUNK_STATUS_OK;
1762 } else {
1763 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1764 }
1765 pthread_mutex_unlock(&chunk->lock);
1766 return status;
1767}
1768
2c5ff4e4
JG
1769enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
1770 struct lttng_trace_chunk *chunk,
1771 enum lttng_trace_chunk_command_type close_command)
1772{
1773 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1774
1775 if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
1776 close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
1777 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
6def6cd7 1778 goto end;
2c5ff4e4
JG
1779 }
1780
1781 pthread_mutex_lock(&chunk->lock);
1782 if (chunk->close_command.is_set) {
1783 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
a6bc4ca9
SM
1784 lttng_trace_chunk_command_type_str(chunk->close_command.value),
1785 lttng_trace_chunk_command_type_str(close_command));
420acd90 1786 } else {
2c5ff4e4 1787 DBG("Setting trace chunk close command to \"%s\"",
a6bc4ca9 1788 lttng_trace_chunk_command_type_str(close_command));
420acd90 1789 }
343defc2
MD
1790 /*
1791 * Unset close command for no-op for backward compatibility with relayd
1792 * 2.11.
1793 */
1794 if (close_command != LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) {
1795 LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
1796 } else {
1797 LTTNG_OPTIONAL_UNSET(&chunk->close_command);
1798 }
2c5ff4e4 1799 pthread_mutex_unlock(&chunk->lock);
6def6cd7 1800end:
2c5ff4e4
JG
1801 return status;
1802}
1803
bbc4768c
JG
1804const char *lttng_trace_chunk_command_type_get_name(
1805 enum lttng_trace_chunk_command_type command)
1806{
1807 switch (command) {
1808 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
1809 return "move to completed trace chunk folder";
343defc2
MD
1810 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
1811 return "no operation";
1812 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
1813 return "delete";
bbc4768c
JG
1814 default:
1815 abort();
1816 }
1817}
1818
ad8bec24
JG
1819bool lttng_trace_chunk_ids_equal(const struct lttng_trace_chunk *chunk_a,
1820 const struct lttng_trace_chunk *chunk_b)
1821{
1822 bool equal = false;
1823
80516611
JG
1824 if (chunk_a == chunk_b) {
1825 equal = true;
1826 goto end;
1827 }
1828
1829 if (!!chunk_a ^ !!chunk_b) {
ad8bec24
JG
1830 goto end;
1831 }
1832
1833 if (chunk_a->id.is_set ^ chunk_a->id.is_set) {
1834 /* One id is set and not the other, thus they are not equal. */
1835 goto end;
1836 }
1837
1838 if (!chunk_a->id.is_set) {
1839 /* Both ids are unset. */
1840 equal = true;
1841 } else {
1842 equal = chunk_a->id.value == chunk_b->id.value;
1843 }
1844
1845end:
1846 return equal;
1847}
1848
2c5ff4e4
JG
1849bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
1850{
1851 return urcu_ref_get_unless_zero(&chunk->ref);
1852}
1853
1854static
1855void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
1856{
1857 struct lttng_trace_chunk_registry_element *element =
1858 container_of(node, typeof(*element), rcu_node);
1859
1860 lttng_trace_chunk_fini(&element->chunk);
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
1882 element = container_of(chunk, typeof(*element), chunk);
1883 if (element->registry) {
1884 rcu_read_lock();
1885 cds_lfht_del(element->registry->ht,
1886 &element->trace_chunk_registry_ht_node);
1887 rcu_read_unlock();
1888 call_rcu(&element->rcu_node,
1889 free_lttng_trace_chunk_registry_element);
1890 } else {
1891 /* Never published, can be free'd immediately. */
1892 free_lttng_trace_chunk_registry_element(
1893 &element->rcu_node);
1894 }
1895 } else {
1896 /* Not RCU-protected, free immediately. */
1897 lttng_trace_chunk_fini(chunk);
1898 free(chunk);
1899 }
1900}
1901
2c5ff4e4
JG
1902void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
1903{
1904 if (!chunk) {
1905 return;
1906 }
a0377dfe 1907 LTTNG_ASSERT(chunk->ref.refcount);
2c5ff4e4
JG
1908 urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
1909}
1910
2c5ff4e4
JG
1911struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void)
1912{
1913 struct lttng_trace_chunk_registry *registry;
1914
a6bc4ca9 1915 registry = (lttng_trace_chunk_registry *) zmalloc(sizeof(*registry));
2c5ff4e4
JG
1916 if (!registry) {
1917 goto end;
1918 }
1919
1920 registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
1921 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
1922 if (!registry->ht) {
1923 goto error;
1924 }
1925end:
1926 return registry;
1927error:
1928 lttng_trace_chunk_registry_destroy(registry);
8243bf12 1929 return NULL;
2c5ff4e4
JG
1930}
1931
2c5ff4e4
JG
1932void lttng_trace_chunk_registry_destroy(
1933 struct lttng_trace_chunk_registry *registry)
1934{
1935 if (!registry) {
1936 return;
1937 }
1938 if (registry->ht) {
1939 int ret = cds_lfht_destroy(registry->ht, NULL);
a0377dfe 1940 LTTNG_ASSERT(!ret);
2c5ff4e4
JG
1941 }
1942 free(registry);
1943}
1944
1945static
1946struct lttng_trace_chunk_registry_element *
1947lttng_trace_chunk_registry_element_create_from_chunk(
1948 struct lttng_trace_chunk *chunk, uint64_t session_id)
1949{
1950 struct lttng_trace_chunk_registry_element *element =
a6bc4ca9 1951 (lttng_trace_chunk_registry_element *) zmalloc(sizeof(*element));
2c5ff4e4
JG
1952
1953 if (!element) {
1954 goto end;
1955 }
1956 cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
1957 element->session_id = session_id;
1958
1959 element->chunk = *chunk;
1960 lttng_trace_chunk_init(&element->chunk);
cbf53d23
JG
1961 if (chunk->session_output_directory) {
1962 /* Transferred ownership. */
1963 element->chunk.session_output_directory =
1964 chunk->session_output_directory;
1965 chunk->session_output_directory = NULL;
1966 }
1967 if (chunk->chunk_directory) {
1968 /* Transferred ownership. */
1969 element->chunk.chunk_directory = chunk->chunk_directory;
1970 chunk->chunk_directory = NULL;
2c5ff4e4
JG
1971 }
1972 /*
a7ceb342
MD
1973 * The original chunk becomes invalid; the name and path attributes are
1974 * transferred to the new chunk instance.
2c5ff4e4
JG
1975 */
1976 chunk->name = NULL;
a7ceb342 1977 chunk->path = NULL;
b2621f79 1978 element->chunk.fd_tracker = chunk->fd_tracker;
2c5ff4e4
JG
1979 element->chunk.in_registry_element = true;
1980end:
1981 return element;
1982}
1983
2c5ff4e4
JG
1984struct lttng_trace_chunk *
1985lttng_trace_chunk_registry_publish_chunk(
1986 struct lttng_trace_chunk_registry *registry,
1987 uint64_t session_id, struct lttng_trace_chunk *chunk)
1988{
1989 struct lttng_trace_chunk_registry_element *element;
1990 unsigned long element_hash;
1991
1992 pthread_mutex_lock(&chunk->lock);
1993 element = lttng_trace_chunk_registry_element_create_from_chunk(chunk,
1994 session_id);
1995 pthread_mutex_unlock(&chunk->lock);
1996 if (!element) {
1997 goto end;
1998 }
1999 /*
2000 * chunk is now invalid, the only valid operation is a 'put' from the
2001 * caller.
2002 */
2003 chunk = NULL;
2004 element_hash = lttng_trace_chunk_registry_element_hash(element);
2005
2006 rcu_read_lock();
2007 while (1) {
2008 struct cds_lfht_node *published_node;
2009 struct lttng_trace_chunk *published_chunk;
2010 struct lttng_trace_chunk_registry_element *published_element;
2011
2012 published_node = cds_lfht_add_unique(registry->ht,
420acd90 2013 element_hash,
2c5ff4e4 2014 lttng_trace_chunk_registry_element_match,
420acd90 2015 element,
2c5ff4e4
JG
2016 &element->trace_chunk_registry_ht_node);
2017 if (published_node == &element->trace_chunk_registry_ht_node) {
2018 /* Successfully published the new element. */
420acd90 2019 element->registry = registry;
2c5ff4e4
JG
2020 /* Acquire a reference for the caller. */
2021 if (lttng_trace_chunk_get(&element->chunk)) {
2022 break;
2023 } else {
2024 /*
2025 * Another thread concurrently unpublished the
2026 * trace chunk. This is currently unexpected.
2027 *
2028 * Re-attempt to publish.
2029 */
87cee602 2030 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
2c5ff4e4
JG
2031 continue;
2032 }
2033 }
2034
2035 /*
2036 * An equivalent trace chunk was published before this trace
2037 * chunk. Attempt to acquire a reference to the one that was
2038 * already published and release the reference to the copy we
2039 * created if successful.
2040 */
2041 published_element = container_of(published_node,
2042 typeof(*published_element),
2043 trace_chunk_registry_ht_node);
2044 published_chunk = &published_element->chunk;
2045 if (lttng_trace_chunk_get(published_chunk)) {
2046 lttng_trace_chunk_put(&element->chunk);
2047 element = published_element;
2048 break;
2049 }
2050 /*
2051 * A reference to the previously published trace chunk could not
a6bc4ca9 2052 * be acquired. Hence, retry to publish our copy of the trace
2c5ff4e4
JG
2053 * chunk.
2054 */
2055 }
2056 rcu_read_unlock();
2057end:
2058 return element ? &element->chunk : NULL;
2059}
2060
2061/*
2062 * Note that the caller must be registered as an RCU thread.
2063 * However, it does not need to hold the RCU read lock. The RCU read lock is
2064 * acquired to perform the look-up in the registry's hash table and held until
2065 * after a reference to the "found" trace chunk is acquired.
2066 *
2067 * IOW, holding a reference guarantees the existence of the object for the
2068 * caller.
2069 */
2070static
2071struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
2072 const struct lttng_trace_chunk_registry *registry,
2073 uint64_t session_id, uint64_t *chunk_id)
2074{
a6bc4ca9
SM
2075 lttng_trace_chunk_registry_element target_element {};
2076
2077 target_element.chunk.id.is_set = !!chunk_id;
2078 target_element.chunk.id.value = chunk_id ? *chunk_id : 0;
2079 target_element.session_id = session_id;
2080
2c5ff4e4
JG
2081 const unsigned long element_hash =
2082 lttng_trace_chunk_registry_element_hash(
2083 &target_element);
2084 struct cds_lfht_node *published_node;
2085 struct lttng_trace_chunk_registry_element *published_element;
2086 struct lttng_trace_chunk *published_chunk = NULL;
2087 struct cds_lfht_iter iter;
2088
2089 rcu_read_lock();
2090 cds_lfht_lookup(registry->ht,
2091 element_hash,
2092 lttng_trace_chunk_registry_element_match,
2093 &target_element,
2094 &iter);
2095 published_node = cds_lfht_iter_get_node(&iter);
2096 if (!published_node) {
2097 goto end;
2098 }
2099
2100 published_element = container_of(published_node,
2101 typeof(*published_element),
2102 trace_chunk_registry_ht_node);
2103 if (lttng_trace_chunk_get(&published_element->chunk)) {
2104 published_chunk = &published_element->chunk;
2105 }
2106end:
2107 rcu_read_unlock();
2108 return published_chunk;
2109}
2110
2c5ff4e4
JG
2111struct lttng_trace_chunk *
2112lttng_trace_chunk_registry_find_chunk(
2113 const struct lttng_trace_chunk_registry *registry,
2114 uint64_t session_id, uint64_t chunk_id)
2115{
420acd90 2116 return _lttng_trace_chunk_registry_find_chunk(registry,
2c5ff4e4
JG
2117 session_id, &chunk_id);
2118}
2119
6b584c2e
JG
2120int lttng_trace_chunk_registry_chunk_exists(
2121 const struct lttng_trace_chunk_registry *registry,
2122 uint64_t session_id, uint64_t chunk_id, bool *chunk_exists)
2123{
2124 int ret = 0;
a6bc4ca9
SM
2125 lttng_trace_chunk_registry_element target_element;
2126
2127 target_element.chunk.id.is_set = true;
2128 target_element.chunk.id.value = chunk_id;
2129 target_element.session_id = session_id;
2130
6b584c2e
JG
2131 const unsigned long element_hash =
2132 lttng_trace_chunk_registry_element_hash(
2133 &target_element);
2134 struct cds_lfht_node *published_node;
2135 struct cds_lfht_iter iter;
2136
2137 rcu_read_lock();
2138 cds_lfht_lookup(registry->ht,
2139 element_hash,
2140 lttng_trace_chunk_registry_element_match,
2141 &target_element,
2142 &iter);
2143 published_node = cds_lfht_iter_get_node(&iter);
2144 if (!published_node) {
2145 *chunk_exists = false;
2146 goto end;
2147 }
2148
2149 *chunk_exists = !cds_lfht_is_node_deleted(published_node);
2150end:
2151 rcu_read_unlock();
2152 return ret;
2153}
2154
2c5ff4e4
JG
2155struct lttng_trace_chunk *
2156lttng_trace_chunk_registry_find_anonymous_chunk(
2157 const struct lttng_trace_chunk_registry *registry,
2158 uint64_t session_id)
2159{
420acd90 2160 return _lttng_trace_chunk_registry_find_chunk(registry,
2c5ff4e4
JG
2161 session_id, NULL);
2162}
e10aec8f
MD
2163
2164unsigned int lttng_trace_chunk_registry_put_each_chunk(
8ced4811 2165 const struct lttng_trace_chunk_registry *registry)
e10aec8f
MD
2166{
2167 struct cds_lfht_iter iter;
2168 struct lttng_trace_chunk_registry_element *chunk_element;
2169 unsigned int trace_chunks_left = 0;
2170
2171 DBG("Releasing trace chunk registry to all trace chunks");
2172 rcu_read_lock();
2173 cds_lfht_for_each_entry(registry->ht,
2174 &iter, chunk_element, trace_chunk_registry_ht_node) {
2175 const char *chunk_id_str = "none";
2176 char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)];
2177
2178 pthread_mutex_lock(&chunk_element->chunk.lock);
2179 if (chunk_element->chunk.id.is_set) {
2180 int fmt_ret;
2181
2182 fmt_ret = snprintf(chunk_id_buf, sizeof(chunk_id_buf),
2183 "%" PRIu64,
2184 chunk_element->chunk.id.value);
2185 if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) {
2186 chunk_id_str = "formatting error";
2187 } else {
2188 chunk_id_str = chunk_id_buf;
2189 }
2190 }
2191
2192 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
2193 "chunk_id = %s, name = \"%s\", status = %s",
2194 chunk_element->session_id,
2195 chunk_id_str,
2196 chunk_element->chunk.name ? : "none",
2197 chunk_element->chunk.close_command.is_set ?
2198 "open" : "closed");
2199 pthread_mutex_unlock(&chunk_element->chunk.lock);
2200 lttng_trace_chunk_put(&chunk_element->chunk);
2201 trace_chunks_left++;
2202 }
2203 rcu_read_unlock();
2204 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left,
2205 __FUNCTION__);
2206
2207 return trace_chunks_left;
2208}
This page took 0.13601 seconds and 4 git commands to generate.