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