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