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