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