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