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