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