Add a copy method to the trace chunk interface
[lttng-tools.git] / src / common / trace-chunk.c
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
47 enum 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 */
58 typedef void (*chunk_close_command)(struct lttng_trace_chunk *trace_chunk);
59
60 /* Move a completed trace chunk to the 'completed' trace archive folder. */
61 static
62 void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk);
63
64 struct chunk_credentials {
65 bool use_current_user;
66 struct lttng_credentials user;
67 };
68
69 /* NOTE: Make sure to update lttng_trace_chunk_copy if you modify this. */
70 struct 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 *'.
77 *
78 * Only used by _owner_ mode chunks.
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;
83 bool name_overridden;
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. */
96 struct lttng_trace_chunk_registry_element {
97 struct lttng_trace_chunk chunk;
98 uint64_t session_id;
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
106 struct lttng_trace_chunk_registry {
107 struct cds_lfht *ht;
108 };
109
110 static const
111 char *close_command_names[] = {
112 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
113 "move to completed chunk folder",
114 };
115
116 static const
117 chunk_close_command close_command_funcs[] = {
118 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
119 lttng_trace_chunk_move_to_completed,
120 };
121
122 static
123 bool 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;
137 not_equal:
138 return false;
139 }
140
141 static
142 int 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
153 static
154 unsigned 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
167 static
168 char *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;
173 char start_datetime[ISO8601_STR_LEN] = {};
174 /* Add 1 for a '-' prefix. */
175 char end_datetime_suffix[ISO8601_STR_LEN + 1] = {};
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,
189 sizeof(end_datetime_suffix) - 1);
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;
208 error:
209 free(new_name);
210 return NULL;
211 }
212
213 static
214 void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk)
215 {
216 urcu_ref_init(&chunk->ref);
217 pthread_mutex_init(&chunk->lock, NULL);
218 lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free);
219 }
220
221 static
222 void 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;
233 lttng_dynamic_pointer_array_reset(&chunk->top_level_directories);
234 pthread_mutex_destroy(&chunk->lock);
235 }
236
237 static
238 struct 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);
248 end:
249 return chunk;
250 }
251
252 LTTNG_HIDDEN
253 struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous(void)
254 {
255 DBG("Creating anonymous trace chunk");
256 return lttng_trace_chunk_allocate();
257 }
258
259 LTTNG_HIDDEN
260 struct 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)");
301 end:
302 return chunk;
303 error:
304 lttng_trace_chunk_put(chunk);
305 return NULL;
306 }
307
308 LTTNG_HIDDEN
309 struct 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);
366 end:
367 return new_chunk;
368 error_unlock:
369 pthread_mutex_unlock(&source_chunk->lock);
370 return NULL;
371 }
372
373 LTTNG_HIDDEN
374 enum 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
389 LTTNG_HIDDEN
390 enum 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
406 LTTNG_HIDDEN
407 enum 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
422 LTTNG_HIDDEN
423 enum 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);
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 }
448 }
449 end:
450 pthread_mutex_unlock(&chunk->lock);
451 return status;
452 }
453
454 LTTNG_HIDDEN
455 enum lttng_trace_chunk_status lttng_trace_chunk_get_name(
456 struct lttng_trace_chunk *chunk, const char **name,
457 bool *name_overridden)
458 {
459 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
460
461 pthread_mutex_lock(&chunk->lock);
462 if (name_overridden) {
463 *name_overridden = chunk->name_overridden;
464 }
465 if (!chunk->name) {
466 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
467 goto end;
468 }
469 *name = chunk->name;
470 end:
471 pthread_mutex_unlock(&chunk->lock);
472 return status;
473 }
474
475 static
476 bool is_valid_chunk_name(const char *name)
477 {
478 size_t len;
479
480 if (!name) {
481 return false;
482 }
483
484 len = lttng_strnlen(name, LTTNG_NAME_MAX);
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
496 LTTNG_HIDDEN
497 enum 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
504 if (!is_valid_chunk_name(name)) {
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;
526 chunk->name_overridden = true;
527 end_unlock:
528 pthread_mutex_unlock(&chunk->lock);
529 end:
530 return status;
531 }
532
533 LTTNG_HIDDEN
534 enum 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
555 LTTNG_HIDDEN
556 enum 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);
572 end:
573 pthread_mutex_unlock(&chunk->lock);
574 return status;
575 }
576
577 LTTNG_HIDDEN
578 enum 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);
592 end:
593 pthread_mutex_unlock(&chunk->lock);
594 return status;
595 }
596
597
598 LTTNG_HIDDEN
599 enum 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;
605 struct lttng_directory_handle chunk_directory_handle;
606
607 pthread_mutex_lock(&chunk->lock);
608 if (chunk->mode.is_set) {
609 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
610 goto end;
611 }
612 if (!chunk->credentials.is_set) {
613 /*
614 * Fatal error, credentials must be set before a
615 * directory is created.
616 */
617 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
618 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
619 goto end;
620 }
621
622 if (chunk->name) {
623 /*
624 * A nameless chunk does not need its own output directory.
625 * The session's output directory will be used.
626 */
627 ret = lttng_directory_handle_create_subdirectory_as_user(
628 session_output_directory,
629 chunk->name,
630 DIR_CREATION_MODE,
631 !chunk->credentials.value.use_current_user ?
632 &chunk->credentials.value.user : NULL);
633 if (ret) {
634 PERROR("Failed to create chunk output directory \"%s\"",
635 chunk->name);
636 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
637 goto end;
638 }
639 }
640 ret = lttng_directory_handle_init_from_handle(&chunk_directory_handle,
641 chunk->name,
642 session_output_directory);
643 if (ret) {
644 /* The function already logs on all error paths. */
645 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
646 goto end;
647 }
648 LTTNG_OPTIONAL_SET(&chunk->session_output_directory,
649 lttng_directory_handle_move(session_output_directory));
650 LTTNG_OPTIONAL_SET(&chunk->chunk_directory,
651 lttng_directory_handle_move(&chunk_directory_handle));
652 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER);
653 end:
654 pthread_mutex_unlock(&chunk->lock);
655 return status;
656 }
657
658 LTTNG_HIDDEN
659 enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user(
660 struct lttng_trace_chunk *chunk,
661 struct lttng_directory_handle *chunk_directory)
662 {
663 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
664
665 pthread_mutex_lock(&chunk->lock);
666 if (chunk->mode.is_set) {
667 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
668 goto end;
669 }
670 if (!chunk->credentials.is_set) {
671 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
672 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
673 goto end;
674 }
675 LTTNG_OPTIONAL_SET(&chunk->chunk_directory,
676 lttng_directory_handle_move(chunk_directory));
677 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER);
678 end:
679 pthread_mutex_unlock(&chunk->lock);
680 return status;
681 }
682
683 LTTNG_HIDDEN
684 enum lttng_trace_chunk_status lttng_trace_chunk_get_chunk_directory_handle(
685 struct lttng_trace_chunk *chunk,
686 const struct lttng_directory_handle **handle)
687 {
688 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
689
690 pthread_mutex_lock(&chunk->lock);
691 if (!chunk->chunk_directory.is_set) {
692 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
693 goto end;
694 }
695
696 *handle = &chunk->chunk_directory.value;
697 end:
698 pthread_mutex_unlock(&chunk->lock);
699 return status;
700 }
701
702 /* Add a top-level directory to the trace chunk if it was previously unknown. */
703 static
704 int add_top_level_directory_unique(struct lttng_trace_chunk *chunk,
705 const char *new_path)
706 {
707 int ret = 0;
708 bool found = false;
709 size_t i, count = lttng_dynamic_pointer_array_get_count(
710 &chunk->top_level_directories);
711 const char *new_path_separator_pos = strchr(new_path, '/');
712 const ptrdiff_t new_path_top_level_len = new_path_separator_pos ?
713 new_path_separator_pos - new_path : strlen(new_path);
714
715 for (i = 0; i < count; i++) {
716 const char *path = lttng_dynamic_pointer_array_get_pointer(
717 &chunk->top_level_directories, i);
718 const ptrdiff_t path_top_level_len = strlen(path);
719
720 if (path_top_level_len != new_path_top_level_len) {
721 continue;
722 }
723 if (!strncmp(path, new_path, path_top_level_len)) {
724 found = true;
725 break;
726 }
727 }
728
729 if (!found) {
730 char *copy = lttng_strndup(new_path, new_path_top_level_len);
731
732 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
733 new_path, chunk->name ? : "(unnamed)");
734 if (!copy) {
735 PERROR("Failed to copy path");
736 ret = -1;
737 goto end;
738 }
739 ret = lttng_dynamic_pointer_array_add_pointer(
740 &chunk->top_level_directories, copy);
741 if (ret) {
742 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
743 free(copy);
744 goto end;
745 }
746 }
747 end:
748 return ret;
749 }
750
751 LTTNG_HIDDEN
752 enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory(
753 struct lttng_trace_chunk *chunk,
754 const char *path)
755 {
756 int ret;
757 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
758
759 DBG("Creating trace chunk subdirectory \"%s\"", path);
760 pthread_mutex_lock(&chunk->lock);
761 if (!chunk->credentials.is_set) {
762 /*
763 * Fatal error, credentials must be set before a
764 * directory is created.
765 */
766 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
767 path);
768 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
769 goto end;
770 }
771 if (!chunk->mode.is_set ||
772 chunk->mode.value != TRACE_CHUNK_MODE_OWNER) {
773 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
774 path);
775 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
776 goto end;
777 }
778 if (!chunk->chunk_directory.is_set) {
779 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
780 path);
781 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
782 goto end;
783 }
784 if (*path == '/') {
785 ERR("Refusing to create absolute trace chunk directory \"%s\"",
786 path);
787 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
788 goto end;
789 }
790 ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
791 &chunk->chunk_directory.value, path,
792 DIR_CREATION_MODE,
793 chunk->credentials.value.use_current_user ?
794 NULL : &chunk->credentials.value.user);
795 if (ret) {
796 PERROR("Failed to create trace chunk subdirectory \"%s\"",
797 path);
798 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
799 goto end;
800 }
801 ret = add_top_level_directory_unique(chunk, path);
802 if (ret) {
803 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
804 goto end;
805 }
806 end:
807 pthread_mutex_unlock(&chunk->lock);
808 return status;
809 }
810
811 LTTNG_HIDDEN
812 enum lttng_trace_chunk_status lttng_trace_chunk_open_file(
813 struct lttng_trace_chunk *chunk, const char *file_path,
814 int flags, mode_t mode, int *out_fd)
815 {
816 int ret;
817 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
818
819 DBG("Opening trace chunk file \"%s\"", file_path);
820 pthread_mutex_lock(&chunk->lock);
821 if (!chunk->credentials.is_set) {
822 /*
823 * Fatal error, credentials must be set before a
824 * file is created.
825 */
826 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
827 file_path);
828 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
829 goto end;
830 }
831 if (!chunk->chunk_directory.is_set) {
832 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
833 file_path);
834 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
835 goto end;
836 }
837 ret = lttng_directory_handle_open_file_as_user(
838 &chunk->chunk_directory.value, file_path, flags, mode,
839 chunk->credentials.value.use_current_user ?
840 NULL : &chunk->credentials.value.user);
841 if (ret < 0) {
842 ERR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
843 file_path, flags, (int) mode);
844 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
845 goto end;
846 }
847 *out_fd = ret;
848 end:
849 pthread_mutex_unlock(&chunk->lock);
850 return status;
851 }
852
853 LTTNG_HIDDEN
854 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk,
855 const char *file_path)
856 {
857 int ret;
858 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
859
860 DBG("Unlinking trace chunk file \"%s\"", file_path);
861 pthread_mutex_lock(&chunk->lock);
862 if (!chunk->credentials.is_set) {
863 /*
864 * Fatal error, credentials must be set before a
865 * directory is created.
866 */
867 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
868 file_path);
869 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
870 goto end;
871 }
872 if (!chunk->chunk_directory.is_set) {
873 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
874 file_path);
875 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
876 goto end;
877 }
878 ret = lttng_directory_handle_unlink_file_as_user(
879 &chunk->chunk_directory.value, file_path,
880 chunk->credentials.value.use_current_user ?
881 NULL : &chunk->credentials.value.user);
882 if (ret < 0) {
883 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
884 goto end;
885 }
886 end:
887 pthread_mutex_unlock(&chunk->lock);
888 return status;
889 }
890
891 static
892 void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk)
893 {
894 int ret;
895 char *directory_to_rename = NULL;
896 bool free_directory_to_rename = false;
897 char *archived_chunk_name = NULL;
898 const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id);
899 const time_t creation_timestamp =
900 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation);
901 const time_t close_timestamp =
902 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close);
903 LTTNG_OPTIONAL(struct lttng_directory_handle) archived_chunks_directory = {};
904
905 if (!trace_chunk->mode.is_set ||
906 trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER ||
907 !trace_chunk->session_output_directory.is_set) {
908 /*
909 * This command doesn't need to run if the output is remote
910 * or if the trace chunk is not owned by this process.
911 */
912 goto end;
913 }
914
915 assert(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
916 assert(!trace_chunk->name_overridden);
917
918 /*
919 * The fist trace chunk of a session is directly output to the
920 * session's output folder. In this case, the top level directories
921 * must be moved to a temporary folder before that temporary directory
922 * is renamed to match the chunk's name.
923 */
924 if (chunk_id == 0) {
925 struct lttng_directory_handle temporary_rename_directory;
926 size_t i, count = lttng_dynamic_pointer_array_get_count(
927 &trace_chunk->top_level_directories);
928
929 ret = lttng_directory_handle_create_subdirectory_as_user(
930 &trace_chunk->session_output_directory.value,
931 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY,
932 DIR_CREATION_MODE,
933 !trace_chunk->credentials.value.use_current_user ?
934 &trace_chunk->credentials.value.user : NULL);
935 if (ret) {
936 PERROR("Failed to create temporary trace chunk rename directory \"%s\"",
937 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY);
938 }
939
940 ret = lttng_directory_handle_init_from_handle(&temporary_rename_directory,
941 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY,
942 &trace_chunk->session_output_directory.value);
943 if (ret) {
944 ERR("Failed to get handle to temporary trace chunk rename directory");
945 goto end;
946 }
947
948 for (i = 0; i < count; i++) {
949 const char *top_level_name =
950 lttng_dynamic_pointer_array_get_pointer(
951 &trace_chunk->top_level_directories, i);
952
953 ret = lttng_directory_handle_rename_as_user(
954 &trace_chunk->session_output_directory.value,
955 top_level_name,
956 &temporary_rename_directory,
957 top_level_name,
958 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
959 NULL :
960 &trace_chunk->credentials.value.user);
961 if (ret) {
962 PERROR("Failed to move \"%s\" to temporary trace chunk rename directory",
963 top_level_name);
964 lttng_directory_handle_fini(
965 &temporary_rename_directory);
966 goto end;
967 }
968 }
969 lttng_directory_handle_fini(&temporary_rename_directory);
970 directory_to_rename = DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY;
971 free_directory_to_rename = false;
972 } else {
973 directory_to_rename = generate_chunk_name(chunk_id,
974 creation_timestamp, NULL);
975 if (!directory_to_rename) {
976 ERR("Failed to generate initial trace chunk name while renaming trace chunk");
977 }
978 free_directory_to_rename = true;
979 }
980
981 archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp,
982 &close_timestamp);
983 if (!archived_chunk_name) {
984 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
985 goto end;
986 }
987
988 ret = lttng_directory_handle_create_subdirectory_as_user(
989 &trace_chunk->session_output_directory.value,
990 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
991 DIR_CREATION_MODE,
992 !trace_chunk->credentials.value.use_current_user ?
993 &trace_chunk->credentials.value.user :
994 NULL);
995 if (ret) {
996 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
997 "\" directory for archived trace chunks");
998 goto end;
999 }
1000
1001 ret = lttng_directory_handle_init_from_handle(
1002 &archived_chunks_directory.value,
1003 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
1004 &trace_chunk->session_output_directory.value);
1005 if (ret) {
1006 PERROR("Failed to get handle to archived trace chunks directory");
1007 goto end;
1008 }
1009 archived_chunks_directory.is_set = true;
1010
1011 ret = lttng_directory_handle_rename_as_user(
1012 &trace_chunk->session_output_directory.value,
1013 directory_to_rename,
1014 &archived_chunks_directory.value,
1015 archived_chunk_name,
1016 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
1017 NULL :
1018 &trace_chunk->credentials.value.user);
1019 if (ret) {
1020 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1021 directory_to_rename, archived_chunk_name);
1022 }
1023
1024 end:
1025 if (archived_chunks_directory.is_set) {
1026 lttng_directory_handle_fini(&archived_chunks_directory.value);
1027 }
1028 free(archived_chunk_name);
1029 if (free_directory_to_rename) {
1030 free(directory_to_rename);
1031 }
1032 }
1033
1034 LTTNG_HIDDEN
1035 enum lttng_trace_chunk_status lttng_trace_chunk_get_close_command(
1036 struct lttng_trace_chunk *chunk,
1037 enum lttng_trace_chunk_command_type *command_type)
1038 {
1039 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1040
1041 pthread_mutex_lock(&chunk->lock);
1042 if (chunk->close_command.is_set) {
1043 *command_type = chunk->close_command.value;
1044 status = LTTNG_TRACE_CHUNK_STATUS_OK;
1045 } else {
1046 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1047 }
1048 pthread_mutex_unlock(&chunk->lock);
1049 return status;
1050 }
1051
1052 LTTNG_HIDDEN
1053 enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
1054 struct lttng_trace_chunk *chunk,
1055 enum lttng_trace_chunk_command_type close_command)
1056 {
1057 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1058
1059 if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
1060 close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
1061 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
1062 goto end;
1063 }
1064
1065 pthread_mutex_lock(&chunk->lock);
1066 if (chunk->close_command.is_set) {
1067 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1068 close_command_names[chunk->close_command.value],
1069 close_command_names[close_command]);
1070 } else {
1071 DBG("Setting trace chunk close command to \"%s\"",
1072 close_command_names[close_command]);
1073 }
1074 LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
1075 pthread_mutex_unlock(&chunk->lock);
1076 end:
1077 return status;
1078 }
1079
1080 LTTNG_HIDDEN
1081 const char *lttng_trace_chunk_command_type_get_name(
1082 enum lttng_trace_chunk_command_type command)
1083 {
1084 switch (command) {
1085 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
1086 return "move to completed trace chunk folder";
1087 default:
1088 abort();
1089 }
1090 }
1091
1092 LTTNG_HIDDEN
1093 bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
1094 {
1095 return urcu_ref_get_unless_zero(&chunk->ref);
1096 }
1097
1098 static
1099 void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
1100 {
1101 struct lttng_trace_chunk_registry_element *element =
1102 container_of(node, typeof(*element), rcu_node);
1103
1104 lttng_trace_chunk_fini(&element->chunk);
1105 free(element);
1106 }
1107
1108 static
1109 void lttng_trace_chunk_release(struct urcu_ref *ref)
1110 {
1111 struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk),
1112 ref);
1113
1114 if (chunk->close_command.is_set) {
1115 close_command_funcs[chunk->close_command.value](chunk);
1116 }
1117
1118 if (chunk->in_registry_element) {
1119 struct lttng_trace_chunk_registry_element *element;
1120
1121 element = container_of(chunk, typeof(*element), chunk);
1122 if (element->registry) {
1123 rcu_read_lock();
1124 cds_lfht_del(element->registry->ht,
1125 &element->trace_chunk_registry_ht_node);
1126 rcu_read_unlock();
1127 call_rcu(&element->rcu_node,
1128 free_lttng_trace_chunk_registry_element);
1129 } else {
1130 /* Never published, can be free'd immediately. */
1131 free_lttng_trace_chunk_registry_element(
1132 &element->rcu_node);
1133 }
1134 } else {
1135 /* Not RCU-protected, free immediately. */
1136 lttng_trace_chunk_fini(chunk);
1137 free(chunk);
1138 }
1139 }
1140
1141 LTTNG_HIDDEN
1142 void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
1143 {
1144 if (!chunk) {
1145 return;
1146 }
1147 assert(chunk->ref.refcount);
1148 urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
1149 }
1150
1151 LTTNG_HIDDEN
1152 struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void)
1153 {
1154 struct lttng_trace_chunk_registry *registry;
1155
1156 registry = zmalloc(sizeof(*registry));
1157 if (!registry) {
1158 goto end;
1159 }
1160
1161 registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
1162 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
1163 if (!registry->ht) {
1164 goto error;
1165 }
1166 end:
1167 return registry;
1168 error:
1169 lttng_trace_chunk_registry_destroy(registry);
1170 return NULL;
1171 }
1172
1173 LTTNG_HIDDEN
1174 void lttng_trace_chunk_registry_destroy(
1175 struct lttng_trace_chunk_registry *registry)
1176 {
1177 if (!registry) {
1178 return;
1179 }
1180 if (registry->ht) {
1181 int ret = cds_lfht_destroy(registry->ht, NULL);
1182 assert(!ret);
1183 }
1184 free(registry);
1185 }
1186
1187 static
1188 struct lttng_trace_chunk_registry_element *
1189 lttng_trace_chunk_registry_element_create_from_chunk(
1190 struct lttng_trace_chunk *chunk, uint64_t session_id)
1191 {
1192 struct lttng_trace_chunk_registry_element *element =
1193 zmalloc(sizeof(*element));
1194
1195 if (!element) {
1196 goto end;
1197 }
1198 cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
1199 element->session_id = session_id;
1200
1201 element->chunk = *chunk;
1202 lttng_trace_chunk_init(&element->chunk);
1203 if (chunk->session_output_directory.is_set) {
1204 element->chunk.session_output_directory.value =
1205 lttng_directory_handle_move(
1206 &chunk->session_output_directory.value);
1207 }
1208 if (chunk->chunk_directory.is_set) {
1209 element->chunk.chunk_directory.value =
1210 lttng_directory_handle_move(
1211 &chunk->chunk_directory.value);
1212 }
1213 /*
1214 * The original chunk becomes invalid; the name attribute is transferred
1215 * to the new chunk instance.
1216 */
1217 chunk->name = NULL;
1218 element->chunk.in_registry_element = true;
1219 end:
1220 return element;
1221 }
1222
1223 LTTNG_HIDDEN
1224 struct lttng_trace_chunk *
1225 lttng_trace_chunk_registry_publish_chunk(
1226 struct lttng_trace_chunk_registry *registry,
1227 uint64_t session_id, struct lttng_trace_chunk *chunk)
1228 {
1229 struct lttng_trace_chunk_registry_element *element;
1230 unsigned long element_hash;
1231
1232 pthread_mutex_lock(&chunk->lock);
1233 element = lttng_trace_chunk_registry_element_create_from_chunk(chunk,
1234 session_id);
1235 pthread_mutex_unlock(&chunk->lock);
1236 if (!element) {
1237 goto end;
1238 }
1239 /*
1240 * chunk is now invalid, the only valid operation is a 'put' from the
1241 * caller.
1242 */
1243 chunk = NULL;
1244 element_hash = lttng_trace_chunk_registry_element_hash(element);
1245
1246 rcu_read_lock();
1247 while (1) {
1248 struct cds_lfht_node *published_node;
1249 struct lttng_trace_chunk *published_chunk;
1250 struct lttng_trace_chunk_registry_element *published_element;
1251
1252 published_node = cds_lfht_add_unique(registry->ht,
1253 element_hash,
1254 lttng_trace_chunk_registry_element_match,
1255 element,
1256 &element->trace_chunk_registry_ht_node);
1257 if (published_node == &element->trace_chunk_registry_ht_node) {
1258 /* Successfully published the new element. */
1259 element->registry = registry;
1260 /* Acquire a reference for the caller. */
1261 if (lttng_trace_chunk_get(&element->chunk)) {
1262 break;
1263 } else {
1264 /*
1265 * Another thread concurrently unpublished the
1266 * trace chunk. This is currently unexpected.
1267 *
1268 * Re-attempt to publish.
1269 */
1270 ERR("Attemp to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1271 continue;
1272 }
1273 }
1274
1275 /*
1276 * An equivalent trace chunk was published before this trace
1277 * chunk. Attempt to acquire a reference to the one that was
1278 * already published and release the reference to the copy we
1279 * created if successful.
1280 */
1281 published_element = container_of(published_node,
1282 typeof(*published_element),
1283 trace_chunk_registry_ht_node);
1284 published_chunk = &published_element->chunk;
1285 if (lttng_trace_chunk_get(published_chunk)) {
1286 lttng_trace_chunk_put(&element->chunk);
1287 element = published_element;
1288 break;
1289 }
1290 /*
1291 * A reference to the previously published trace chunk could not
1292 * be acquired. Hence, retry to publish our copy of the trace
1293 * chunk.
1294 */
1295 }
1296 rcu_read_unlock();
1297 end:
1298 return element ? &element->chunk : NULL;
1299 }
1300
1301 /*
1302 * Note that the caller must be registered as an RCU thread.
1303 * However, it does not need to hold the RCU read lock. The RCU read lock is
1304 * acquired to perform the look-up in the registry's hash table and held until
1305 * after a reference to the "found" trace chunk is acquired.
1306 *
1307 * IOW, holding a reference guarantees the existence of the object for the
1308 * caller.
1309 */
1310 static
1311 struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
1312 const struct lttng_trace_chunk_registry *registry,
1313 uint64_t session_id, uint64_t *chunk_id)
1314 {
1315 const struct lttng_trace_chunk_registry_element target_element = {
1316 .chunk.id.is_set = !!chunk_id,
1317 .chunk.id.value = chunk_id ? *chunk_id : 0,
1318 .session_id = session_id,
1319 };
1320 const unsigned long element_hash =
1321 lttng_trace_chunk_registry_element_hash(
1322 &target_element);
1323 struct cds_lfht_node *published_node;
1324 struct lttng_trace_chunk_registry_element *published_element;
1325 struct lttng_trace_chunk *published_chunk = NULL;
1326 struct cds_lfht_iter iter;
1327
1328 rcu_read_lock();
1329 cds_lfht_lookup(registry->ht,
1330 element_hash,
1331 lttng_trace_chunk_registry_element_match,
1332 &target_element,
1333 &iter);
1334 published_node = cds_lfht_iter_get_node(&iter);
1335 if (!published_node) {
1336 goto end;
1337 }
1338
1339 published_element = container_of(published_node,
1340 typeof(*published_element),
1341 trace_chunk_registry_ht_node);
1342 if (lttng_trace_chunk_get(&published_element->chunk)) {
1343 published_chunk = &published_element->chunk;
1344 }
1345 end:
1346 rcu_read_unlock();
1347 return published_chunk;
1348 }
1349
1350 LTTNG_HIDDEN
1351 struct lttng_trace_chunk *
1352 lttng_trace_chunk_registry_find_chunk(
1353 const struct lttng_trace_chunk_registry *registry,
1354 uint64_t session_id, uint64_t chunk_id)
1355 {
1356 return _lttng_trace_chunk_registry_find_chunk(registry,
1357 session_id, &chunk_id);
1358 }
1359
1360 LTTNG_HIDDEN
1361 struct lttng_trace_chunk *
1362 lttng_trace_chunk_registry_find_anonymous_chunk(
1363 const struct lttng_trace_chunk_registry *registry,
1364 uint64_t session_id)
1365 {
1366 return _lttng_trace_chunk_registry_find_chunk(registry,
1367 session_id, NULL);
1368 }
1369
1370 unsigned int lttng_trace_chunk_registry_put_each_chunk(
1371 struct lttng_trace_chunk_registry *registry)
1372 {
1373 struct cds_lfht_iter iter;
1374 struct lttng_trace_chunk_registry_element *chunk_element;
1375 unsigned int trace_chunks_left = 0;
1376
1377 DBG("Releasing trace chunk registry to all trace chunks");
1378 rcu_read_lock();
1379 cds_lfht_for_each_entry(registry->ht,
1380 &iter, chunk_element, trace_chunk_registry_ht_node) {
1381 const char *chunk_id_str = "none";
1382 char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)];
1383
1384 pthread_mutex_lock(&chunk_element->chunk.lock);
1385 if (chunk_element->chunk.id.is_set) {
1386 int fmt_ret;
1387
1388 fmt_ret = snprintf(chunk_id_buf, sizeof(chunk_id_buf),
1389 "%" PRIu64,
1390 chunk_element->chunk.id.value);
1391 if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) {
1392 chunk_id_str = "formatting error";
1393 } else {
1394 chunk_id_str = chunk_id_buf;
1395 }
1396 }
1397
1398 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
1399 "chunk_id = %s, name = \"%s\", status = %s",
1400 chunk_element->session_id,
1401 chunk_id_str,
1402 chunk_element->chunk.name ? : "none",
1403 chunk_element->chunk.close_command.is_set ?
1404 "open" : "closed");
1405 pthread_mutex_unlock(&chunk_element->chunk.lock);
1406 lttng_trace_chunk_put(&chunk_element->chunk);
1407 trace_chunks_left++;
1408 }
1409 rcu_read_unlock();
1410 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left,
1411 __FUNCTION__);
1412
1413 return trace_chunks_left;
1414 }
This page took 0.095679 seconds and 5 git commands to generate.