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