directory-handle: make lttng_directory_handle_stat public
[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 <common/compat/directory-handle.h>
19 #include <common/credentials.h>
20 #include <common/defaults.h>
21 #include <common/dynamic-array.h>
22 #include <common/error.h>
23 #include <common/fd-tracker/fd-tracker.h>
24 #include <common/hashtable/hashtable.h>
25 #include <common/hashtable/utils.h>
26 #include <common/optional.h>
27 #include <common/string-utils/format.h>
28 #include <common/time.h>
29 #include <common/trace-chunk-registry.h>
30 #include <common/trace-chunk.h>
31 #include <common/utils.h>
32 #include <lttng/constant.h>
33
34 #include <inttypes.h>
35 #include <pthread.h>
36 #include <stdio.h>
37 #include <sys/stat.h>
38 #include <urcu/rculfhash.h>
39 #include <urcu/ref.h>
40
41 /*
42 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
43 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
44 */
45 #define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
46 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
47
48 enum trace_chunk_mode {
49 TRACE_CHUNK_MODE_USER,
50 TRACE_CHUNK_MODE_OWNER,
51 };
52
53 /*
54 * Callback to invoke on release of a trace chunk. Note that there is no
55 * need to 'lock' the trace chunk during the execution of these callbacks
56 * since only one thread may access a chunk during its destruction (the last
57 * to release its reference to the chunk).
58 */
59 typedef int (*chunk_command)(struct lttng_trace_chunk *trace_chunk);
60
61 /* Move a completed trace chunk to the 'completed' trace archive folder. */
62 static
63 int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk *trace_chunk);
64 /* Empty callback. */
65 static
66 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk);
67 /* Unlink old chunk files. */
68 static
69 int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk *trace_chunk);
70 static
71 enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock(
72 struct lttng_trace_chunk *chunk, const char *path);
73
74 struct chunk_credentials {
75 bool use_current_user;
76 struct lttng_credentials user;
77 };
78
79 /*
80 * NOTE: Make sure to update:
81 * - lttng_trace_chunk_copy(),
82 * - lttng_trace_chunk_registry_element_create_from_chunk()
83 * if you modify this structure.
84 */
85 struct lttng_trace_chunk {
86 pthread_mutex_t lock;
87 struct urcu_ref ref;
88 LTTNG_OPTIONAL(enum trace_chunk_mode) mode;
89 /*
90 * First-level directories created within the trace chunk.
91 * Elements are of type 'char *'.
92 *
93 * Only used by _owner_ mode chunks.
94 */
95 struct lttng_dynamic_pointer_array top_level_directories;
96 /*
97 * All files contained within the trace chunk.
98 * Array of paths (char *).
99 */
100 struct lttng_dynamic_pointer_array files;
101 /* Is contained within an lttng_trace_chunk_registry_element? */
102 bool in_registry_element;
103 bool name_overridden;
104 char *name;
105 char *path;
106 /* An unset id means the chunk is anonymous. */
107 LTTNG_OPTIONAL(uint64_t) id;
108 LTTNG_OPTIONAL(time_t) timestamp_creation;
109 LTTNG_OPTIONAL(time_t) timestamp_close;
110 LTTNG_OPTIONAL(struct chunk_credentials) credentials;
111 struct lttng_directory_handle *session_output_directory;
112 struct lttng_directory_handle *chunk_directory;
113 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type) close_command;
114 /*
115 * fd_tracker instance through which file descriptors should be
116 * created/closed.
117 *
118 * An fd_tracker always outlives any trace chunk; there is no
119 * need to perform any reference counting of that object.
120 */
121 struct fd_tracker *fd_tracker;
122 };
123
124 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
125 struct lttng_trace_chunk_registry_element {
126 struct lttng_trace_chunk chunk;
127 uint64_t session_id;
128 /* Weak and only set when added. */
129 struct lttng_trace_chunk_registry *registry;
130 struct cds_lfht_node trace_chunk_registry_ht_node;
131 /* call_rcu delayed reclaim. */
132 struct rcu_head rcu_node;
133 };
134
135 struct lttng_trace_chunk_registry {
136 struct cds_lfht *ht;
137 };
138
139 static const
140 char *close_command_names[] = {
141 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
142 "move to completed chunk folder",
143 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION] =
144 "no operation",
145 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE] =
146 "delete",
147 };
148
149 static const
150 chunk_command close_command_post_release_funcs[] = {
151 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
152 lttng_trace_chunk_move_to_completed_post_release,
153 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION] =
154 lttng_trace_chunk_no_operation,
155 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE] =
156 lttng_trace_chunk_delete_post_release,
157 };
158
159 static
160 bool lttng_trace_chunk_registry_element_equals(
161 const struct lttng_trace_chunk_registry_element *a,
162 const struct lttng_trace_chunk_registry_element *b)
163 {
164 if (a->session_id != b->session_id) {
165 goto not_equal;
166 }
167 if (a->chunk.id.is_set != b->chunk.id.is_set) {
168 goto not_equal;
169 }
170 if (a->chunk.id.is_set && a->chunk.id.value != b->chunk.id.value) {
171 goto not_equal;
172 }
173 return true;
174 not_equal:
175 return false;
176 }
177
178 static
179 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node *node,
180 const void *key)
181 {
182 const struct lttng_trace_chunk_registry_element *element_a, *element_b;
183
184 element_a = (const struct lttng_trace_chunk_registry_element *) key;
185 element_b = caa_container_of(node, typeof(*element_b),
186 trace_chunk_registry_ht_node);
187 return lttng_trace_chunk_registry_element_equals(element_a, element_b);
188 }
189
190 static
191 unsigned long lttng_trace_chunk_registry_element_hash(
192 const struct lttng_trace_chunk_registry_element *element)
193 {
194 unsigned long hash = hash_key_u64(&element->session_id,
195 lttng_ht_seed);
196
197 if (element->chunk.id.is_set) {
198 hash ^= hash_key_u64(&element->chunk.id.value, lttng_ht_seed);
199 }
200
201 return hash;
202 }
203
204 static
205 char *generate_chunk_name(uint64_t chunk_id, time_t creation_timestamp,
206 const time_t *close_timestamp)
207 {
208 int ret = 0;
209 char *new_name= NULL;
210 char start_datetime[ISO8601_STR_LEN] = {};
211 /* Add 1 for a '-' prefix. */
212 char end_datetime_suffix[ISO8601_STR_LEN + 1] = {};
213
214 ret = time_to_iso8601_str(
215 creation_timestamp,
216 start_datetime, sizeof(start_datetime));
217 if (ret) {
218 ERR("Failed to format trace chunk start date time");
219 goto error;
220 }
221 if (close_timestamp) {
222 *end_datetime_suffix = '-';
223 ret = time_to_iso8601_str(
224 *close_timestamp,
225 end_datetime_suffix + 1,
226 sizeof(end_datetime_suffix) - 1);
227 if (ret) {
228 ERR("Failed to format trace chunk end date time");
229 goto error;
230 }
231 }
232 new_name = zmalloc(GENERATED_CHUNK_NAME_LEN);
233 if (!new_name) {
234 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
235 goto error;
236 }
237 ret = snprintf(new_name, GENERATED_CHUNK_NAME_LEN, "%s%s-%" PRIu64,
238 start_datetime, end_datetime_suffix, chunk_id);
239 if (ret >= GENERATED_CHUNK_NAME_LEN || ret == -1) {
240 ERR("Failed to format trace chunk name");
241 goto error;
242 }
243
244 return new_name;
245 error:
246 free(new_name);
247 return NULL;
248 }
249
250 static
251 void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk)
252 {
253 urcu_ref_init(&chunk->ref);
254 pthread_mutex_init(&chunk->lock, NULL);
255 lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free);
256 lttng_dynamic_pointer_array_init(&chunk->files, free);
257 }
258
259 static
260 void lttng_trace_chunk_fini(struct lttng_trace_chunk *chunk)
261 {
262 if (chunk->session_output_directory) {
263 lttng_directory_handle_put(
264 chunk->session_output_directory);
265 chunk->session_output_directory = NULL;
266 }
267 if (chunk->chunk_directory) {
268 lttng_directory_handle_put(chunk->chunk_directory);
269 chunk->chunk_directory = NULL;
270 }
271 free(chunk->name);
272 chunk->name = NULL;
273 free(chunk->path);
274 chunk->path = NULL;
275 lttng_dynamic_pointer_array_reset(&chunk->top_level_directories);
276 lttng_dynamic_pointer_array_reset(&chunk->files);
277 pthread_mutex_destroy(&chunk->lock);
278 }
279
280 static
281 struct lttng_trace_chunk *lttng_trace_chunk_allocate(void)
282 {
283 struct lttng_trace_chunk *chunk = NULL;
284
285 chunk = zmalloc(sizeof(*chunk));
286 if (!chunk) {
287 ERR("Failed to allocate trace chunk");
288 goto end;
289 }
290 lttng_trace_chunk_init(chunk);
291 end:
292 return chunk;
293 }
294
295 LTTNG_HIDDEN
296 struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous(void)
297 {
298 DBG("Creating anonymous trace chunk");
299 return lttng_trace_chunk_allocate();
300 }
301
302 LTTNG_HIDDEN
303 struct lttng_trace_chunk *lttng_trace_chunk_create(
304 uint64_t chunk_id, time_t chunk_creation_time, const char *path)
305 {
306 struct lttng_trace_chunk *chunk;
307 char chunk_creation_datetime_buf[16] = {};
308 const char *chunk_creation_datetime_str = "(formatting error)";
309 struct tm timeinfo_buf, *timeinfo;
310
311 timeinfo = localtime_r(&chunk_creation_time, &timeinfo_buf);
312 if (timeinfo) {
313 size_t strftime_ret;
314
315 /* Don't fail because of this; it is only used for logging. */
316 strftime_ret = strftime(chunk_creation_datetime_buf,
317 sizeof(chunk_creation_datetime_buf),
318 "%Y%m%d-%H%M%S", timeinfo);
319 if (strftime_ret) {
320 chunk_creation_datetime_str =
321 chunk_creation_datetime_buf;
322 }
323 }
324
325 DBG("Creating trace chunk: chunk_id = %" PRIu64 ", creation time = %s",
326 chunk_id, chunk_creation_datetime_str);
327 chunk = lttng_trace_chunk_allocate();
328 if (!chunk) {
329 goto end;
330 }
331
332 LTTNG_OPTIONAL_SET(&chunk->id, chunk_id);
333 LTTNG_OPTIONAL_SET(&chunk->timestamp_creation, chunk_creation_time);
334 if (chunk_id != 0) {
335 chunk->name = generate_chunk_name(chunk_id,
336 chunk_creation_time, NULL);
337 if (!chunk->name) {
338 ERR("Failed to allocate trace chunk name storage");
339 goto error;
340 }
341 }
342 if (path) {
343 chunk->path = strdup(path);
344 if (!chunk->path) {
345 goto error;
346 }
347 } else {
348 if (chunk->name) {
349 chunk->path = strdup(chunk->name);
350 if (!chunk->path) {
351 goto error;
352 }
353 }
354 }
355
356 DBG("Chunk name set to \"%s\"", chunk->name ? : "(none)");
357 end:
358 return chunk;
359 error:
360 lttng_trace_chunk_put(chunk);
361 return NULL;
362 }
363
364 LTTNG_HIDDEN
365 void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk *chunk,
366 struct fd_tracker *fd_tracker)
367 {
368 assert(!chunk->session_output_directory);
369 assert(!chunk->chunk_directory);
370 assert(lttng_dynamic_pointer_array_get_count(&chunk->files) == 0);
371 chunk->fd_tracker = fd_tracker;
372 }
373
374 LTTNG_HIDDEN
375 struct lttng_trace_chunk *lttng_trace_chunk_copy(
376 struct lttng_trace_chunk *source_chunk)
377 {
378 struct lttng_trace_chunk *new_chunk = lttng_trace_chunk_allocate();
379
380 if (!new_chunk) {
381 goto end;
382 }
383
384 pthread_mutex_lock(&source_chunk->lock);
385 /*
386 * A new chunk is always a user; it shall create no new trace
387 * subdirectories.
388 */
389 new_chunk->mode = (typeof(new_chunk->mode)) {
390 .is_set = true,
391 .value = TRACE_CHUNK_MODE_USER,
392 };
393 /*
394 * top_level_directories is not copied as it is never used
395 * by _user_ mode chunks.
396 */
397 /* The new chunk is not part of a registry (yet, at least). */
398 new_chunk->in_registry_element = false;
399 new_chunk->name_overridden = source_chunk->name_overridden;
400 if (source_chunk->name) {
401 new_chunk->name = strdup(source_chunk->name);
402 if (!new_chunk->name) {
403 ERR("Failed to copy source trace chunk name in %s()",
404 __FUNCTION__);
405 goto error_unlock;
406 }
407 }
408 if (source_chunk->path) {
409 new_chunk->path = strdup(source_chunk->path);
410 if (!new_chunk->path) {
411 ERR("Failed to copy source trace chunk path in %s()",
412 __FUNCTION__);
413 }
414 }
415 new_chunk->id = source_chunk->id;
416 new_chunk->timestamp_creation = source_chunk->timestamp_creation;
417 new_chunk->timestamp_close = source_chunk->timestamp_close;
418 new_chunk->credentials = source_chunk->credentials;
419 if (source_chunk->session_output_directory) {
420 const bool reference_acquired = lttng_directory_handle_get(
421 source_chunk->session_output_directory);
422
423 assert(reference_acquired);
424 new_chunk->session_output_directory =
425 source_chunk->session_output_directory;
426 }
427 if (source_chunk->chunk_directory) {
428 const bool reference_acquired = lttng_directory_handle_get(
429 source_chunk->chunk_directory);
430
431 assert(reference_acquired);
432 new_chunk->chunk_directory = source_chunk->chunk_directory;
433 }
434 new_chunk->close_command = source_chunk->close_command;
435 new_chunk->fd_tracker = source_chunk->fd_tracker;
436 pthread_mutex_unlock(&source_chunk->lock);
437 end:
438 return new_chunk;
439 error_unlock:
440 pthread_mutex_unlock(&source_chunk->lock);
441 lttng_trace_chunk_put(new_chunk);
442 return NULL;
443 }
444
445 LTTNG_HIDDEN
446 enum lttng_trace_chunk_status lttng_trace_chunk_get_id(
447 struct lttng_trace_chunk *chunk, uint64_t *id)
448 {
449 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
450
451 pthread_mutex_lock(&chunk->lock);
452 if (chunk->id.is_set) {
453 *id = chunk->id.value;
454 } else {
455 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
456 }
457 pthread_mutex_unlock(&chunk->lock);
458 return status;
459 }
460
461 LTTNG_HIDDEN
462 enum lttng_trace_chunk_status lttng_trace_chunk_get_creation_timestamp(
463 struct lttng_trace_chunk *chunk, time_t *creation_ts)
464
465 {
466 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
467
468 pthread_mutex_lock(&chunk->lock);
469 if (chunk->timestamp_creation.is_set) {
470 *creation_ts = chunk->timestamp_creation.value;
471 } else {
472 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
473 }
474 pthread_mutex_unlock(&chunk->lock);
475 return status;
476 }
477
478 LTTNG_HIDDEN
479 enum lttng_trace_chunk_status lttng_trace_chunk_get_close_timestamp(
480 struct lttng_trace_chunk *chunk, time_t *close_ts)
481 {
482 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
483
484 pthread_mutex_lock(&chunk->lock);
485 if (chunk->timestamp_close.is_set) {
486 *close_ts = chunk->timestamp_close.value;
487 } else {
488 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
489 }
490 pthread_mutex_unlock(&chunk->lock);
491 return status;
492 }
493
494 LTTNG_HIDDEN
495 enum lttng_trace_chunk_status lttng_trace_chunk_set_close_timestamp(
496 struct lttng_trace_chunk *chunk, time_t close_ts)
497 {
498 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
499
500 pthread_mutex_lock(&chunk->lock);
501 if (!chunk->timestamp_creation.is_set) {
502 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
503 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
504 goto end;
505 }
506 if (chunk->timestamp_creation.value > close_ts) {
507 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
508 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
509 goto end;
510 }
511 LTTNG_OPTIONAL_SET(&chunk->timestamp_close, close_ts);
512 if (!chunk->name_overridden) {
513 free(chunk->name);
514 chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id),
515 LTTNG_OPTIONAL_GET(chunk->timestamp_creation),
516 &close_ts);
517 if (!chunk->name) {
518 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
519 }
520 }
521 end:
522 pthread_mutex_unlock(&chunk->lock);
523 return status;
524 }
525
526 LTTNG_HIDDEN
527 enum lttng_trace_chunk_status lttng_trace_chunk_get_name(
528 struct lttng_trace_chunk *chunk, const char **name,
529 bool *name_overridden)
530 {
531 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
532
533 pthread_mutex_lock(&chunk->lock);
534 if (name_overridden) {
535 *name_overridden = chunk->name_overridden;
536 }
537 if (!chunk->name) {
538 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
539 goto end;
540 }
541 *name = chunk->name;
542 end:
543 pthread_mutex_unlock(&chunk->lock);
544 return status;
545 }
546
547 LTTNG_HIDDEN
548 bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk *chunk)
549 {
550 bool name_overridden;
551
552 pthread_mutex_lock(&chunk->lock);
553 name_overridden = chunk->name_overridden;
554 pthread_mutex_unlock(&chunk->lock);
555 return name_overridden;
556 }
557
558 static
559 bool is_valid_chunk_name(const char *name)
560 {
561 size_t len;
562
563 if (!name) {
564 return false;
565 }
566
567 len = lttng_strnlen(name, LTTNG_NAME_MAX);
568 if (len == 0 || len == LTTNG_NAME_MAX) {
569 return false;
570 }
571
572 if (strchr(name, '/') || strchr(name, '.')) {
573 return false;
574 }
575
576 return true;
577 }
578
579 LTTNG_HIDDEN
580 enum lttng_trace_chunk_status lttng_trace_chunk_override_name(
581 struct lttng_trace_chunk *chunk, const char *name)
582
583 {
584 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
585 char *new_name, *new_path;
586
587 DBG("Override trace chunk name from %s to %s", chunk->name, name);
588 if (!is_valid_chunk_name(name)) {
589 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
590 name ? : "NULL");
591 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
592 goto end;
593 }
594
595 pthread_mutex_lock(&chunk->lock);
596 if (!chunk->id.is_set) {
597 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
598 name);
599 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
600 goto end_unlock;
601 }
602
603 new_name = strdup(name);
604 if (!new_name) {
605 ERR("Failed to allocate new trace chunk name");
606 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
607 goto end_unlock;
608 }
609 free(chunk->name);
610 chunk->name = new_name;
611
612 new_path = strdup(name);
613 if (!new_path) {
614 ERR("Failed to allocate new trace chunk path");
615 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
616 goto end_unlock;
617 }
618 free(chunk->path);
619 chunk->path = new_path;
620
621 chunk->name_overridden = true;
622 end_unlock:
623 pthread_mutex_unlock(&chunk->lock);
624 end:
625 return status;
626 }
627
628 static
629 enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock(
630 struct lttng_trace_chunk *chunk, const char *path)
631
632 {
633 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
634 struct lttng_directory_handle *rename_directory = NULL;
635 char *new_path, *old_path;
636 int ret;
637
638 if (chunk->name_overridden) {
639 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
640 goto end;
641 }
642
643 old_path = chunk->path;
644 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path, path);
645
646 if ((!old_path && !path) ||
647 (old_path && path && !strcmp(old_path, path))) {
648 goto end;
649 }
650 /*
651 * Use chunk name as path if NULL path is specified.
652 */
653 if (!path) {
654 path = chunk->name;
655 }
656
657 /* Renaming from "" to "" is not accepted. */
658 if (path[0] == '\0' && old_path[0] == '\0') {
659 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
660 goto end;
661 }
662
663 /*
664 * If a rename is performed on a chunk for which the chunk_directory
665 * is not set (yet), or the session_output_directory is not set
666 * (interacting with a relay daemon), there is no rename to perform.
667 */
668 if (!chunk->chunk_directory ||
669 !chunk->session_output_directory) {
670 goto skip_move;
671 }
672
673 if (old_path[0] != '\0' && path[0] != '\0') {
674 /* Rename chunk directory. */
675 ret = lttng_directory_handle_rename_as_user(
676 chunk->session_output_directory,
677 old_path,
678 chunk->session_output_directory,
679 path,
680 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
681 NULL :
682 &chunk->credentials.value.user);
683 if (ret) {
684 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
685 old_path, path);
686 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
687 goto end;
688 }
689 rename_directory = lttng_directory_handle_create_from_handle(
690 path,
691 chunk->session_output_directory);
692 if (!rename_directory) {
693 ERR("Failed to get handle to trace chunk rename directory");
694 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
695 goto end;
696 }
697
698 /* Release old handle. */
699 lttng_directory_handle_put(chunk->chunk_directory);
700 /*
701 * Transfer new handle reference to chunk as the current chunk
702 * handle.
703 */
704 chunk->chunk_directory = rename_directory;
705 rename_directory = NULL;
706 } else if (old_path[0] == '\0') {
707 size_t i, count = lttng_dynamic_pointer_array_get_count(
708 &chunk->top_level_directories);
709
710 ret = lttng_directory_handle_create_subdirectory_as_user(
711 chunk->session_output_directory,
712 path,
713 DIR_CREATION_MODE,
714 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
715 NULL :
716 &chunk->credentials.value.user);
717 if (ret) {
718 PERROR("Failed to create trace chunk rename directory \"%s\"",
719 path);
720 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
721 goto end;
722 }
723
724 rename_directory = lttng_directory_handle_create_from_handle(
725 path, chunk->session_output_directory);
726 if (!rename_directory) {
727 ERR("Failed to get handle to trace chunk rename directory");
728 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
729 goto end;
730 }
731
732 /* Move toplevel directories. */
733 for (i = 0; i < count; i++) {
734 const char *top_level_name =
735 lttng_dynamic_pointer_array_get_pointer(
736 &chunk->top_level_directories, i);
737
738 ret = lttng_directory_handle_rename_as_user(
739 chunk->chunk_directory,
740 top_level_name,
741 rename_directory,
742 top_level_name,
743 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
744 NULL :
745 &chunk->credentials.value.user);
746 if (ret) {
747 PERROR("Failed to move \"%s\" to trace chunk rename directory",
748 top_level_name);
749 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
750 goto end;
751 }
752 }
753 /* Release old handle. */
754 lttng_directory_handle_put(chunk->chunk_directory);
755 /*
756 * Transfer new handle reference to chunk as the current chunk
757 * handle.
758 */
759 chunk->chunk_directory = rename_directory;
760 rename_directory = NULL;
761 } else {
762 size_t i, count = lttng_dynamic_pointer_array_get_count(
763 &chunk->top_level_directories);
764 const bool reference_acquired = lttng_directory_handle_get(
765 chunk->session_output_directory);
766
767 assert(reference_acquired);
768 rename_directory = chunk->session_output_directory;
769
770 /* Move toplevel directories. */
771 for (i = 0; i < count; i++) {
772 const char *top_level_name =
773 lttng_dynamic_pointer_array_get_pointer(
774 &chunk->top_level_directories, i);
775
776 ret = lttng_directory_handle_rename_as_user(
777 chunk->chunk_directory,
778 top_level_name,
779 rename_directory,
780 top_level_name,
781 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
782 NULL :
783 &chunk->credentials.value.user);
784 if (ret) {
785 PERROR("Failed to move \"%s\" to trace chunk rename directory",
786 top_level_name);
787 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
788 goto end;
789 }
790 }
791 /* Release old handle. */
792 lttng_directory_handle_put(chunk->chunk_directory);
793 /*
794 * Transfer new handle reference to chunk as the current chunk
795 * handle.
796 */
797 chunk->chunk_directory = rename_directory;
798 rename_directory = NULL;
799
800 /* Remove old directory. */
801 status = lttng_directory_handle_remove_subdirectory(
802 chunk->session_output_directory,
803 old_path);
804 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
805 ERR("Error removing subdirectory '%s' file when deleting chunk",
806 old_path);
807 ret = -1;
808 goto end;
809 }
810 }
811
812 skip_move:
813 if (path) {
814 new_path = strdup(path);
815 if (!new_path) {
816 ERR("Failed to allocate new trace chunk path");
817 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
818 goto end;
819 }
820 } else {
821 new_path = NULL;
822 }
823 free(chunk->path);
824 chunk->path = new_path;
825 end:
826 lttng_directory_handle_put(rename_directory);
827 return status;
828 }
829
830 LTTNG_HIDDEN
831 enum lttng_trace_chunk_status lttng_trace_chunk_rename_path(
832 struct lttng_trace_chunk *chunk, const char *path)
833
834 {
835 enum lttng_trace_chunk_status status;
836
837 pthread_mutex_lock(&chunk->lock);
838 status = lttng_trace_chunk_rename_path_no_lock(chunk, path);
839 pthread_mutex_unlock(&chunk->lock);
840
841 return status;
842 }
843
844 LTTNG_HIDDEN
845 enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials(
846 struct lttng_trace_chunk *chunk,
847 struct lttng_credentials *credentials)
848 {
849 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
850
851 pthread_mutex_lock(&chunk->lock);
852 if (chunk->credentials.is_set) {
853 if (chunk->credentials.value.use_current_user) {
854 credentials->uid = geteuid();
855 credentials->gid = getegid();
856 } else {
857 *credentials = chunk->credentials.value.user;
858 }
859 } else {
860 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
861 }
862 pthread_mutex_unlock(&chunk->lock);
863 return status;
864 }
865
866 LTTNG_HIDDEN
867 enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials(
868 struct lttng_trace_chunk *chunk,
869 const struct lttng_credentials *user_credentials)
870 {
871 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
872 const struct chunk_credentials credentials = {
873 .user = *user_credentials,
874 .use_current_user = false,
875 };
876
877 pthread_mutex_lock(&chunk->lock);
878 if (chunk->credentials.is_set) {
879 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
880 goto end;
881 }
882 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
883 end:
884 pthread_mutex_unlock(&chunk->lock);
885 return status;
886 }
887
888 LTTNG_HIDDEN
889 enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials_current_user(
890 struct lttng_trace_chunk *chunk)
891 {
892 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
893 const struct chunk_credentials credentials = {
894 .use_current_user = true,
895 };
896
897 pthread_mutex_lock(&chunk->lock);
898 if (chunk->credentials.is_set) {
899 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
900 goto end;
901 }
902 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
903 end:
904 pthread_mutex_unlock(&chunk->lock);
905 return status;
906 }
907
908
909 LTTNG_HIDDEN
910 enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner(
911 struct lttng_trace_chunk *chunk,
912 struct lttng_directory_handle *session_output_directory)
913 {
914 int ret;
915 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
916 struct lttng_directory_handle *chunk_directory_handle = NULL;
917 bool reference_acquired;
918
919 pthread_mutex_lock(&chunk->lock);
920 if (chunk->mode.is_set) {
921 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
922 goto end;
923 }
924 if (!chunk->credentials.is_set) {
925 /*
926 * Fatal error, credentials must be set before a
927 * directory is created.
928 */
929 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
930 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
931 goto end;
932 }
933 if (chunk->path[0] != '\0') {
934 ret = lttng_directory_handle_create_subdirectory_as_user(
935 session_output_directory,
936 chunk->path,
937 DIR_CREATION_MODE,
938 !chunk->credentials.value.use_current_user ?
939 &chunk->credentials.value.user : NULL);
940 if (ret) {
941 PERROR("Failed to create chunk output directory \"%s\"",
942 chunk->path);
943 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
944 goto end;
945 }
946 chunk_directory_handle =
947 lttng_directory_handle_create_from_handle(
948 chunk->path,
949 session_output_directory);
950 if (!chunk_directory_handle) {
951 /* The function already logs on all error paths. */
952 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
953 goto end;
954 }
955 } else {
956 /*
957 * A nameless chunk does not need its own output directory.
958 * The session's output directory will be used.
959 */
960 const bool reference_acquired =
961 lttng_directory_handle_get(
962 session_output_directory);
963
964 assert(reference_acquired);
965 chunk_directory_handle = session_output_directory;
966 }
967 chunk->chunk_directory = chunk_directory_handle;
968 chunk_directory_handle = NULL;
969 reference_acquired = lttng_directory_handle_get(
970 session_output_directory);
971 assert(reference_acquired);
972 chunk->session_output_directory = session_output_directory;
973 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER);
974 end:
975 pthread_mutex_unlock(&chunk->lock);
976 return status;
977 }
978
979 LTTNG_HIDDEN
980 enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user(
981 struct lttng_trace_chunk *chunk,
982 struct lttng_directory_handle *chunk_directory)
983 {
984 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
985 bool reference_acquired;
986
987 pthread_mutex_lock(&chunk->lock);
988 if (chunk->mode.is_set) {
989 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
990 goto end;
991 }
992 if (!chunk->credentials.is_set) {
993 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
994 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
995 goto end;
996 }
997 reference_acquired = lttng_directory_handle_get(chunk_directory);
998 assert(reference_acquired);
999 chunk->chunk_directory = chunk_directory;
1000 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER);
1001 end:
1002 pthread_mutex_unlock(&chunk->lock);
1003 return status;
1004 }
1005
1006 LTTNG_HIDDEN
1007 enum lttng_trace_chunk_status
1008 lttng_trace_chunk_get_session_output_directory_handle(
1009 struct lttng_trace_chunk *chunk,
1010 struct lttng_directory_handle **handle)
1011 {
1012 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1013
1014 pthread_mutex_lock(&chunk->lock);
1015 if (!chunk->session_output_directory) {
1016 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1017 *handle = NULL;
1018 goto end;
1019 } else {
1020 const bool reference_acquired = lttng_directory_handle_get(
1021 chunk->session_output_directory);
1022
1023 assert(reference_acquired);
1024 *handle = chunk->session_output_directory;
1025 }
1026 end:
1027 pthread_mutex_unlock(&chunk->lock);
1028 return status;
1029 }
1030
1031 LTTNG_HIDDEN
1032 enum lttng_trace_chunk_status lttng_trace_chunk_borrow_chunk_directory_handle(
1033 struct lttng_trace_chunk *chunk,
1034 const struct lttng_directory_handle **handle)
1035 {
1036 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1037
1038 pthread_mutex_lock(&chunk->lock);
1039 if (!chunk->chunk_directory) {
1040 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1041 goto end;
1042 }
1043
1044 *handle = chunk->chunk_directory;
1045 end:
1046 pthread_mutex_unlock(&chunk->lock);
1047 return status;
1048 }
1049
1050 /* Add a top-level directory to the trace chunk if it was previously unknown. */
1051 static
1052 int add_top_level_directory_unique(struct lttng_trace_chunk *chunk,
1053 const char *new_path)
1054 {
1055 int ret = 0;
1056 bool found = false;
1057 size_t i, count = lttng_dynamic_pointer_array_get_count(
1058 &chunk->top_level_directories);
1059 const char *new_path_separator_pos = strchr(new_path, '/');
1060 const ptrdiff_t new_path_top_level_len = new_path_separator_pos ?
1061 new_path_separator_pos - new_path : strlen(new_path);
1062
1063 for (i = 0; i < count; i++) {
1064 const char *path = lttng_dynamic_pointer_array_get_pointer(
1065 &chunk->top_level_directories, i);
1066 const ptrdiff_t path_top_level_len = strlen(path);
1067
1068 if (path_top_level_len != new_path_top_level_len) {
1069 continue;
1070 }
1071 if (!strncmp(path, new_path, path_top_level_len)) {
1072 found = true;
1073 break;
1074 }
1075 }
1076
1077 if (!found) {
1078 char *copy = lttng_strndup(new_path, new_path_top_level_len);
1079
1080 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1081 new_path, chunk->name ? : "(unnamed)");
1082 if (!copy) {
1083 PERROR("Failed to copy path");
1084 ret = -1;
1085 goto end;
1086 }
1087 ret = lttng_dynamic_pointer_array_add_pointer(
1088 &chunk->top_level_directories, copy);
1089 if (ret) {
1090 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1091 free(copy);
1092 goto end;
1093 }
1094 }
1095 end:
1096 return ret;
1097 }
1098
1099 LTTNG_HIDDEN
1100 enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory(
1101 struct lttng_trace_chunk *chunk,
1102 const char *path)
1103 {
1104 int ret;
1105 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1106
1107 DBG("Creating trace chunk subdirectory \"%s\"", path);
1108 pthread_mutex_lock(&chunk->lock);
1109 if (!chunk->credentials.is_set) {
1110 /*
1111 * Fatal error, credentials must be set before a
1112 * directory is created.
1113 */
1114 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1115 path);
1116 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1117 goto end;
1118 }
1119 if (!chunk->mode.is_set ||
1120 chunk->mode.value != TRACE_CHUNK_MODE_OWNER) {
1121 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1122 path);
1123 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1124 goto end;
1125 }
1126 if (!chunk->chunk_directory) {
1127 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1128 path);
1129 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1130 goto end;
1131 }
1132 if (*path == '/') {
1133 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1134 path);
1135 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
1136 goto end;
1137 }
1138 ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
1139 chunk->chunk_directory, path,
1140 DIR_CREATION_MODE,
1141 chunk->credentials.value.use_current_user ?
1142 NULL : &chunk->credentials.value.user);
1143 if (ret) {
1144 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1145 path);
1146 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1147 goto end;
1148 }
1149 ret = add_top_level_directory_unique(chunk, path);
1150 if (ret) {
1151 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1152 goto end;
1153 }
1154 end:
1155 pthread_mutex_unlock(&chunk->lock);
1156 return status;
1157 }
1158
1159 /*
1160 * TODO: Implement O(1) lookup.
1161 */
1162 static
1163 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk *chunk,
1164 const char *path, size_t *index)
1165 {
1166 size_t i, count;
1167
1168 count = lttng_dynamic_pointer_array_get_count(&chunk->files);
1169 for (i = 0; i < count; i++) {
1170 const char *iter_path =
1171 lttng_dynamic_pointer_array_get_pointer(
1172 &chunk->files, i);
1173 if (!strcmp(iter_path, path)) {
1174 if (index) {
1175 *index = i;
1176 }
1177 return true;
1178 }
1179 }
1180 return false;
1181 }
1182
1183 static
1184 enum lttng_trace_chunk_status lttng_trace_chunk_add_file(
1185 struct lttng_trace_chunk *chunk,
1186 const char *path)
1187 {
1188 char *copy;
1189 int ret;
1190 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1191
1192 if (lttng_trace_chunk_find_file(chunk, path, NULL)) {
1193 return LTTNG_TRACE_CHUNK_STATUS_OK;
1194 }
1195 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1196 path, chunk->name ? : "(unnamed)");
1197 copy = strdup(path);
1198 if (!copy) {
1199 PERROR("Failed to copy path");
1200 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1201 goto end;
1202 }
1203 ret = lttng_dynamic_pointer_array_add_pointer(
1204 &chunk->files, copy);
1205 if (ret) {
1206 ERR("Allocation failure while adding file to a trace chunk");
1207 free(copy);
1208 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1209 goto end;
1210 }
1211 end:
1212 return status;
1213 }
1214
1215 static
1216 void lttng_trace_chunk_remove_file(
1217 struct lttng_trace_chunk *chunk,
1218 const char *path)
1219 {
1220 size_t index;
1221 bool found;
1222 int ret;
1223
1224 found = lttng_trace_chunk_find_file(chunk, path, &index);
1225 if (!found) {
1226 return;
1227 }
1228 ret = lttng_dynamic_pointer_array_remove_pointer(
1229 &chunk->files, index);
1230 assert(!ret);
1231 }
1232
1233 LTTNG_HIDDEN
1234 enum lttng_trace_chunk_status lttng_trace_chunk_open_file(
1235 struct lttng_trace_chunk *chunk, const char *file_path,
1236 int flags, mode_t mode, int *out_fd, bool expect_no_file)
1237 {
1238 int ret;
1239 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1240
1241 DBG("Opening trace chunk file \"%s\"", file_path);
1242 pthread_mutex_lock(&chunk->lock);
1243 if (!chunk->credentials.is_set) {
1244 /*
1245 * Fatal error, credentials must be set before a
1246 * file is created.
1247 */
1248 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1249 file_path);
1250 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1251 goto end;
1252 }
1253 if (!chunk->chunk_directory) {
1254 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1255 file_path);
1256 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1257 goto end;
1258 }
1259 status = lttng_trace_chunk_add_file(chunk, file_path);
1260 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1261 goto end;
1262 }
1263 ret = lttng_directory_handle_open_file_as_user(
1264 chunk->chunk_directory, file_path, flags, mode,
1265 chunk->credentials.value.use_current_user ?
1266 NULL : &chunk->credentials.value.user);
1267 if (ret < 0) {
1268 if (errno == ENOENT && expect_no_file) {
1269 status = LTTNG_TRACE_CHUNK_STATUS_NO_FILE;
1270 } else {
1271 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1272 file_path, flags, (int) mode);
1273 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1274 }
1275 lttng_trace_chunk_remove_file(chunk, file_path);
1276 goto end;
1277 }
1278 *out_fd = ret;
1279 end:
1280 pthread_mutex_unlock(&chunk->lock);
1281 return status;
1282 }
1283
1284 LTTNG_HIDDEN
1285 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk,
1286 const char *file_path)
1287 {
1288 int ret;
1289 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1290
1291 DBG("Unlinking trace chunk file \"%s\"", file_path);
1292 pthread_mutex_lock(&chunk->lock);
1293 if (!chunk->credentials.is_set) {
1294 /*
1295 * Fatal error, credentials must be set before a
1296 * file is unlinked.
1297 */
1298 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1299 file_path);
1300 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1301 goto end;
1302 }
1303 if (!chunk->chunk_directory) {
1304 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1305 file_path);
1306 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1307 goto end;
1308 }
1309 ret = lttng_directory_handle_unlink_file_as_user(
1310 chunk->chunk_directory, file_path,
1311 chunk->credentials.value.use_current_user ?
1312 NULL : &chunk->credentials.value.user);
1313 if (ret < 0) {
1314 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1315 goto end;
1316 }
1317 lttng_trace_chunk_remove_file(chunk, file_path);
1318 end:
1319 pthread_mutex_unlock(&chunk->lock);
1320 return status;
1321 }
1322
1323 LTTNG_HIDDEN
1324 int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk *chunk,
1325 const char *path)
1326 {
1327 int ret;
1328 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1329
1330 DBG("Recursively removing trace chunk directory \"%s\"", path);
1331 pthread_mutex_lock(&chunk->lock);
1332 if (!chunk->credentials.is_set) {
1333 /*
1334 * Fatal error, credentials must be set before a
1335 * directory is removed.
1336 */
1337 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1338 path);
1339 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1340 goto end;
1341 }
1342 if (!chunk->chunk_directory) {
1343 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1344 path);
1345 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1346 goto end;
1347 }
1348 ret = lttng_directory_handle_remove_subdirectory_recursive_as_user(
1349 chunk->chunk_directory, path,
1350 chunk->credentials.value.use_current_user ?
1351 NULL : &chunk->credentials.value.user,
1352 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
1353 if (ret < 0) {
1354 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1355 goto end;
1356 }
1357 end:
1358 pthread_mutex_unlock(&chunk->lock);
1359 return status;
1360 }
1361
1362 static
1363 int lttng_trace_chunk_move_to_completed_post_release(
1364 struct lttng_trace_chunk *trace_chunk)
1365 {
1366 int ret = 0;
1367 char *archived_chunk_name = NULL;
1368 const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id);
1369 const time_t creation_timestamp =
1370 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation);
1371 const time_t close_timestamp =
1372 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close);
1373 struct lttng_directory_handle *archived_chunks_directory = NULL;
1374 enum lttng_trace_chunk_status status;
1375
1376 if (!trace_chunk->mode.is_set ||
1377 trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER ||
1378 !trace_chunk->session_output_directory) {
1379 /*
1380 * This command doesn't need to run if the output is remote
1381 * or if the trace chunk is not owned by this process.
1382 */
1383 goto end;
1384 }
1385
1386 assert(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
1387 assert(!trace_chunk->name_overridden);
1388 assert(trace_chunk->path);
1389
1390 archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp,
1391 &close_timestamp);
1392 if (!archived_chunk_name) {
1393 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1394 ret = -1;
1395 goto end;
1396 }
1397
1398 ret = lttng_directory_handle_create_subdirectory_as_user(
1399 trace_chunk->session_output_directory,
1400 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
1401 DIR_CREATION_MODE,
1402 !trace_chunk->credentials.value.use_current_user ?
1403 &trace_chunk->credentials.value.user :
1404 NULL);
1405 if (ret) {
1406 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1407 "\" directory for archived trace chunks");
1408 goto end;
1409 }
1410
1411 archived_chunks_directory = lttng_directory_handle_create_from_handle(
1412 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
1413 trace_chunk->session_output_directory);
1414 if (!archived_chunks_directory) {
1415 PERROR("Failed to get handle to archived trace chunks directory");
1416 ret = -1;
1417 goto end;
1418 }
1419
1420 /*
1421 * Make sure chunk is renamed to old directory if not already done by
1422 * the creation of the next chunk. This happens if a rotation is
1423 * performed while tracing is stopped.
1424 */
1425 if (!trace_chunk->path || strcmp(trace_chunk->path,
1426 DEFAULT_CHUNK_TMP_OLD_DIRECTORY)) {
1427 status = lttng_trace_chunk_rename_path_no_lock(trace_chunk,
1428 DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
1429 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1430 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
1431 ret = -1;
1432 goto end;
1433 }
1434 }
1435
1436 ret = lttng_directory_handle_rename_as_user(
1437 trace_chunk->session_output_directory,
1438 trace_chunk->path,
1439 archived_chunks_directory,
1440 archived_chunk_name,
1441 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
1442 NULL :
1443 &trace_chunk->credentials.value.user);
1444 if (ret) {
1445 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1446 trace_chunk->path,
1447 archived_chunk_name);
1448 }
1449
1450 end:
1451 lttng_directory_handle_put(archived_chunks_directory);
1452 free(archived_chunk_name);
1453 return ret;
1454 }
1455
1456 static
1457 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk)
1458 {
1459 return 0;
1460 }
1461
1462 static
1463 int lttng_trace_chunk_delete_post_release_user(
1464 struct lttng_trace_chunk *trace_chunk)
1465 {
1466 int ret = 0;
1467
1468 DBG("Trace chunk \"delete\" close command post-release (User)");
1469
1470 /* Unlink all files. */
1471 while (lttng_dynamic_pointer_array_get_count(&trace_chunk->files) != 0) {
1472 enum lttng_trace_chunk_status status;
1473 const char *path;
1474
1475 /* Remove first. */
1476 path = lttng_dynamic_pointer_array_get_pointer(
1477 &trace_chunk->files, 0);
1478 DBG("Unlink file: %s", path);
1479 status = lttng_trace_chunk_unlink_file(trace_chunk, path);
1480 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1481 ERR("Error unlinking file '%s' when deleting chunk", path);
1482 ret = -1;
1483 goto end;
1484 }
1485 }
1486 end:
1487 return ret;
1488 }
1489
1490 static
1491 int lttng_trace_chunk_delete_post_release_owner(
1492 struct lttng_trace_chunk *trace_chunk)
1493 {
1494 enum lttng_trace_chunk_status status;
1495 size_t i, count;
1496 int ret = 0;
1497
1498 ret = lttng_trace_chunk_delete_post_release_user(trace_chunk);
1499 if (ret) {
1500 goto end;
1501 }
1502
1503 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1504
1505 assert(trace_chunk->session_output_directory);
1506 assert(trace_chunk->chunk_directory);
1507
1508 /* Remove empty directories. */
1509 count = lttng_dynamic_pointer_array_get_count(
1510 &trace_chunk->top_level_directories);
1511
1512 for (i = 0; i < count; i++) {
1513 const char *top_level_name =
1514 lttng_dynamic_pointer_array_get_pointer(
1515 &trace_chunk->top_level_directories, i);
1516
1517 status = lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk, top_level_name);
1518 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1519 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1520 top_level_name);
1521 ret = -1;
1522 break;
1523 }
1524 }
1525 if (!ret) {
1526 lttng_directory_handle_put(trace_chunk->chunk_directory);
1527 trace_chunk->chunk_directory = NULL;
1528
1529 if (trace_chunk->path && trace_chunk->path[0] != '\0') {
1530 status = lttng_directory_handle_remove_subdirectory(
1531 trace_chunk->session_output_directory,
1532 trace_chunk->path);
1533 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1534 ERR("Error removing subdirectory '%s' file when deleting chunk",
1535 trace_chunk->path);
1536 ret = -1;
1537 }
1538 }
1539 }
1540 free(trace_chunk->path);
1541 trace_chunk->path = NULL;
1542 end:
1543 return ret;
1544 }
1545
1546 /*
1547 * For local files, session and consumer daemons all run the delete hook. The
1548 * consumer daemons have the list of files to unlink, and technically the
1549 * session daemon is the owner of the chunk. Unlink all files owned by each
1550 * consumer daemon.
1551 */
1552 static
1553 int lttng_trace_chunk_delete_post_release(
1554 struct lttng_trace_chunk *trace_chunk)
1555 {
1556 if (!trace_chunk->chunk_directory) {
1557 return 0;
1558 }
1559
1560 if (trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER) {
1561 return lttng_trace_chunk_delete_post_release_owner(trace_chunk);
1562 } else {
1563 return lttng_trace_chunk_delete_post_release_user(trace_chunk);
1564 }
1565 }
1566
1567 LTTNG_HIDDEN
1568 enum lttng_trace_chunk_status lttng_trace_chunk_get_close_command(
1569 struct lttng_trace_chunk *chunk,
1570 enum lttng_trace_chunk_command_type *command_type)
1571 {
1572 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1573
1574 pthread_mutex_lock(&chunk->lock);
1575 if (chunk->close_command.is_set) {
1576 *command_type = chunk->close_command.value;
1577 status = LTTNG_TRACE_CHUNK_STATUS_OK;
1578 } else {
1579 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1580 }
1581 pthread_mutex_unlock(&chunk->lock);
1582 return status;
1583 }
1584
1585 LTTNG_HIDDEN
1586 enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
1587 struct lttng_trace_chunk *chunk,
1588 enum lttng_trace_chunk_command_type close_command)
1589 {
1590 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1591
1592 if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
1593 close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
1594 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
1595 goto end;
1596 }
1597
1598 pthread_mutex_lock(&chunk->lock);
1599 if (chunk->close_command.is_set) {
1600 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1601 close_command_names[chunk->close_command.value],
1602 close_command_names[close_command]);
1603 } else {
1604 DBG("Setting trace chunk close command to \"%s\"",
1605 close_command_names[close_command]);
1606 }
1607 /*
1608 * Unset close command for no-op for backward compatibility with relayd
1609 * 2.11.
1610 */
1611 if (close_command != LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) {
1612 LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
1613 } else {
1614 LTTNG_OPTIONAL_UNSET(&chunk->close_command);
1615 }
1616 pthread_mutex_unlock(&chunk->lock);
1617 end:
1618 return status;
1619 }
1620
1621 LTTNG_HIDDEN
1622 const char *lttng_trace_chunk_command_type_get_name(
1623 enum lttng_trace_chunk_command_type command)
1624 {
1625 switch (command) {
1626 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
1627 return "move to completed trace chunk folder";
1628 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
1629 return "no operation";
1630 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
1631 return "delete";
1632 default:
1633 abort();
1634 }
1635 }
1636
1637 LTTNG_HIDDEN
1638 bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
1639 {
1640 return urcu_ref_get_unless_zero(&chunk->ref);
1641 }
1642
1643 static
1644 void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
1645 {
1646 struct lttng_trace_chunk_registry_element *element =
1647 container_of(node, typeof(*element), rcu_node);
1648
1649 lttng_trace_chunk_fini(&element->chunk);
1650 free(element);
1651 }
1652
1653 static
1654 void lttng_trace_chunk_release(struct urcu_ref *ref)
1655 {
1656 struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk),
1657 ref);
1658
1659 if (chunk->close_command.is_set) {
1660 if (close_command_post_release_funcs[
1661 chunk->close_command.value](chunk)) {
1662 ERR("Trace chunk post-release command %s has failed.",
1663 close_command_names[chunk->close_command.value]);
1664 }
1665 }
1666
1667 if (chunk->in_registry_element) {
1668 struct lttng_trace_chunk_registry_element *element;
1669
1670 element = container_of(chunk, typeof(*element), chunk);
1671 if (element->registry) {
1672 rcu_read_lock();
1673 cds_lfht_del(element->registry->ht,
1674 &element->trace_chunk_registry_ht_node);
1675 rcu_read_unlock();
1676 call_rcu(&element->rcu_node,
1677 free_lttng_trace_chunk_registry_element);
1678 } else {
1679 /* Never published, can be free'd immediately. */
1680 free_lttng_trace_chunk_registry_element(
1681 &element->rcu_node);
1682 }
1683 } else {
1684 /* Not RCU-protected, free immediately. */
1685 lttng_trace_chunk_fini(chunk);
1686 free(chunk);
1687 }
1688 }
1689
1690 LTTNG_HIDDEN
1691 void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
1692 {
1693 if (!chunk) {
1694 return;
1695 }
1696 assert(chunk->ref.refcount);
1697 urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
1698 }
1699
1700 LTTNG_HIDDEN
1701 struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void)
1702 {
1703 struct lttng_trace_chunk_registry *registry;
1704
1705 registry = zmalloc(sizeof(*registry));
1706 if (!registry) {
1707 goto end;
1708 }
1709
1710 registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
1711 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
1712 if (!registry->ht) {
1713 goto error;
1714 }
1715 end:
1716 return registry;
1717 error:
1718 lttng_trace_chunk_registry_destroy(registry);
1719 return NULL;
1720 }
1721
1722 LTTNG_HIDDEN
1723 void lttng_trace_chunk_registry_destroy(
1724 struct lttng_trace_chunk_registry *registry)
1725 {
1726 if (!registry) {
1727 return;
1728 }
1729 if (registry->ht) {
1730 int ret = cds_lfht_destroy(registry->ht, NULL);
1731 assert(!ret);
1732 }
1733 free(registry);
1734 }
1735
1736 static
1737 struct lttng_trace_chunk_registry_element *
1738 lttng_trace_chunk_registry_element_create_from_chunk(
1739 struct lttng_trace_chunk *chunk, uint64_t session_id)
1740 {
1741 struct lttng_trace_chunk_registry_element *element =
1742 zmalloc(sizeof(*element));
1743
1744 if (!element) {
1745 goto end;
1746 }
1747 cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
1748 element->session_id = session_id;
1749
1750 element->chunk = *chunk;
1751 lttng_trace_chunk_init(&element->chunk);
1752 if (chunk->session_output_directory) {
1753 /* Transferred ownership. */
1754 element->chunk.session_output_directory =
1755 chunk->session_output_directory;
1756 chunk->session_output_directory = NULL;
1757 }
1758 if (chunk->chunk_directory) {
1759 /* Transferred ownership. */
1760 element->chunk.chunk_directory = chunk->chunk_directory;
1761 chunk->chunk_directory = NULL;
1762 }
1763 /*
1764 * The original chunk becomes invalid; the name and path attributes are
1765 * transferred to the new chunk instance.
1766 */
1767 chunk->name = NULL;
1768 chunk->path = NULL;
1769 element->chunk.fd_tracker = chunk->fd_tracker;
1770 element->chunk.in_registry_element = true;
1771 end:
1772 return element;
1773 }
1774
1775 LTTNG_HIDDEN
1776 struct lttng_trace_chunk *
1777 lttng_trace_chunk_registry_publish_chunk(
1778 struct lttng_trace_chunk_registry *registry,
1779 uint64_t session_id, struct lttng_trace_chunk *chunk)
1780 {
1781 struct lttng_trace_chunk_registry_element *element;
1782 unsigned long element_hash;
1783
1784 pthread_mutex_lock(&chunk->lock);
1785 element = lttng_trace_chunk_registry_element_create_from_chunk(chunk,
1786 session_id);
1787 pthread_mutex_unlock(&chunk->lock);
1788 if (!element) {
1789 goto end;
1790 }
1791 /*
1792 * chunk is now invalid, the only valid operation is a 'put' from the
1793 * caller.
1794 */
1795 chunk = NULL;
1796 element_hash = lttng_trace_chunk_registry_element_hash(element);
1797
1798 rcu_read_lock();
1799 while (1) {
1800 struct cds_lfht_node *published_node;
1801 struct lttng_trace_chunk *published_chunk;
1802 struct lttng_trace_chunk_registry_element *published_element;
1803
1804 published_node = cds_lfht_add_unique(registry->ht,
1805 element_hash,
1806 lttng_trace_chunk_registry_element_match,
1807 element,
1808 &element->trace_chunk_registry_ht_node);
1809 if (published_node == &element->trace_chunk_registry_ht_node) {
1810 /* Successfully published the new element. */
1811 element->registry = registry;
1812 /* Acquire a reference for the caller. */
1813 if (lttng_trace_chunk_get(&element->chunk)) {
1814 break;
1815 } else {
1816 /*
1817 * Another thread concurrently unpublished the
1818 * trace chunk. This is currently unexpected.
1819 *
1820 * Re-attempt to publish.
1821 */
1822 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1823 continue;
1824 }
1825 }
1826
1827 /*
1828 * An equivalent trace chunk was published before this trace
1829 * chunk. Attempt to acquire a reference to the one that was
1830 * already published and release the reference to the copy we
1831 * created if successful.
1832 */
1833 published_element = container_of(published_node,
1834 typeof(*published_element),
1835 trace_chunk_registry_ht_node);
1836 published_chunk = &published_element->chunk;
1837 if (lttng_trace_chunk_get(published_chunk)) {
1838 lttng_trace_chunk_put(&element->chunk);
1839 element = published_element;
1840 break;
1841 }
1842 /*
1843 * A reference to the previously published trace chunk could not
1844 * be acquired. Hence, retry to publish our copy of the trace
1845 * chunk.
1846 */
1847 }
1848 rcu_read_unlock();
1849 end:
1850 return element ? &element->chunk : NULL;
1851 }
1852
1853 /*
1854 * Note that the caller must be registered as an RCU thread.
1855 * However, it does not need to hold the RCU read lock. The RCU read lock is
1856 * acquired to perform the look-up in the registry's hash table and held until
1857 * after a reference to the "found" trace chunk is acquired.
1858 *
1859 * IOW, holding a reference guarantees the existence of the object for the
1860 * caller.
1861 */
1862 static
1863 struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
1864 const struct lttng_trace_chunk_registry *registry,
1865 uint64_t session_id, uint64_t *chunk_id)
1866 {
1867 const struct lttng_trace_chunk_registry_element target_element = {
1868 .chunk.id.is_set = !!chunk_id,
1869 .chunk.id.value = chunk_id ? *chunk_id : 0,
1870 .session_id = session_id,
1871 };
1872 const unsigned long element_hash =
1873 lttng_trace_chunk_registry_element_hash(
1874 &target_element);
1875 struct cds_lfht_node *published_node;
1876 struct lttng_trace_chunk_registry_element *published_element;
1877 struct lttng_trace_chunk *published_chunk = NULL;
1878 struct cds_lfht_iter iter;
1879
1880 rcu_read_lock();
1881 cds_lfht_lookup(registry->ht,
1882 element_hash,
1883 lttng_trace_chunk_registry_element_match,
1884 &target_element,
1885 &iter);
1886 published_node = cds_lfht_iter_get_node(&iter);
1887 if (!published_node) {
1888 goto end;
1889 }
1890
1891 published_element = container_of(published_node,
1892 typeof(*published_element),
1893 trace_chunk_registry_ht_node);
1894 if (lttng_trace_chunk_get(&published_element->chunk)) {
1895 published_chunk = &published_element->chunk;
1896 }
1897 end:
1898 rcu_read_unlock();
1899 return published_chunk;
1900 }
1901
1902 LTTNG_HIDDEN
1903 struct lttng_trace_chunk *
1904 lttng_trace_chunk_registry_find_chunk(
1905 const struct lttng_trace_chunk_registry *registry,
1906 uint64_t session_id, uint64_t chunk_id)
1907 {
1908 return _lttng_trace_chunk_registry_find_chunk(registry,
1909 session_id, &chunk_id);
1910 }
1911
1912 LTTNG_HIDDEN
1913 int lttng_trace_chunk_registry_chunk_exists(
1914 const struct lttng_trace_chunk_registry *registry,
1915 uint64_t session_id, uint64_t chunk_id, bool *chunk_exists)
1916 {
1917 int ret = 0;
1918 const struct lttng_trace_chunk_registry_element target_element = {
1919 .chunk.id.is_set = true,
1920 .chunk.id.value = chunk_id,
1921 .session_id = session_id,
1922 };
1923 const unsigned long element_hash =
1924 lttng_trace_chunk_registry_element_hash(
1925 &target_element);
1926 struct cds_lfht_node *published_node;
1927 struct cds_lfht_iter iter;
1928
1929 rcu_read_lock();
1930 cds_lfht_lookup(registry->ht,
1931 element_hash,
1932 lttng_trace_chunk_registry_element_match,
1933 &target_element,
1934 &iter);
1935 published_node = cds_lfht_iter_get_node(&iter);
1936 if (!published_node) {
1937 *chunk_exists = false;
1938 goto end;
1939 }
1940
1941 *chunk_exists = !cds_lfht_is_node_deleted(published_node);
1942 end:
1943 rcu_read_unlock();
1944 return ret;
1945 }
1946
1947 LTTNG_HIDDEN
1948 struct lttng_trace_chunk *
1949 lttng_trace_chunk_registry_find_anonymous_chunk(
1950 const struct lttng_trace_chunk_registry *registry,
1951 uint64_t session_id)
1952 {
1953 return _lttng_trace_chunk_registry_find_chunk(registry,
1954 session_id, NULL);
1955 }
1956
1957 LTTNG_HIDDEN
1958 unsigned int lttng_trace_chunk_registry_put_each_chunk(
1959 const struct lttng_trace_chunk_registry *registry)
1960 {
1961 struct cds_lfht_iter iter;
1962 struct lttng_trace_chunk_registry_element *chunk_element;
1963 unsigned int trace_chunks_left = 0;
1964
1965 DBG("Releasing trace chunk registry to all trace chunks");
1966 rcu_read_lock();
1967 cds_lfht_for_each_entry(registry->ht,
1968 &iter, chunk_element, trace_chunk_registry_ht_node) {
1969 const char *chunk_id_str = "none";
1970 char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)];
1971
1972 pthread_mutex_lock(&chunk_element->chunk.lock);
1973 if (chunk_element->chunk.id.is_set) {
1974 int fmt_ret;
1975
1976 fmt_ret = snprintf(chunk_id_buf, sizeof(chunk_id_buf),
1977 "%" PRIu64,
1978 chunk_element->chunk.id.value);
1979 if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) {
1980 chunk_id_str = "formatting error";
1981 } else {
1982 chunk_id_str = chunk_id_buf;
1983 }
1984 }
1985
1986 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
1987 "chunk_id = %s, name = \"%s\", status = %s",
1988 chunk_element->session_id,
1989 chunk_id_str,
1990 chunk_element->chunk.name ? : "none",
1991 chunk_element->chunk.close_command.is_set ?
1992 "open" : "closed");
1993 pthread_mutex_unlock(&chunk_element->chunk.lock);
1994 lttng_trace_chunk_put(&chunk_element->chunk);
1995 trace_chunks_left++;
1996 }
1997 rcu_read_unlock();
1998 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left,
1999 __FUNCTION__);
2000
2001 return trace_chunks_left;
2002 }
This page took 0.073293 seconds and 4 git commands to generate.