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