trace-chunk: implement no-op and delete release commands
[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 lttng_trace_chunk_borrow_chunk_directory_handle(
988 struct lttng_trace_chunk *chunk,
989 const struct lttng_directory_handle **handle)
990 {
991 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
992
993 pthread_mutex_lock(&chunk->lock);
994 if (!chunk->chunk_directory) {
995 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
996 goto end;
997 }
998
999 *handle = chunk->chunk_directory;
1000 end:
1001 pthread_mutex_unlock(&chunk->lock);
1002 return status;
1003 }
1004
1005 /* Add a top-level directory to the trace chunk if it was previously unknown. */
1006 static
1007 int add_top_level_directory_unique(struct lttng_trace_chunk *chunk,
1008 const char *new_path)
1009 {
1010 int ret = 0;
1011 bool found = false;
1012 size_t i, count = lttng_dynamic_pointer_array_get_count(
1013 &chunk->top_level_directories);
1014 const char *new_path_separator_pos = strchr(new_path, '/');
1015 const ptrdiff_t new_path_top_level_len = new_path_separator_pos ?
1016 new_path_separator_pos - new_path : strlen(new_path);
1017
1018 for (i = 0; i < count; i++) {
1019 const char *path = lttng_dynamic_pointer_array_get_pointer(
1020 &chunk->top_level_directories, i);
1021 const ptrdiff_t path_top_level_len = strlen(path);
1022
1023 if (path_top_level_len != new_path_top_level_len) {
1024 continue;
1025 }
1026 if (!strncmp(path, new_path, path_top_level_len)) {
1027 found = true;
1028 break;
1029 }
1030 }
1031
1032 if (!found) {
1033 char *copy = lttng_strndup(new_path, new_path_top_level_len);
1034
1035 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1036 new_path, chunk->name ? : "(unnamed)");
1037 if (!copy) {
1038 PERROR("Failed to copy path");
1039 ret = -1;
1040 goto end;
1041 }
1042 ret = lttng_dynamic_pointer_array_add_pointer(
1043 &chunk->top_level_directories, copy);
1044 if (ret) {
1045 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1046 free(copy);
1047 goto end;
1048 }
1049 }
1050 end:
1051 return ret;
1052 }
1053
1054 LTTNG_HIDDEN
1055 enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory(
1056 struct lttng_trace_chunk *chunk,
1057 const char *path)
1058 {
1059 int ret;
1060 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1061
1062 DBG("Creating trace chunk subdirectory \"%s\"", path);
1063 pthread_mutex_lock(&chunk->lock);
1064 if (!chunk->credentials.is_set) {
1065 /*
1066 * Fatal error, credentials must be set before a
1067 * directory is created.
1068 */
1069 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1070 path);
1071 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1072 goto end;
1073 }
1074 if (!chunk->mode.is_set ||
1075 chunk->mode.value != TRACE_CHUNK_MODE_OWNER) {
1076 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1077 path);
1078 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1079 goto end;
1080 }
1081 if (!chunk->chunk_directory) {
1082 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1083 path);
1084 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1085 goto end;
1086 }
1087 if (*path == '/') {
1088 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1089 path);
1090 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
1091 goto end;
1092 }
1093 ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
1094 chunk->chunk_directory, path,
1095 DIR_CREATION_MODE,
1096 chunk->credentials.value.use_current_user ?
1097 NULL : &chunk->credentials.value.user);
1098 if (ret) {
1099 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1100 path);
1101 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1102 goto end;
1103 }
1104 ret = add_top_level_directory_unique(chunk, path);
1105 if (ret) {
1106 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1107 goto end;
1108 }
1109 end:
1110 pthread_mutex_unlock(&chunk->lock);
1111 return status;
1112 }
1113
1114 /*
1115 * TODO: Implement O(1) lookup.
1116 */
1117 static
1118 bool lttng_trace_chunk_find_file(struct lttng_trace_chunk *chunk,
1119 const char *path, size_t *index)
1120 {
1121 size_t i, count;
1122
1123 count = lttng_dynamic_pointer_array_get_count(&chunk->files);
1124 for (i = 0; i < count; i++) {
1125 const char *iter_path =
1126 lttng_dynamic_pointer_array_get_pointer(
1127 &chunk->files, i);
1128 if (!strcmp(iter_path, path)) {
1129 if (index) {
1130 *index = i;
1131 }
1132 return true;
1133 }
1134 }
1135 return false;
1136 }
1137
1138 static
1139 enum lttng_trace_chunk_status lttng_trace_chunk_add_file(
1140 struct lttng_trace_chunk *chunk,
1141 const char *path)
1142 {
1143 char *copy;
1144 int ret;
1145 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1146
1147 if (lttng_trace_chunk_find_file(chunk, path, NULL)) {
1148 return LTTNG_TRACE_CHUNK_STATUS_OK;
1149 }
1150 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1151 path, chunk->name ? : "(unnamed)");
1152 copy = strdup(path);
1153 if (!copy) {
1154 PERROR("Failed to copy path");
1155 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1156 goto end;
1157 }
1158 ret = lttng_dynamic_pointer_array_add_pointer(
1159 &chunk->files, copy);
1160 if (ret) {
1161 ERR("Allocation failure while adding file to a trace chunk");
1162 free(copy);
1163 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1164 goto end;
1165 }
1166 end:
1167 return status;
1168 }
1169
1170 static
1171 void lttng_trace_chunk_remove_file(
1172 struct lttng_trace_chunk *chunk,
1173 const char *path)
1174 {
1175 size_t index;
1176 bool found;
1177 int ret;
1178
1179 found = lttng_trace_chunk_find_file(chunk, path, &index);
1180 if (!found) {
1181 return;
1182 }
1183 ret = lttng_dynamic_pointer_array_remove_pointer(
1184 &chunk->files, index);
1185 assert(!ret);
1186 }
1187
1188 LTTNG_HIDDEN
1189 enum lttng_trace_chunk_status lttng_trace_chunk_open_file(
1190 struct lttng_trace_chunk *chunk, const char *file_path,
1191 int flags, mode_t mode, int *out_fd, bool expect_no_file)
1192 {
1193 int ret;
1194 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1195
1196 DBG("Opening trace chunk file \"%s\"", file_path);
1197 pthread_mutex_lock(&chunk->lock);
1198 if (!chunk->credentials.is_set) {
1199 /*
1200 * Fatal error, credentials must be set before a
1201 * file is created.
1202 */
1203 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1204 file_path);
1205 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1206 goto end;
1207 }
1208 if (!chunk->chunk_directory) {
1209 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1210 file_path);
1211 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1212 goto end;
1213 }
1214 status = lttng_trace_chunk_add_file(chunk, file_path);
1215 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1216 goto end;
1217 }
1218 ret = lttng_directory_handle_open_file_as_user(
1219 chunk->chunk_directory, file_path, flags, mode,
1220 chunk->credentials.value.use_current_user ?
1221 NULL : &chunk->credentials.value.user);
1222 if (ret < 0) {
1223 if (errno == ENOENT && expect_no_file) {
1224 status = LTTNG_TRACE_CHUNK_STATUS_NO_FILE;
1225 } else {
1226 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
1227 file_path, flags, (int) mode);
1228 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1229 }
1230 lttng_trace_chunk_remove_file(chunk, file_path);
1231 goto end;
1232 }
1233 *out_fd = ret;
1234 end:
1235 pthread_mutex_unlock(&chunk->lock);
1236 return status;
1237 }
1238
1239 LTTNG_HIDDEN
1240 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk,
1241 const char *file_path)
1242 {
1243 int ret;
1244 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1245
1246 DBG("Unlinking trace chunk file \"%s\"", file_path);
1247 pthread_mutex_lock(&chunk->lock);
1248 if (!chunk->credentials.is_set) {
1249 /*
1250 * Fatal error, credentials must be set before a
1251 * file is unlinked.
1252 */
1253 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1254 file_path);
1255 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1256 goto end;
1257 }
1258 if (!chunk->chunk_directory) {
1259 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1260 file_path);
1261 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1262 goto end;
1263 }
1264 ret = lttng_directory_handle_unlink_file_as_user(
1265 chunk->chunk_directory, file_path,
1266 chunk->credentials.value.use_current_user ?
1267 NULL : &chunk->credentials.value.user);
1268 if (ret < 0) {
1269 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1270 goto end;
1271 }
1272 lttng_trace_chunk_remove_file(chunk, file_path);
1273 end:
1274 pthread_mutex_unlock(&chunk->lock);
1275 return status;
1276 }
1277
1278 LTTNG_HIDDEN
1279 int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk *chunk,
1280 const char *path)
1281 {
1282 int ret;
1283 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1284
1285 DBG("Recursively removing trace chunk directory \"%s\"", path);
1286 pthread_mutex_lock(&chunk->lock);
1287 if (!chunk->credentials.is_set) {
1288 /*
1289 * Fatal error, credentials must be set before a
1290 * directory is removed.
1291 */
1292 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1293 path);
1294 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1295 goto end;
1296 }
1297 if (!chunk->chunk_directory) {
1298 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1299 path);
1300 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1301 goto end;
1302 }
1303 ret = lttng_directory_handle_remove_subdirectory_recursive_as_user(
1304 chunk->chunk_directory, path,
1305 chunk->credentials.value.use_current_user ?
1306 NULL : &chunk->credentials.value.user,
1307 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
1308 if (ret < 0) {
1309 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1310 goto end;
1311 }
1312 end:
1313 pthread_mutex_unlock(&chunk->lock);
1314 return status;
1315 }
1316
1317 static
1318 int lttng_trace_chunk_move_to_completed_post_release(
1319 struct lttng_trace_chunk *trace_chunk)
1320 {
1321 int ret = 0;
1322 char *archived_chunk_name = NULL;
1323 const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id);
1324 const time_t creation_timestamp =
1325 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation);
1326 const time_t close_timestamp =
1327 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close);
1328 struct lttng_directory_handle *archived_chunks_directory = NULL;
1329 enum lttng_trace_chunk_status status;
1330
1331 if (!trace_chunk->mode.is_set ||
1332 trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER ||
1333 !trace_chunk->session_output_directory) {
1334 /*
1335 * This command doesn't need to run if the output is remote
1336 * or if the trace chunk is not owned by this process.
1337 */
1338 goto end;
1339 }
1340
1341 assert(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
1342 assert(!trace_chunk->name_overridden);
1343 assert(trace_chunk->path);
1344
1345 archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp,
1346 &close_timestamp);
1347 if (!archived_chunk_name) {
1348 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
1349 ret = -1;
1350 goto end;
1351 }
1352
1353 ret = lttng_directory_handle_create_subdirectory_as_user(
1354 trace_chunk->session_output_directory,
1355 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
1356 DIR_CREATION_MODE,
1357 !trace_chunk->credentials.value.use_current_user ?
1358 &trace_chunk->credentials.value.user :
1359 NULL);
1360 if (ret) {
1361 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1362 "\" directory for archived trace chunks");
1363 goto end;
1364 }
1365
1366 archived_chunks_directory = lttng_directory_handle_create_from_handle(
1367 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
1368 trace_chunk->session_output_directory);
1369 if (!archived_chunks_directory) {
1370 PERROR("Failed to get handle to archived trace chunks directory");
1371 ret = -1;
1372 goto end;
1373 }
1374
1375 /*
1376 * Make sure chunk is renamed to old directory if not already done by
1377 * the creation of the next chunk. This happens if a rotation is
1378 * performed while tracing is stopped.
1379 */
1380 if (!trace_chunk->path || strcmp(trace_chunk->path,
1381 DEFAULT_CHUNK_TMP_OLD_DIRECTORY)) {
1382 status = lttng_trace_chunk_rename_path_no_lock(trace_chunk,
1383 DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
1384 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1385 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
1386 ret = -1;
1387 goto end;
1388 }
1389 }
1390
1391 ret = lttng_directory_handle_rename_as_user(
1392 trace_chunk->session_output_directory,
1393 trace_chunk->path,
1394 archived_chunks_directory,
1395 archived_chunk_name,
1396 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
1397 NULL :
1398 &trace_chunk->credentials.value.user);
1399 if (ret) {
1400 PERROR("Failed to rename folder \"%s\" to \"%s\"",
1401 trace_chunk->path,
1402 archived_chunk_name);
1403 }
1404
1405 end:
1406 lttng_directory_handle_put(archived_chunks_directory);
1407 free(archived_chunk_name);
1408 return ret;
1409 }
1410
1411 static
1412 int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk)
1413 {
1414 return 0;
1415 }
1416
1417 static
1418 int lttng_trace_chunk_delete_post_release_user(
1419 struct lttng_trace_chunk *trace_chunk)
1420 {
1421 int ret = 0;
1422
1423 DBG("Trace chunk \"delete\" close command post-release (User)");
1424
1425 /* Unlink all files. */
1426 while (lttng_dynamic_pointer_array_get_count(&trace_chunk->files) != 0) {
1427 enum lttng_trace_chunk_status status;
1428 const char *path;
1429
1430 /* Remove first. */
1431 path = lttng_dynamic_pointer_array_get_pointer(
1432 &trace_chunk->files, 0);
1433 DBG("Unlink file: %s", path);
1434 status = lttng_trace_chunk_unlink_file(trace_chunk, path);
1435 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1436 ERR("Error unlinking file '%s' when deleting chunk", path);
1437 ret = -1;
1438 goto end;
1439 }
1440 }
1441 end:
1442 return ret;
1443 }
1444
1445 static
1446 int lttng_trace_chunk_delete_post_release_owner(
1447 struct lttng_trace_chunk *trace_chunk)
1448 {
1449 enum lttng_trace_chunk_status status;
1450 size_t i, count;
1451 int ret = 0;
1452
1453 ret = lttng_trace_chunk_delete_post_release_user(trace_chunk);
1454 if (ret) {
1455 goto end;
1456 }
1457
1458 DBG("Trace chunk \"delete\" close command post-release (Owner)");
1459
1460 assert(trace_chunk->session_output_directory);
1461 assert(trace_chunk->chunk_directory);
1462
1463 /* Remove empty directories. */
1464 count = lttng_dynamic_pointer_array_get_count(
1465 &trace_chunk->top_level_directories);
1466
1467 for (i = 0; i < count; i++) {
1468 const char *top_level_name =
1469 lttng_dynamic_pointer_array_get_pointer(
1470 &trace_chunk->top_level_directories, i);
1471
1472 status = lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk, top_level_name);
1473 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1474 ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
1475 top_level_name);
1476 ret = -1;
1477 break;
1478 }
1479 }
1480 if (!ret) {
1481 lttng_directory_handle_put(trace_chunk->chunk_directory);
1482 trace_chunk->chunk_directory = NULL;
1483
1484 if (trace_chunk->path && trace_chunk->path[0] != '\0') {
1485 status = lttng_directory_handle_remove_subdirectory(
1486 trace_chunk->session_output_directory,
1487 trace_chunk->path);
1488 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1489 ERR("Error removing subdirectory '%s' file when deleting chunk",
1490 trace_chunk->path);
1491 ret = -1;
1492 }
1493 }
1494 }
1495 free(trace_chunk->path);
1496 trace_chunk->path = NULL;
1497 end:
1498 return ret;
1499 }
1500
1501 /*
1502 * For local files, session and consumer daemons all run the delete hook. The
1503 * consumer daemons have the list of files to unlink, and technically the
1504 * session daemon is the owner of the chunk. Unlink all files owned by each
1505 * consumer daemon.
1506 */
1507 static
1508 int lttng_trace_chunk_delete_post_release(
1509 struct lttng_trace_chunk *trace_chunk)
1510 {
1511 if (!trace_chunk->chunk_directory) {
1512 return 0;
1513 }
1514
1515 if (trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER) {
1516 return lttng_trace_chunk_delete_post_release_owner(trace_chunk);
1517 } else {
1518 return lttng_trace_chunk_delete_post_release_user(trace_chunk);
1519 }
1520 }
1521
1522 LTTNG_HIDDEN
1523 enum lttng_trace_chunk_status lttng_trace_chunk_get_close_command(
1524 struct lttng_trace_chunk *chunk,
1525 enum lttng_trace_chunk_command_type *command_type)
1526 {
1527 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1528
1529 pthread_mutex_lock(&chunk->lock);
1530 if (chunk->close_command.is_set) {
1531 *command_type = chunk->close_command.value;
1532 status = LTTNG_TRACE_CHUNK_STATUS_OK;
1533 } else {
1534 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1535 }
1536 pthread_mutex_unlock(&chunk->lock);
1537 return status;
1538 }
1539
1540 LTTNG_HIDDEN
1541 enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
1542 struct lttng_trace_chunk *chunk,
1543 enum lttng_trace_chunk_command_type close_command)
1544 {
1545 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1546
1547 if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
1548 close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
1549 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
1550 goto end;
1551 }
1552
1553 pthread_mutex_lock(&chunk->lock);
1554 if (chunk->close_command.is_set) {
1555 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1556 close_command_names[chunk->close_command.value],
1557 close_command_names[close_command]);
1558 } else {
1559 DBG("Setting trace chunk close command to \"%s\"",
1560 close_command_names[close_command]);
1561 }
1562 /*
1563 * Unset close command for no-op for backward compatibility with relayd
1564 * 2.11.
1565 */
1566 if (close_command != LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) {
1567 LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
1568 } else {
1569 LTTNG_OPTIONAL_UNSET(&chunk->close_command);
1570 }
1571 pthread_mutex_unlock(&chunk->lock);
1572 end:
1573 return status;
1574 }
1575
1576 LTTNG_HIDDEN
1577 const char *lttng_trace_chunk_command_type_get_name(
1578 enum lttng_trace_chunk_command_type command)
1579 {
1580 switch (command) {
1581 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
1582 return "move to completed trace chunk folder";
1583 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
1584 return "no operation";
1585 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
1586 return "delete";
1587 default:
1588 abort();
1589 }
1590 }
1591
1592 LTTNG_HIDDEN
1593 bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
1594 {
1595 return urcu_ref_get_unless_zero(&chunk->ref);
1596 }
1597
1598 static
1599 void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
1600 {
1601 struct lttng_trace_chunk_registry_element *element =
1602 container_of(node, typeof(*element), rcu_node);
1603
1604 lttng_trace_chunk_fini(&element->chunk);
1605 free(element);
1606 }
1607
1608 static
1609 void lttng_trace_chunk_release(struct urcu_ref *ref)
1610 {
1611 struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk),
1612 ref);
1613
1614 if (chunk->close_command.is_set) {
1615 if (close_command_post_release_funcs[
1616 chunk->close_command.value](chunk)) {
1617 ERR("Trace chunk post-release command %s has failed.",
1618 close_command_names[chunk->close_command.value]);
1619 }
1620 }
1621
1622 if (chunk->in_registry_element) {
1623 struct lttng_trace_chunk_registry_element *element;
1624
1625 element = container_of(chunk, typeof(*element), chunk);
1626 if (element->registry) {
1627 rcu_read_lock();
1628 cds_lfht_del(element->registry->ht,
1629 &element->trace_chunk_registry_ht_node);
1630 rcu_read_unlock();
1631 call_rcu(&element->rcu_node,
1632 free_lttng_trace_chunk_registry_element);
1633 } else {
1634 /* Never published, can be free'd immediately. */
1635 free_lttng_trace_chunk_registry_element(
1636 &element->rcu_node);
1637 }
1638 } else {
1639 /* Not RCU-protected, free immediately. */
1640 lttng_trace_chunk_fini(chunk);
1641 free(chunk);
1642 }
1643 }
1644
1645 LTTNG_HIDDEN
1646 void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
1647 {
1648 if (!chunk) {
1649 return;
1650 }
1651 assert(chunk->ref.refcount);
1652 urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
1653 }
1654
1655 LTTNG_HIDDEN
1656 struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void)
1657 {
1658 struct lttng_trace_chunk_registry *registry;
1659
1660 registry = zmalloc(sizeof(*registry));
1661 if (!registry) {
1662 goto end;
1663 }
1664
1665 registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
1666 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
1667 if (!registry->ht) {
1668 goto error;
1669 }
1670 end:
1671 return registry;
1672 error:
1673 lttng_trace_chunk_registry_destroy(registry);
1674 return NULL;
1675 }
1676
1677 LTTNG_HIDDEN
1678 void lttng_trace_chunk_registry_destroy(
1679 struct lttng_trace_chunk_registry *registry)
1680 {
1681 if (!registry) {
1682 return;
1683 }
1684 if (registry->ht) {
1685 int ret = cds_lfht_destroy(registry->ht, NULL);
1686 assert(!ret);
1687 }
1688 free(registry);
1689 }
1690
1691 static
1692 struct lttng_trace_chunk_registry_element *
1693 lttng_trace_chunk_registry_element_create_from_chunk(
1694 struct lttng_trace_chunk *chunk, uint64_t session_id)
1695 {
1696 struct lttng_trace_chunk_registry_element *element =
1697 zmalloc(sizeof(*element));
1698
1699 if (!element) {
1700 goto end;
1701 }
1702 cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
1703 element->session_id = session_id;
1704
1705 element->chunk = *chunk;
1706 lttng_trace_chunk_init(&element->chunk);
1707 if (chunk->session_output_directory) {
1708 /* Transferred ownership. */
1709 element->chunk.session_output_directory =
1710 chunk->session_output_directory;
1711 chunk->session_output_directory = NULL;
1712 }
1713 if (chunk->chunk_directory) {
1714 /* Transferred ownership. */
1715 element->chunk.chunk_directory = chunk->chunk_directory;
1716 chunk->chunk_directory = NULL;
1717 }
1718 /*
1719 * The original chunk becomes invalid; the name and path attributes are
1720 * transferred to the new chunk instance.
1721 */
1722 chunk->name = NULL;
1723 chunk->path = NULL;
1724 element->chunk.in_registry_element = true;
1725 end:
1726 return element;
1727 }
1728
1729 LTTNG_HIDDEN
1730 struct lttng_trace_chunk *
1731 lttng_trace_chunk_registry_publish_chunk(
1732 struct lttng_trace_chunk_registry *registry,
1733 uint64_t session_id, struct lttng_trace_chunk *chunk)
1734 {
1735 struct lttng_trace_chunk_registry_element *element;
1736 unsigned long element_hash;
1737
1738 pthread_mutex_lock(&chunk->lock);
1739 element = lttng_trace_chunk_registry_element_create_from_chunk(chunk,
1740 session_id);
1741 pthread_mutex_unlock(&chunk->lock);
1742 if (!element) {
1743 goto end;
1744 }
1745 /*
1746 * chunk is now invalid, the only valid operation is a 'put' from the
1747 * caller.
1748 */
1749 chunk = NULL;
1750 element_hash = lttng_trace_chunk_registry_element_hash(element);
1751
1752 rcu_read_lock();
1753 while (1) {
1754 struct cds_lfht_node *published_node;
1755 struct lttng_trace_chunk *published_chunk;
1756 struct lttng_trace_chunk_registry_element *published_element;
1757
1758 published_node = cds_lfht_add_unique(registry->ht,
1759 element_hash,
1760 lttng_trace_chunk_registry_element_match,
1761 element,
1762 &element->trace_chunk_registry_ht_node);
1763 if (published_node == &element->trace_chunk_registry_ht_node) {
1764 /* Successfully published the new element. */
1765 element->registry = registry;
1766 /* Acquire a reference for the caller. */
1767 if (lttng_trace_chunk_get(&element->chunk)) {
1768 break;
1769 } else {
1770 /*
1771 * Another thread concurrently unpublished the
1772 * trace chunk. This is currently unexpected.
1773 *
1774 * Re-attempt to publish.
1775 */
1776 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1777 continue;
1778 }
1779 }
1780
1781 /*
1782 * An equivalent trace chunk was published before this trace
1783 * chunk. Attempt to acquire a reference to the one that was
1784 * already published and release the reference to the copy we
1785 * created if successful.
1786 */
1787 published_element = container_of(published_node,
1788 typeof(*published_element),
1789 trace_chunk_registry_ht_node);
1790 published_chunk = &published_element->chunk;
1791 if (lttng_trace_chunk_get(published_chunk)) {
1792 lttng_trace_chunk_put(&element->chunk);
1793 element = published_element;
1794 break;
1795 }
1796 /*
1797 * A reference to the previously published trace chunk could not
1798 * be acquired. Hence, retry to publish our copy of the trace
1799 * chunk.
1800 */
1801 }
1802 rcu_read_unlock();
1803 end:
1804 return element ? &element->chunk : NULL;
1805 }
1806
1807 /*
1808 * Note that the caller must be registered as an RCU thread.
1809 * However, it does not need to hold the RCU read lock. The RCU read lock is
1810 * acquired to perform the look-up in the registry's hash table and held until
1811 * after a reference to the "found" trace chunk is acquired.
1812 *
1813 * IOW, holding a reference guarantees the existence of the object for the
1814 * caller.
1815 */
1816 static
1817 struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
1818 const struct lttng_trace_chunk_registry *registry,
1819 uint64_t session_id, uint64_t *chunk_id)
1820 {
1821 const struct lttng_trace_chunk_registry_element target_element = {
1822 .chunk.id.is_set = !!chunk_id,
1823 .chunk.id.value = chunk_id ? *chunk_id : 0,
1824 .session_id = session_id,
1825 };
1826 const unsigned long element_hash =
1827 lttng_trace_chunk_registry_element_hash(
1828 &target_element);
1829 struct cds_lfht_node *published_node;
1830 struct lttng_trace_chunk_registry_element *published_element;
1831 struct lttng_trace_chunk *published_chunk = NULL;
1832 struct cds_lfht_iter iter;
1833
1834 rcu_read_lock();
1835 cds_lfht_lookup(registry->ht,
1836 element_hash,
1837 lttng_trace_chunk_registry_element_match,
1838 &target_element,
1839 &iter);
1840 published_node = cds_lfht_iter_get_node(&iter);
1841 if (!published_node) {
1842 goto end;
1843 }
1844
1845 published_element = container_of(published_node,
1846 typeof(*published_element),
1847 trace_chunk_registry_ht_node);
1848 if (lttng_trace_chunk_get(&published_element->chunk)) {
1849 published_chunk = &published_element->chunk;
1850 }
1851 end:
1852 rcu_read_unlock();
1853 return published_chunk;
1854 }
1855
1856 LTTNG_HIDDEN
1857 struct lttng_trace_chunk *
1858 lttng_trace_chunk_registry_find_chunk(
1859 const struct lttng_trace_chunk_registry *registry,
1860 uint64_t session_id, uint64_t chunk_id)
1861 {
1862 return _lttng_trace_chunk_registry_find_chunk(registry,
1863 session_id, &chunk_id);
1864 }
1865
1866 LTTNG_HIDDEN
1867 int lttng_trace_chunk_registry_chunk_exists(
1868 const struct lttng_trace_chunk_registry *registry,
1869 uint64_t session_id, uint64_t chunk_id, bool *chunk_exists)
1870 {
1871 int ret = 0;
1872 const struct lttng_trace_chunk_registry_element target_element = {
1873 .chunk.id.is_set = true,
1874 .chunk.id.value = chunk_id,
1875 .session_id = session_id,
1876 };
1877 const unsigned long element_hash =
1878 lttng_trace_chunk_registry_element_hash(
1879 &target_element);
1880 struct cds_lfht_node *published_node;
1881 struct cds_lfht_iter iter;
1882
1883 rcu_read_lock();
1884 cds_lfht_lookup(registry->ht,
1885 element_hash,
1886 lttng_trace_chunk_registry_element_match,
1887 &target_element,
1888 &iter);
1889 published_node = cds_lfht_iter_get_node(&iter);
1890 if (!published_node) {
1891 *chunk_exists = false;
1892 goto end;
1893 }
1894
1895 *chunk_exists = !cds_lfht_is_node_deleted(published_node);
1896 end:
1897 rcu_read_unlock();
1898 return ret;
1899 }
1900
1901 LTTNG_HIDDEN
1902 struct lttng_trace_chunk *
1903 lttng_trace_chunk_registry_find_anonymous_chunk(
1904 const struct lttng_trace_chunk_registry *registry,
1905 uint64_t session_id)
1906 {
1907 return _lttng_trace_chunk_registry_find_chunk(registry,
1908 session_id, NULL);
1909 }
1910
1911 LTTNG_HIDDEN
1912 unsigned int lttng_trace_chunk_registry_put_each_chunk(
1913 const struct lttng_trace_chunk_registry *registry)
1914 {
1915 struct cds_lfht_iter iter;
1916 struct lttng_trace_chunk_registry_element *chunk_element;
1917 unsigned int trace_chunks_left = 0;
1918
1919 DBG("Releasing trace chunk registry to all trace chunks");
1920 rcu_read_lock();
1921 cds_lfht_for_each_entry(registry->ht,
1922 &iter, chunk_element, trace_chunk_registry_ht_node) {
1923 const char *chunk_id_str = "none";
1924 char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)];
1925
1926 pthread_mutex_lock(&chunk_element->chunk.lock);
1927 if (chunk_element->chunk.id.is_set) {
1928 int fmt_ret;
1929
1930 fmt_ret = snprintf(chunk_id_buf, sizeof(chunk_id_buf),
1931 "%" PRIu64,
1932 chunk_element->chunk.id.value);
1933 if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) {
1934 chunk_id_str = "formatting error";
1935 } else {
1936 chunk_id_str = chunk_id_buf;
1937 }
1938 }
1939
1940 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
1941 "chunk_id = %s, name = \"%s\", status = %s",
1942 chunk_element->session_id,
1943 chunk_id_str,
1944 chunk_element->chunk.name ? : "none",
1945 chunk_element->chunk.close_command.is_set ?
1946 "open" : "closed");
1947 pthread_mutex_unlock(&chunk_element->chunk.lock);
1948 lttng_trace_chunk_put(&chunk_element->chunk);
1949 trace_chunks_left++;
1950 }
1951 rcu_read_unlock();
1952 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left,
1953 __FUNCTION__);
1954
1955 return trace_chunks_left;
1956 }
This page took 0.122857 seconds and 5 git commands to generate.