Make lttng_directory_handle reference countable
[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 */
58typedef void (*chunk_close_command)(struct lttng_trace_chunk *trace_chunk);
59
60/* Move a completed trace chunk to the 'completed' trace archive folder. */
61static
62void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk);
63
64struct chunk_credentials {
65 bool use_current_user;
66 struct lttng_credentials user;
67};
68
1a414e3a 69/* NOTE: Make sure to update lttng_trace_chunk_copy if you modify this. */
2c5ff4e4
JG
70struct lttng_trace_chunk {
71 pthread_mutex_t lock;
72 struct urcu_ref ref;
73 LTTNG_OPTIONAL(enum trace_chunk_mode) mode;
74 /*
75 * First-level directories created within the trace chunk.
76 * Elements are of type 'char *'.
1a414e3a
JG
77 *
78 * Only used by _owner_ mode chunks.
2c5ff4e4
JG
79 */
80 struct lttng_dynamic_pointer_array top_level_directories;
81 /* Is contained within an lttng_trace_chunk_registry_element? */
82 bool in_registry_element;
913a542b 83 bool name_overridden;
2c5ff4e4
JG
84 char *name;
85 /* An unset id means the chunk is anonymous. */
86 LTTNG_OPTIONAL(uint64_t) id;
87 LTTNG_OPTIONAL(time_t) timestamp_creation;
88 LTTNG_OPTIONAL(time_t) timestamp_close;
89 LTTNG_OPTIONAL(struct chunk_credentials) credentials;
cbf53d23
JG
90 struct lttng_directory_handle *session_output_directory;
91 struct lttng_directory_handle *chunk_directory;
2c5ff4e4
JG
92 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type) close_command;
93};
94
95/* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
96struct lttng_trace_chunk_registry_element {
2c5ff4e4 97 struct lttng_trace_chunk chunk;
1f2292f6 98 uint64_t session_id;
2c5ff4e4
JG
99 /* Weak and only set when added. */
100 struct lttng_trace_chunk_registry *registry;
101 struct cds_lfht_node trace_chunk_registry_ht_node;
102 /* call_rcu delayed reclaim. */
103 struct rcu_head rcu_node;
104};
105
106struct lttng_trace_chunk_registry {
107 struct cds_lfht *ht;
108};
109
606846ba
JG
110static const
111char *close_command_names[] = {
2c5ff4e4
JG
112 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
113 "move to completed chunk folder",
114};
115
606846ba 116static const
2c5ff4e4
JG
117chunk_close_command close_command_funcs[] = {
118 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
119 lttng_trace_chunk_move_to_completed,
120};
121
122static
123bool lttng_trace_chunk_registry_element_equals(
124 const struct lttng_trace_chunk_registry_element *a,
125 const struct lttng_trace_chunk_registry_element *b)
126{
127 if (a->session_id != b->session_id) {
128 goto not_equal;
129 }
130 if (a->chunk.id.is_set != b->chunk.id.is_set) {
131 goto not_equal;
132 }
133 if (a->chunk.id.is_set && a->chunk.id.value != b->chunk.id.value) {
134 goto not_equal;
135 }
136 return true;
137not_equal:
138 return false;
139}
140
141static
142int lttng_trace_chunk_registry_element_match(struct cds_lfht_node *node,
143 const void *key)
144{
145 const struct lttng_trace_chunk_registry_element *element_a, *element_b;
146
147 element_a = (const struct lttng_trace_chunk_registry_element *) key;
148 element_b = caa_container_of(node, typeof(*element_b),
149 trace_chunk_registry_ht_node);
150 return lttng_trace_chunk_registry_element_equals(element_a, element_b);
151}
152
153static
154unsigned long lttng_trace_chunk_registry_element_hash(
155 const struct lttng_trace_chunk_registry_element *element)
156{
157 unsigned long hash = hash_key_u64(&element->session_id,
158 lttng_ht_seed);
159
160 if (element->chunk.id.is_set) {
161 hash ^= hash_key_u64(&element->chunk.id.value, lttng_ht_seed);
162 }
163
164 return hash;
165}
166
167static
168char *generate_chunk_name(uint64_t chunk_id, time_t creation_timestamp,
169 const time_t *close_timestamp)
170{
171 int ret = 0;
172 char *new_name= NULL;
6e7e5048
JG
173 char start_datetime[ISO8601_STR_LEN] = {};
174 /* Add 1 for a '-' prefix. */
175 char end_datetime_suffix[ISO8601_STR_LEN + 1] = {};
2c5ff4e4
JG
176
177 ret = time_to_iso8601_str(
178 creation_timestamp,
179 start_datetime, sizeof(start_datetime));
180 if (ret) {
181 ERR("Failed to format trace chunk start date time");
182 goto error;
183 }
184 if (close_timestamp) {
185 *end_datetime_suffix = '-';
186 ret = time_to_iso8601_str(
187 *close_timestamp,
188 end_datetime_suffix + 1,
6e7e5048 189 sizeof(end_datetime_suffix) - 1);
2c5ff4e4
JG
190 if (ret) {
191 ERR("Failed to format trace chunk end date time");
192 goto error;
193 }
194 }
195 new_name = zmalloc(GENERATED_CHUNK_NAME_LEN);
196 if (!new_name) {
197 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
198 goto error;
199 }
200 ret = snprintf(new_name, GENERATED_CHUNK_NAME_LEN, "%s%s-%" PRIu64,
201 start_datetime, end_datetime_suffix, chunk_id);
202 if (ret >= GENERATED_CHUNK_NAME_LEN || ret == -1) {
203 ERR("Failed to format trace chunk name");
204 goto error;
205 }
206
207 return new_name;
208error:
209 free(new_name);
210 return NULL;
211}
212
213static
214void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk)
215{
216 urcu_ref_init(&chunk->ref);
217 pthread_mutex_init(&chunk->lock, NULL);
93bed9fe 218 lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free);
2c5ff4e4
JG
219}
220
221static
222void lttng_trace_chunk_fini(struct lttng_trace_chunk *chunk)
223{
cbf53d23
JG
224 if (chunk->session_output_directory) {
225 lttng_directory_handle_put(
226 chunk->session_output_directory);
227 chunk->session_output_directory = NULL;
2c5ff4e4 228 }
cbf53d23
JG
229 if (chunk->chunk_directory) {
230 lttng_directory_handle_put(chunk->chunk_directory);
231 chunk->chunk_directory = NULL;
2c5ff4e4
JG
232 }
233 free(chunk->name);
234 chunk->name = NULL;
93bed9fe 235 lttng_dynamic_pointer_array_reset(&chunk->top_level_directories);
2c5ff4e4
JG
236 pthread_mutex_destroy(&chunk->lock);
237}
238
239static
240struct lttng_trace_chunk *lttng_trace_chunk_allocate(void)
241{
242 struct lttng_trace_chunk *chunk = NULL;
243
244 chunk = zmalloc(sizeof(*chunk));
245 if (!chunk) {
246 ERR("Failed to allocate trace chunk");
247 goto end;
248 }
249 lttng_trace_chunk_init(chunk);
250end:
251 return chunk;
252}
253
254LTTNG_HIDDEN
255struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous(void)
256{
257 DBG("Creating anonymous trace chunk");
258 return lttng_trace_chunk_allocate();
259}
260
261LTTNG_HIDDEN
262struct lttng_trace_chunk *lttng_trace_chunk_create(
263 uint64_t chunk_id, time_t chunk_creation_time)
264{
265 struct lttng_trace_chunk *chunk;
266 char chunk_creation_datetime_buf[16] = {};
267 const char *chunk_creation_datetime_str = "(formatting error)";
268 struct tm timeinfo_buf, *timeinfo;
269
270 timeinfo = localtime_r(&chunk_creation_time, &timeinfo_buf);
271 if (timeinfo) {
272 size_t strftime_ret;
273
274 /* Don't fail because of this; it is only used for logging. */
275 strftime_ret = strftime(chunk_creation_datetime_buf,
276 sizeof(chunk_creation_datetime_buf),
277 "%Y%m%d-%H%M%S", timeinfo);
278 if (strftime_ret) {
279 chunk_creation_datetime_str =
280 chunk_creation_datetime_buf;
281 }
282 }
283
284 DBG("Creating trace chunk: chunk_id = %" PRIu64 ", creation time = %s",
285 chunk_id, chunk_creation_datetime_str);
286 chunk = lttng_trace_chunk_allocate();
287 if (!chunk) {
288 goto end;
289 }
290
291 LTTNG_OPTIONAL_SET(&chunk->id, chunk_id);
292 LTTNG_OPTIONAL_SET(&chunk->timestamp_creation, chunk_creation_time);
293 if (chunk_id != 0) {
294 chunk->name = generate_chunk_name(chunk_id,
295 chunk_creation_time, NULL);
296 if (!chunk->name) {
297 ERR("Failed to allocate trace chunk name storage");
298 goto error;
299 }
300 }
301
302 DBG("Chunk name set to \"%s\"", chunk->name ? : "(none)");
303end:
304 return chunk;
305error:
306 lttng_trace_chunk_put(chunk);
307 return NULL;
308}
309
1a414e3a
JG
310LTTNG_HIDDEN
311struct lttng_trace_chunk *lttng_trace_chunk_copy(
312 struct lttng_trace_chunk *source_chunk)
313{
314 struct lttng_trace_chunk *new_chunk = lttng_trace_chunk_allocate();
315
316 if (!new_chunk) {
317 goto end;
318 }
319
320 pthread_mutex_lock(&source_chunk->lock);
321 /*
322 * A new chunk is always a user; it shall create no new trace
323 * subdirectories.
324 */
325 new_chunk->mode = (typeof(new_chunk->mode)) {
326 .is_set = true,
327 .value = TRACE_CHUNK_MODE_USER,
328 };
329 /*
330 * top_level_directories is not copied as it is never used
331 * by _user_ mode chunks.
332 */
333 /* The new chunk is not part of a registry (yet, at least). */
334 new_chunk->in_registry_element = false;
335 new_chunk->name_overridden = source_chunk->name_overridden;
336 if (source_chunk->name) {
337 new_chunk->name = strdup(source_chunk->name);
338 if (!new_chunk->name) {
339 ERR("Failed to copy source trace chunk name in %s()",
340 __FUNCTION__);
341 goto error_unlock;
342 }
343 }
344 new_chunk->id = source_chunk->id;
345 new_chunk->timestamp_creation = source_chunk->timestamp_creation;
346 new_chunk->timestamp_close = source_chunk->timestamp_close;
347 new_chunk->credentials = source_chunk->credentials;
cbf53d23
JG
348 if (source_chunk->session_output_directory) {
349 const bool reference_acquired = lttng_directory_handle_get(
350 source_chunk->session_output_directory);
351
352 assert(reference_acquired);
353 new_chunk->session_output_directory =
354 source_chunk->session_output_directory;
1a414e3a 355 }
cbf53d23
JG
356 if (source_chunk->chunk_directory) {
357 const bool reference_acquired = lttng_directory_handle_get(
358 source_chunk->chunk_directory);
359
360 assert(reference_acquired);
361 new_chunk->chunk_directory = source_chunk->chunk_directory;
1a414e3a
JG
362 }
363 new_chunk->close_command = source_chunk->close_command;
364 pthread_mutex_unlock(&source_chunk->lock);
365end:
366 return new_chunk;
367error_unlock:
368 pthread_mutex_unlock(&source_chunk->lock);
41d2ab71 369 lttng_trace_chunk_put(new_chunk);
1a414e3a
JG
370 return NULL;
371}
372
2c5ff4e4
JG
373LTTNG_HIDDEN
374enum lttng_trace_chunk_status lttng_trace_chunk_get_id(
375 struct lttng_trace_chunk *chunk, uint64_t *id)
376{
377 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
378
379 pthread_mutex_lock(&chunk->lock);
380 if (chunk->id.is_set) {
381 *id = chunk->id.value;
382 } else {
383 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
384 }
385 pthread_mutex_unlock(&chunk->lock);
386 return status;
387}
388
389LTTNG_HIDDEN
390enum lttng_trace_chunk_status lttng_trace_chunk_get_creation_timestamp(
391 struct lttng_trace_chunk *chunk, time_t *creation_ts)
392
393{
394 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
395
396 pthread_mutex_lock(&chunk->lock);
397 if (chunk->timestamp_creation.is_set) {
398 *creation_ts = chunk->timestamp_creation.value;
399 } else {
400 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
401 }
402 pthread_mutex_unlock(&chunk->lock);
403 return status;
404}
405
406LTTNG_HIDDEN
407enum lttng_trace_chunk_status lttng_trace_chunk_get_close_timestamp(
408 struct lttng_trace_chunk *chunk, time_t *close_ts)
409{
410 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
411
412 pthread_mutex_lock(&chunk->lock);
413 if (chunk->timestamp_close.is_set) {
414 *close_ts = chunk->timestamp_close.value;
415 } else {
416 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
417 }
418 pthread_mutex_unlock(&chunk->lock);
419 return status;
420}
421
422LTTNG_HIDDEN
423enum lttng_trace_chunk_status lttng_trace_chunk_set_close_timestamp(
424 struct lttng_trace_chunk *chunk, time_t close_ts)
425{
426 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
427
428 pthread_mutex_lock(&chunk->lock);
429 if (!chunk->timestamp_creation.is_set) {
430 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
431 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
432 goto end;
433 }
434 if (chunk->timestamp_creation.value > close_ts) {
435 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
436 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
437 goto end;
438 }
439 LTTNG_OPTIONAL_SET(&chunk->timestamp_close, close_ts);
ecd1a12f
MD
440 if (!chunk->name_overridden) {
441 free(chunk->name);
442 chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id),
443 LTTNG_OPTIONAL_GET(chunk->timestamp_creation),
444 &close_ts);
445 if (!chunk->name) {
446 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
447 }
2c5ff4e4
JG
448 }
449end:
450 pthread_mutex_unlock(&chunk->lock);
451 return status;
452}
453
454LTTNG_HIDDEN
455enum lttng_trace_chunk_status lttng_trace_chunk_get_name(
456 struct lttng_trace_chunk *chunk, const char **name,
913a542b 457 bool *name_overridden)
2c5ff4e4
JG
458{
459 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
460
461 pthread_mutex_lock(&chunk->lock);
913a542b
MD
462 if (name_overridden) {
463 *name_overridden = chunk->name_overridden;
2c5ff4e4
JG
464 }
465 if (!chunk->name) {
466 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
467 goto end;
468 }
469 *name = chunk->name;
470end:
471 pthread_mutex_unlock(&chunk->lock);
472 return status;
473}
474
84fa4db5
JG
475static
476bool is_valid_chunk_name(const char *name)
477{
478 size_t len;
479
480 if (!name) {
481 return false;
482 }
483
f7399c50 484 len = lttng_strnlen(name, LTTNG_NAME_MAX);
84fa4db5
JG
485 if (len == 0 || len == LTTNG_NAME_MAX) {
486 return false;
487 }
488
489 if (strchr(name, '/') || strchr(name, '.')) {
490 return false;
491 }
492
493 return true;
494}
495
2c5ff4e4
JG
496LTTNG_HIDDEN
497enum lttng_trace_chunk_status lttng_trace_chunk_override_name(
498 struct lttng_trace_chunk *chunk, const char *name)
499
500{
501 char *new_name;
502 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
503
84fa4db5 504 if (!is_valid_chunk_name(name)) {
2c5ff4e4
JG
505 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
506 name ? : "NULL");
507 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
508 goto end;
509 }
510
511 pthread_mutex_lock(&chunk->lock);
512 if (!chunk->id.is_set) {
513 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
514 name);
515 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
516 goto end_unlock;
517 }
518 new_name = strdup(name);
519 if (!new_name) {
520 ERR("Failed to allocate new trace chunk name");
521 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
522 goto end_unlock;
523 }
524 free(chunk->name);
525 chunk->name = new_name;
913a542b 526 chunk->name_overridden = true;
2c5ff4e4
JG
527end_unlock:
528 pthread_mutex_unlock(&chunk->lock);
529end:
530 return status;
531}
532
533LTTNG_HIDDEN
534enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials(
535 struct lttng_trace_chunk *chunk,
536 struct lttng_credentials *credentials)
537{
538 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
539
540 pthread_mutex_lock(&chunk->lock);
541 if (chunk->credentials.is_set) {
542 if (chunk->credentials.value.use_current_user) {
543 credentials->uid = geteuid();
544 credentials->gid = getegid();
545 } else {
546 *credentials = chunk->credentials.value.user;
547 }
548 } else {
549 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
550 }
551 pthread_mutex_unlock(&chunk->lock);
552 return status;
553}
554
555LTTNG_HIDDEN
556enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials(
557 struct lttng_trace_chunk *chunk,
558 const struct lttng_credentials *user_credentials)
559{
560 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
561 const struct chunk_credentials credentials = {
562 .user = *user_credentials,
563 .use_current_user = false,
564 };
565
566 pthread_mutex_lock(&chunk->lock);
567 if (chunk->credentials.is_set) {
568 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
569 goto end;
570 }
571 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
572end:
573 pthread_mutex_unlock(&chunk->lock);
574 return status;
575}
576
577LTTNG_HIDDEN
578enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials_current_user(
579 struct lttng_trace_chunk *chunk)
580{
581 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
582 const struct chunk_credentials credentials = {
583 .use_current_user = true,
584 };
585
586 pthread_mutex_lock(&chunk->lock);
587 if (chunk->credentials.is_set) {
588 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
589 goto end;
590 }
591 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
592end:
593 pthread_mutex_unlock(&chunk->lock);
594 return status;
595}
596
597
598LTTNG_HIDDEN
599enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner(
600 struct lttng_trace_chunk *chunk,
601 struct lttng_directory_handle *session_output_directory)
602{
603 int ret;
604 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
cbf53d23
JG
605 struct lttng_directory_handle *chunk_directory_handle = NULL;
606 bool reference_acquired;
2c5ff4e4
JG
607
608 pthread_mutex_lock(&chunk->lock);
609 if (chunk->mode.is_set) {
610 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
611 goto end;
612 }
613 if (!chunk->credentials.is_set) {
614 /*
615 * Fatal error, credentials must be set before a
616 * directory is created.
617 */
618 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
619 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
620 goto end;
621 }
622
623 if (chunk->name) {
624 /*
625 * A nameless chunk does not need its own output directory.
626 * The session's output directory will be used.
627 */
628 ret = lttng_directory_handle_create_subdirectory_as_user(
629 session_output_directory,
630 chunk->name,
631 DIR_CREATION_MODE,
632 !chunk->credentials.value.use_current_user ?
633 &chunk->credentials.value.user : NULL);
634 if (ret) {
635 PERROR("Failed to create chunk output directory \"%s\"",
636 chunk->name);
637 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
638 goto end;
639 }
640 }
cbf53d23 641 chunk_directory_handle = lttng_directory_handle_create_from_handle(
2c5ff4e4
JG
642 chunk->name,
643 session_output_directory);
cbf53d23 644 if (!chunk_directory_handle) {
2c5ff4e4
JG
645 /* The function already logs on all error paths. */
646 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
647 goto end;
648 }
cbf53d23
JG
649 chunk->chunk_directory = chunk_directory_handle;
650 chunk_directory_handle = NULL;
651 reference_acquired = lttng_directory_handle_get(
652 session_output_directory);
653 assert(reference_acquired);
654 chunk->session_output_directory = session_output_directory;
2c5ff4e4
JG
655 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER);
656end:
657 pthread_mutex_unlock(&chunk->lock);
658 return status;
659}
660
661LTTNG_HIDDEN
662enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user(
663 struct lttng_trace_chunk *chunk,
664 struct lttng_directory_handle *chunk_directory)
665{
666 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
cbf53d23 667 bool reference_acquired;
2c5ff4e4
JG
668
669 pthread_mutex_lock(&chunk->lock);
670 if (chunk->mode.is_set) {
671 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
672 goto end;
673 }
674 if (!chunk->credentials.is_set) {
675 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
676 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
677 goto end;
678 }
cbf53d23
JG
679 reference_acquired = lttng_directory_handle_get(chunk_directory);
680 assert(reference_acquired);
681 chunk->chunk_directory = chunk_directory;
2c5ff4e4
JG
682 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER);
683end:
684 pthread_mutex_unlock(&chunk->lock);
685 return status;
686}
687
688LTTNG_HIDDEN
cbf53d23 689enum lttng_trace_chunk_status lttng_trace_chunk_borrow_chunk_directory_handle(
2c5ff4e4
JG
690 struct lttng_trace_chunk *chunk,
691 const struct lttng_directory_handle **handle)
692{
693 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
694
695 pthread_mutex_lock(&chunk->lock);
cbf53d23 696 if (!chunk->chunk_directory) {
2c5ff4e4
JG
697 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
698 goto end;
699 }
700
cbf53d23 701 *handle = chunk->chunk_directory;
2c5ff4e4
JG
702end:
703 pthread_mutex_unlock(&chunk->lock);
704 return status;
705}
706
707/* Add a top-level directory to the trace chunk if it was previously unknown. */
708static
709int add_top_level_directory_unique(struct lttng_trace_chunk *chunk,
710 const char *new_path)
711{
712 int ret = 0;
713 bool found = false;
714 size_t i, count = lttng_dynamic_pointer_array_get_count(
715 &chunk->top_level_directories);
716 const char *new_path_separator_pos = strchr(new_path, '/');
717 const ptrdiff_t new_path_top_level_len = new_path_separator_pos ?
718 new_path_separator_pos - new_path : strlen(new_path);
719
720 for (i = 0; i < count; i++) {
721 const char *path = lttng_dynamic_pointer_array_get_pointer(
722 &chunk->top_level_directories, i);
723 const ptrdiff_t path_top_level_len = strlen(path);
724
725 if (path_top_level_len != new_path_top_level_len) {
726 continue;
727 }
728 if (!strncmp(path, new_path, path_top_level_len)) {
729 found = true;
730 break;
731 }
732 }
733
734 if (!found) {
c36a763b 735 char *copy = lttng_strndup(new_path, new_path_top_level_len);
2c5ff4e4
JG
736
737 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
738 new_path, chunk->name ? : "(unnamed)");
739 if (!copy) {
740 PERROR("Failed to copy path");
741 ret = -1;
742 goto end;
743 }
744 ret = lttng_dynamic_pointer_array_add_pointer(
745 &chunk->top_level_directories, copy);
746 if (ret) {
747 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
748 free(copy);
749 goto end;
750 }
751 }
752end:
753 return ret;
754}
755
756LTTNG_HIDDEN
757enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory(
758 struct lttng_trace_chunk *chunk,
759 const char *path)
760{
761 int ret;
762 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
763
764 DBG("Creating trace chunk subdirectory \"%s\"", path);
765 pthread_mutex_lock(&chunk->lock);
766 if (!chunk->credentials.is_set) {
767 /*
768 * Fatal error, credentials must be set before a
769 * directory is created.
770 */
771 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
772 path);
773 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
774 goto end;
775 }
776 if (!chunk->mode.is_set ||
777 chunk->mode.value != TRACE_CHUNK_MODE_OWNER) {
778 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
779 path);
780 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
781 goto end;
782 }
cbf53d23 783 if (!chunk->chunk_directory) {
2c5ff4e4
JG
784 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
785 path);
786 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
787 goto end;
788 }
789 if (*path == '/') {
790 ERR("Refusing to create absolute trace chunk directory \"%s\"",
791 path);
792 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
793 goto end;
794 }
795 ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
cbf53d23 796 chunk->chunk_directory, path,
2c5ff4e4
JG
797 DIR_CREATION_MODE,
798 chunk->credentials.value.use_current_user ?
799 NULL : &chunk->credentials.value.user);
800 if (ret) {
801 PERROR("Failed to create trace chunk subdirectory \"%s\"",
802 path);
803 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
804 goto end;
805 }
806 ret = add_top_level_directory_unique(chunk, path);
807 if (ret) {
808 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
809 goto end;
810 }
811end:
812 pthread_mutex_unlock(&chunk->lock);
813 return status;
814}
815
816LTTNG_HIDDEN
817enum lttng_trace_chunk_status lttng_trace_chunk_open_file(
818 struct lttng_trace_chunk *chunk, const char *file_path,
819 int flags, mode_t mode, int *out_fd)
820{
821 int ret;
822 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
823
824 DBG("Opening trace chunk file \"%s\"", file_path);
825 pthread_mutex_lock(&chunk->lock);
826 if (!chunk->credentials.is_set) {
827 /*
828 * Fatal error, credentials must be set before a
829 * file is created.
830 */
831 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
832 file_path);
833 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
834 goto end;
835 }
cbf53d23 836 if (!chunk->chunk_directory) {
2c5ff4e4
JG
837 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
838 file_path);
839 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
840 goto end;
841 }
842 ret = lttng_directory_handle_open_file_as_user(
cbf53d23 843 chunk->chunk_directory, file_path, flags, mode,
2c5ff4e4
JG
844 chunk->credentials.value.use_current_user ?
845 NULL : &chunk->credentials.value.user);
846 if (ret < 0) {
625f36cc 847 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
d2956687 848 file_path, flags, (int) mode);
2c5ff4e4
JG
849 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
850 goto end;
851 }
852 *out_fd = ret;
853end:
854 pthread_mutex_unlock(&chunk->lock);
855 return status;
856}
857
858LTTNG_HIDDEN
859int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk,
860 const char *file_path)
861{
862 int ret;
863 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
864
865 DBG("Unlinking trace chunk file \"%s\"", file_path);
866 pthread_mutex_lock(&chunk->lock);
867 if (!chunk->credentials.is_set) {
868 /*
869 * Fatal error, credentials must be set before a
870 * directory is created.
871 */
872 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
873 file_path);
874 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
875 goto end;
876 }
cbf53d23 877 if (!chunk->chunk_directory) {
2c5ff4e4
JG
878 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
879 file_path);
880 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
881 goto end;
882 }
883 ret = lttng_directory_handle_unlink_file_as_user(
cbf53d23 884 chunk->chunk_directory, file_path,
2c5ff4e4
JG
885 chunk->credentials.value.use_current_user ?
886 NULL : &chunk->credentials.value.user);
887 if (ret < 0) {
888 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
889 goto end;
890 }
891end:
892 pthread_mutex_unlock(&chunk->lock);
893 return status;
894}
895
896static
897void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk)
898{
899 int ret;
900 char *directory_to_rename = NULL;
901 bool free_directory_to_rename = false;
2c5ff4e4
JG
902 char *archived_chunk_name = NULL;
903 const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id);
904 const time_t creation_timestamp =
905 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation);
906 const time_t close_timestamp =
907 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close);
cbf53d23 908 struct lttng_directory_handle *archived_chunks_directory = NULL;
2c5ff4e4 909
bbc4768c
JG
910 if (!trace_chunk->mode.is_set ||
911 trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER ||
cbf53d23 912 !trace_chunk->session_output_directory) {
bbc4768c
JG
913 /*
914 * This command doesn't need to run if the output is remote
915 * or if the trace chunk is not owned by this process.
916 */
917 goto end;
918 }
919
2c5ff4e4 920 assert(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
913a542b 921 assert(!trace_chunk->name_overridden);
2c5ff4e4
JG
922
923 /*
924 * The fist trace chunk of a session is directly output to the
925 * session's output folder. In this case, the top level directories
926 * must be moved to a temporary folder before that temporary directory
927 * is renamed to match the chunk's name.
928 */
929 if (chunk_id == 0) {
cbf53d23
JG
930 struct lttng_directory_handle *temporary_rename_directory =
931 NULL;
2c5ff4e4 932 size_t i, count = lttng_dynamic_pointer_array_get_count(
cbf53d23 933 &trace_chunk->top_level_directories);
2c5ff4e4
JG
934
935 ret = lttng_directory_handle_create_subdirectory_as_user(
cbf53d23 936 trace_chunk->session_output_directory,
2c5ff4e4
JG
937 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY,
938 DIR_CREATION_MODE,
939 !trace_chunk->credentials.value.use_current_user ?
940 &trace_chunk->credentials.value.user : NULL);
941 if (ret) {
942 PERROR("Failed to create temporary trace chunk rename directory \"%s\"",
943 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY);
944 }
945
cbf53d23 946 temporary_rename_directory = lttng_directory_handle_create_from_handle(
2c5ff4e4 947 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY,
cbf53d23
JG
948 trace_chunk->session_output_directory);
949 if (!temporary_rename_directory) {
2c5ff4e4
JG
950 ERR("Failed to get handle to temporary trace chunk rename directory");
951 goto end;
952 }
953
954 for (i = 0; i < count; i++) {
2c5ff4e4
JG
955 const char *top_level_name =
956 lttng_dynamic_pointer_array_get_pointer(
957 &trace_chunk->top_level_directories, i);
958
9de831f8 959 ret = lttng_directory_handle_rename_as_user(
cbf53d23 960 trace_chunk->session_output_directory,
9de831f8 961 top_level_name,
cbf53d23 962 temporary_rename_directory,
9de831f8
JG
963 top_level_name,
964 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
965 NULL :
966 &trace_chunk->credentials.value.user);
2c5ff4e4
JG
967 if (ret) {
968 PERROR("Failed to move \"%s\" to temporary trace chunk rename directory",
969 top_level_name);
cbf53d23
JG
970 lttng_directory_handle_put(
971 temporary_rename_directory);
2c5ff4e4
JG
972 goto end;
973 }
974 }
cbf53d23 975 lttng_directory_handle_put(temporary_rename_directory);
2c5ff4e4
JG
976 directory_to_rename = DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY;
977 free_directory_to_rename = false;
978 } else {
979 directory_to_rename = generate_chunk_name(chunk_id,
980 creation_timestamp, NULL);
981 if (!directory_to_rename) {
982 ERR("Failed to generate initial trace chunk name while renaming trace chunk");
c222b016 983 goto end;
2c5ff4e4
JG
984 }
985 free_directory_to_rename = true;
986 }
987
988 archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp,
989 &close_timestamp);
990 if (!archived_chunk_name) {
991 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
992 goto end;
993 }
994
995 ret = lttng_directory_handle_create_subdirectory_as_user(
cbf53d23 996 trace_chunk->session_output_directory,
2c5ff4e4
JG
997 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
998 DIR_CREATION_MODE,
999 !trace_chunk->credentials.value.use_current_user ?
1000 &trace_chunk->credentials.value.user :
1001 NULL);
1002 if (ret) {
1003 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1004 "\" directory for archived trace chunks");
1005 goto end;
1006 }
1007
cbf53d23 1008 archived_chunks_directory = lttng_directory_handle_create_from_handle(
2c5ff4e4 1009 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
cbf53d23
JG
1010 trace_chunk->session_output_directory);
1011 if (!archived_chunks_directory) {
2c5ff4e4
JG
1012 PERROR("Failed to get handle to archived trace chunks directory");
1013 goto end;
1014 }
2c5ff4e4 1015
9de831f8 1016 ret = lttng_directory_handle_rename_as_user(
cbf53d23 1017 trace_chunk->session_output_directory,
9de831f8 1018 directory_to_rename,
cbf53d23 1019 archived_chunks_directory,
9de831f8
JG
1020 archived_chunk_name,
1021 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
1022 NULL :
1023 &trace_chunk->credentials.value.user);
2c5ff4e4
JG
1024 if (ret) {
1025 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1026 directory_to_rename, archived_chunk_name);
1027 }
1028
1029end:
cbf53d23 1030 lttng_directory_handle_put(archived_chunks_directory);
2c5ff4e4
JG
1031 free(archived_chunk_name);
1032 if (free_directory_to_rename) {
1033 free(directory_to_rename);
1034 }
1035}
1036
bbc4768c
JG
1037LTTNG_HIDDEN
1038enum lttng_trace_chunk_status lttng_trace_chunk_get_close_command(
1039 struct lttng_trace_chunk *chunk,
1040 enum lttng_trace_chunk_command_type *command_type)
1041{
1042 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1043
1044 pthread_mutex_lock(&chunk->lock);
1045 if (chunk->close_command.is_set) {
1046 *command_type = chunk->close_command.value;
1047 status = LTTNG_TRACE_CHUNK_STATUS_OK;
1048 } else {
1049 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1050 }
1051 pthread_mutex_unlock(&chunk->lock);
1052 return status;
1053}
1054
2c5ff4e4
JG
1055LTTNG_HIDDEN
1056enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
1057 struct lttng_trace_chunk *chunk,
1058 enum lttng_trace_chunk_command_type close_command)
1059{
1060 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1061
1062 if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
1063 close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
1064 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
6def6cd7 1065 goto end;
2c5ff4e4
JG
1066 }
1067
1068 pthread_mutex_lock(&chunk->lock);
1069 if (chunk->close_command.is_set) {
1070 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1071 close_command_names[chunk->close_command.value],
1072 close_command_names[close_command]);
1073 } else {
1074 DBG("Setting trace chunk close command to \"%s\"",
1075 close_command_names[close_command]);
1076 }
1077 LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
1078 pthread_mutex_unlock(&chunk->lock);
6def6cd7 1079end:
2c5ff4e4
JG
1080 return status;
1081}
1082
bbc4768c
JG
1083LTTNG_HIDDEN
1084const char *lttng_trace_chunk_command_type_get_name(
1085 enum lttng_trace_chunk_command_type command)
1086{
1087 switch (command) {
1088 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
1089 return "move to completed trace chunk folder";
1090 default:
1091 abort();
1092 }
1093}
1094
2c5ff4e4
JG
1095LTTNG_HIDDEN
1096bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
1097{
1098 return urcu_ref_get_unless_zero(&chunk->ref);
1099}
1100
1101static
1102void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
1103{
1104 struct lttng_trace_chunk_registry_element *element =
1105 container_of(node, typeof(*element), rcu_node);
1106
1107 lttng_trace_chunk_fini(&element->chunk);
1108 free(element);
1109}
1110
1111static
1112void lttng_trace_chunk_release(struct urcu_ref *ref)
1113{
1114 struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk),
1115 ref);
1116
1117 if (chunk->close_command.is_set) {
1118 close_command_funcs[chunk->close_command.value](chunk);
1119 }
1120
1121 if (chunk->in_registry_element) {
1122 struct lttng_trace_chunk_registry_element *element;
1123
1124 element = container_of(chunk, typeof(*element), chunk);
1125 if (element->registry) {
1126 rcu_read_lock();
1127 cds_lfht_del(element->registry->ht,
1128 &element->trace_chunk_registry_ht_node);
1129 rcu_read_unlock();
1130 call_rcu(&element->rcu_node,
1131 free_lttng_trace_chunk_registry_element);
1132 } else {
1133 /* Never published, can be free'd immediately. */
1134 free_lttng_trace_chunk_registry_element(
1135 &element->rcu_node);
1136 }
1137 } else {
1138 /* Not RCU-protected, free immediately. */
1139 lttng_trace_chunk_fini(chunk);
1140 free(chunk);
1141 }
1142}
1143
1144LTTNG_HIDDEN
1145void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
1146{
1147 if (!chunk) {
1148 return;
1149 }
1150 assert(chunk->ref.refcount);
1151 urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
1152}
1153
1154LTTNG_HIDDEN
1155struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void)
1156{
1157 struct lttng_trace_chunk_registry *registry;
1158
1159 registry = zmalloc(sizeof(*registry));
1160 if (!registry) {
1161 goto end;
1162 }
1163
1164 registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
1165 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
1166 if (!registry->ht) {
1167 goto error;
1168 }
1169end:
1170 return registry;
1171error:
1172 lttng_trace_chunk_registry_destroy(registry);
8243bf12 1173 return NULL;
2c5ff4e4
JG
1174}
1175
1176LTTNG_HIDDEN
1177void lttng_trace_chunk_registry_destroy(
1178 struct lttng_trace_chunk_registry *registry)
1179{
1180 if (!registry) {
1181 return;
1182 }
1183 if (registry->ht) {
1184 int ret = cds_lfht_destroy(registry->ht, NULL);
1185 assert(!ret);
1186 }
1187 free(registry);
1188}
1189
1190static
1191struct lttng_trace_chunk_registry_element *
1192lttng_trace_chunk_registry_element_create_from_chunk(
1193 struct lttng_trace_chunk *chunk, uint64_t session_id)
1194{
1195 struct lttng_trace_chunk_registry_element *element =
1196 zmalloc(sizeof(*element));
1197
1198 if (!element) {
1199 goto end;
1200 }
1201 cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
1202 element->session_id = session_id;
1203
1204 element->chunk = *chunk;
1205 lttng_trace_chunk_init(&element->chunk);
cbf53d23
JG
1206 if (chunk->session_output_directory) {
1207 /* Transferred ownership. */
1208 element->chunk.session_output_directory =
1209 chunk->session_output_directory;
1210 chunk->session_output_directory = NULL;
1211 }
1212 if (chunk->chunk_directory) {
1213 /* Transferred ownership. */
1214 element->chunk.chunk_directory = chunk->chunk_directory;
1215 chunk->chunk_directory = NULL;
2c5ff4e4
JG
1216 }
1217 /*
1218 * The original chunk becomes invalid; the name attribute is transferred
1219 * to the new chunk instance.
1220 */
1221 chunk->name = NULL;
1222 element->chunk.in_registry_element = true;
1223end:
1224 return element;
1225}
1226
1227LTTNG_HIDDEN
1228struct lttng_trace_chunk *
1229lttng_trace_chunk_registry_publish_chunk(
1230 struct lttng_trace_chunk_registry *registry,
1231 uint64_t session_id, struct lttng_trace_chunk *chunk)
1232{
1233 struct lttng_trace_chunk_registry_element *element;
1234 unsigned long element_hash;
1235
1236 pthread_mutex_lock(&chunk->lock);
1237 element = lttng_trace_chunk_registry_element_create_from_chunk(chunk,
1238 session_id);
1239 pthread_mutex_unlock(&chunk->lock);
1240 if (!element) {
1241 goto end;
1242 }
1243 /*
1244 * chunk is now invalid, the only valid operation is a 'put' from the
1245 * caller.
1246 */
1247 chunk = NULL;
1248 element_hash = lttng_trace_chunk_registry_element_hash(element);
1249
1250 rcu_read_lock();
1251 while (1) {
1252 struct cds_lfht_node *published_node;
1253 struct lttng_trace_chunk *published_chunk;
1254 struct lttng_trace_chunk_registry_element *published_element;
1255
1256 published_node = cds_lfht_add_unique(registry->ht,
1257 element_hash,
1258 lttng_trace_chunk_registry_element_match,
1259 element,
1260 &element->trace_chunk_registry_ht_node);
1261 if (published_node == &element->trace_chunk_registry_ht_node) {
1262 /* Successfully published the new element. */
1263 element->registry = registry;
1264 /* Acquire a reference for the caller. */
1265 if (lttng_trace_chunk_get(&element->chunk)) {
1266 break;
1267 } else {
1268 /*
1269 * Another thread concurrently unpublished the
1270 * trace chunk. This is currently unexpected.
1271 *
1272 * Re-attempt to publish.
1273 */
87cee602 1274 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
2c5ff4e4
JG
1275 continue;
1276 }
1277 }
1278
1279 /*
1280 * An equivalent trace chunk was published before this trace
1281 * chunk. Attempt to acquire a reference to the one that was
1282 * already published and release the reference to the copy we
1283 * created if successful.
1284 */
1285 published_element = container_of(published_node,
1286 typeof(*published_element),
1287 trace_chunk_registry_ht_node);
1288 published_chunk = &published_element->chunk;
1289 if (lttng_trace_chunk_get(published_chunk)) {
1290 lttng_trace_chunk_put(&element->chunk);
1291 element = published_element;
1292 break;
1293 }
1294 /*
1295 * A reference to the previously published trace chunk could not
1296 * be acquired. Hence, retry to publish our copy of the trace
1297 * chunk.
1298 */
1299 }
1300 rcu_read_unlock();
1301end:
1302 return element ? &element->chunk : NULL;
1303}
1304
1305/*
1306 * Note that the caller must be registered as an RCU thread.
1307 * However, it does not need to hold the RCU read lock. The RCU read lock is
1308 * acquired to perform the look-up in the registry's hash table and held until
1309 * after a reference to the "found" trace chunk is acquired.
1310 *
1311 * IOW, holding a reference guarantees the existence of the object for the
1312 * caller.
1313 */
1314static
1315struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
1316 const struct lttng_trace_chunk_registry *registry,
1317 uint64_t session_id, uint64_t *chunk_id)
1318{
1319 const struct lttng_trace_chunk_registry_element target_element = {
1320 .chunk.id.is_set = !!chunk_id,
1321 .chunk.id.value = chunk_id ? *chunk_id : 0,
1322 .session_id = session_id,
1323 };
1324 const unsigned long element_hash =
1325 lttng_trace_chunk_registry_element_hash(
1326 &target_element);
1327 struct cds_lfht_node *published_node;
1328 struct lttng_trace_chunk_registry_element *published_element;
1329 struct lttng_trace_chunk *published_chunk = NULL;
1330 struct cds_lfht_iter iter;
1331
1332 rcu_read_lock();
1333 cds_lfht_lookup(registry->ht,
1334 element_hash,
1335 lttng_trace_chunk_registry_element_match,
1336 &target_element,
1337 &iter);
1338 published_node = cds_lfht_iter_get_node(&iter);
1339 if (!published_node) {
1340 goto end;
1341 }
1342
1343 published_element = container_of(published_node,
1344 typeof(*published_element),
1345 trace_chunk_registry_ht_node);
1346 if (lttng_trace_chunk_get(&published_element->chunk)) {
1347 published_chunk = &published_element->chunk;
1348 }
1349end:
1350 rcu_read_unlock();
1351 return published_chunk;
1352}
1353
1354LTTNG_HIDDEN
1355struct lttng_trace_chunk *
1356lttng_trace_chunk_registry_find_chunk(
1357 const struct lttng_trace_chunk_registry *registry,
1358 uint64_t session_id, uint64_t chunk_id)
1359{
1360 return _lttng_trace_chunk_registry_find_chunk(registry,
1361 session_id, &chunk_id);
1362}
1363
6b584c2e
JG
1364LTTNG_HIDDEN
1365int lttng_trace_chunk_registry_chunk_exists(
1366 const struct lttng_trace_chunk_registry *registry,
1367 uint64_t session_id, uint64_t chunk_id, bool *chunk_exists)
1368{
1369 int ret = 0;
1370 const struct lttng_trace_chunk_registry_element target_element = {
1371 .chunk.id.is_set = true,
1372 .chunk.id.value = chunk_id,
1373 .session_id = session_id,
1374 };
1375 const unsigned long element_hash =
1376 lttng_trace_chunk_registry_element_hash(
1377 &target_element);
1378 struct cds_lfht_node *published_node;
1379 struct cds_lfht_iter iter;
1380
1381 rcu_read_lock();
1382 cds_lfht_lookup(registry->ht,
1383 element_hash,
1384 lttng_trace_chunk_registry_element_match,
1385 &target_element,
1386 &iter);
1387 published_node = cds_lfht_iter_get_node(&iter);
1388 if (!published_node) {
1389 *chunk_exists = false;
1390 goto end;
1391 }
1392
1393 *chunk_exists = !cds_lfht_is_node_deleted(published_node);
1394end:
1395 rcu_read_unlock();
1396 return ret;
1397}
1398
2c5ff4e4
JG
1399LTTNG_HIDDEN
1400struct lttng_trace_chunk *
1401lttng_trace_chunk_registry_find_anonymous_chunk(
1402 const struct lttng_trace_chunk_registry *registry,
1403 uint64_t session_id)
1404{
1405 return _lttng_trace_chunk_registry_find_chunk(registry,
1406 session_id, NULL);
1407}
e10aec8f
MD
1408
1409unsigned int lttng_trace_chunk_registry_put_each_chunk(
1410 struct lttng_trace_chunk_registry *registry)
1411{
1412 struct cds_lfht_iter iter;
1413 struct lttng_trace_chunk_registry_element *chunk_element;
1414 unsigned int trace_chunks_left = 0;
1415
1416 DBG("Releasing trace chunk registry to all trace chunks");
1417 rcu_read_lock();
1418 cds_lfht_for_each_entry(registry->ht,
1419 &iter, chunk_element, trace_chunk_registry_ht_node) {
1420 const char *chunk_id_str = "none";
1421 char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)];
1422
1423 pthread_mutex_lock(&chunk_element->chunk.lock);
1424 if (chunk_element->chunk.id.is_set) {
1425 int fmt_ret;
1426
1427 fmt_ret = snprintf(chunk_id_buf, sizeof(chunk_id_buf),
1428 "%" PRIu64,
1429 chunk_element->chunk.id.value);
1430 if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) {
1431 chunk_id_str = "formatting error";
1432 } else {
1433 chunk_id_str = chunk_id_buf;
1434 }
1435 }
1436
1437 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
1438 "chunk_id = %s, name = \"%s\", status = %s",
1439 chunk_element->session_id,
1440 chunk_id_str,
1441 chunk_element->chunk.name ? : "none",
1442 chunk_element->chunk.close_command.is_set ?
1443 "open" : "closed");
1444 pthread_mutex_unlock(&chunk_element->chunk.lock);
1445 lttng_trace_chunk_put(&chunk_element->chunk);
1446 trace_chunks_left++;
1447 }
1448 rcu_read_unlock();
1449 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left,
1450 __FUNCTION__);
1451
1452 return trace_chunks_left;
1453}
This page took 0.085646 seconds and 5 git commands to generate.