Add the trace chunk and trace chunk registry interfaces
[lttng-tools.git] / src / common / trace-chunk.c
1 /*
2 * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, version 2.1 only,
6 * as published by the Free Software Foundation.
7 *
8 * This library is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18 #include <lttng/constant.h>
19 #include <common/string-utils/format.h>
20 #include <common/trace-chunk.h>
21 #include <common/trace-chunk-registry.h>
22 #include <common/hashtable/utils.h>
23 #include <common/hashtable/hashtable.h>
24 #include <common/error.h>
25 #include <common/utils.h>
26 #include <common/time.h>
27 #include <common/optional.h>
28 #include <common/compat/directory-handle.h>
29 #include <common/credentials.h>
30 #include <common/defaults.h>
31 #include <common/dynamic-array.h>
32
33 #include <urcu/ref.h>
34 #include <urcu/rculfhash.h>
35 #include <sys/stat.h>
36 #include <inttypes.h>
37 #include <pthread.h>
38 #include <stdio.h>
39
40 /*
41 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
42 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
43 */
44 #define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
45 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
46
47 enum trace_chunk_mode {
48 TRACE_CHUNK_MODE_USER,
49 TRACE_CHUNK_MODE_OWNER,
50 };
51
52 /*
53 * Callback to invoke on release of a trace chunk. Note that there is no
54 * need to 'lock' the trace chunk during the execution of these callbacks
55 * since only one thread may access a chunk during its destruction (the last
56 * to release its reference to the chunk).
57 */
58 typedef void (*chunk_close_command)(struct lttng_trace_chunk *trace_chunk);
59
60 /* Move a completed trace chunk to the 'completed' trace archive folder. */
61 static
62 void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk);
63
64 struct chunk_credentials {
65 bool use_current_user;
66 struct lttng_credentials user;
67 };
68
69 struct lttng_trace_chunk {
70 pthread_mutex_t lock;
71 struct urcu_ref ref;
72 LTTNG_OPTIONAL(enum trace_chunk_mode) mode;
73 /*
74 * First-level directories created within the trace chunk.
75 * Elements are of type 'char *'.
76 */
77 struct lttng_dynamic_pointer_array top_level_directories;
78 /* Is contained within an lttng_trace_chunk_registry_element? */
79 bool in_registry_element;
80 bool name_overriden;
81 char *name;
82 /* An unset id means the chunk is anonymous. */
83 LTTNG_OPTIONAL(uint64_t) id;
84 LTTNG_OPTIONAL(time_t) timestamp_creation;
85 LTTNG_OPTIONAL(time_t) timestamp_close;
86 LTTNG_OPTIONAL(struct chunk_credentials) credentials;
87 LTTNG_OPTIONAL(struct lttng_directory_handle) session_output_directory;
88 LTTNG_OPTIONAL(struct lttng_directory_handle) chunk_directory;
89 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type) close_command;
90 };
91
92 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
93 struct lttng_trace_chunk_registry_element {
94 uint64_t session_id;
95 struct lttng_trace_chunk chunk;
96 /* Weak and only set when added. */
97 struct lttng_trace_chunk_registry *registry;
98 struct cds_lfht_node trace_chunk_registry_ht_node;
99 /* call_rcu delayed reclaim. */
100 struct rcu_head rcu_node;
101 };
102
103 struct lttng_trace_chunk_registry {
104 struct cds_lfht *ht;
105 };
106
107 const char *close_command_names[] = {
108 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
109 "move to completed chunk folder",
110 };
111
112 chunk_close_command close_command_funcs[] = {
113 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
114 lttng_trace_chunk_move_to_completed,
115 };
116
117 static
118 bool lttng_trace_chunk_registry_element_equals(
119 const struct lttng_trace_chunk_registry_element *a,
120 const struct lttng_trace_chunk_registry_element *b)
121 {
122 if (a->session_id != b->session_id) {
123 goto not_equal;
124 }
125 if (a->chunk.id.is_set != b->chunk.id.is_set) {
126 goto not_equal;
127 }
128 if (a->chunk.id.is_set && a->chunk.id.value != b->chunk.id.value) {
129 goto not_equal;
130 }
131 return true;
132 not_equal:
133 return false;
134 }
135
136 static
137 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node *node,
138 const void *key)
139 {
140 const struct lttng_trace_chunk_registry_element *element_a, *element_b;
141
142 element_a = (const struct lttng_trace_chunk_registry_element *) key;
143 element_b = caa_container_of(node, typeof(*element_b),
144 trace_chunk_registry_ht_node);
145 return lttng_trace_chunk_registry_element_equals(element_a, element_b);
146 }
147
148 static
149 unsigned long lttng_trace_chunk_registry_element_hash(
150 const struct lttng_trace_chunk_registry_element *element)
151 {
152 unsigned long hash = hash_key_u64(&element->session_id,
153 lttng_ht_seed);
154
155 if (element->chunk.id.is_set) {
156 hash ^= hash_key_u64(&element->chunk.id.value, lttng_ht_seed);
157 }
158
159 return hash;
160 }
161
162 static
163 char *generate_chunk_name(uint64_t chunk_id, time_t creation_timestamp,
164 const time_t *close_timestamp)
165 {
166 int ret = 0;
167 char *new_name= NULL;
168 char start_datetime[sizeof("YYYYmmddTHHMMSS+HHMM")] = {};
169 char end_datetime_suffix[sizeof("-YYYYmmddTHHMMSS+HHMM")] = {};
170
171 ret = time_to_iso8601_str(
172 creation_timestamp,
173 start_datetime, sizeof(start_datetime));
174 if (ret) {
175 ERR("Failed to format trace chunk start date time");
176 goto error;
177 }
178 if (close_timestamp) {
179 *end_datetime_suffix = '-';
180 ret = time_to_iso8601_str(
181 *close_timestamp,
182 end_datetime_suffix + 1,
183 sizeof(end_datetime_suffix));
184 if (ret) {
185 ERR("Failed to format trace chunk end date time");
186 goto error;
187 }
188 }
189 new_name = zmalloc(GENERATED_CHUNK_NAME_LEN);
190 if (!new_name) {
191 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
192 goto error;
193 }
194 ret = snprintf(new_name, GENERATED_CHUNK_NAME_LEN, "%s%s-%" PRIu64,
195 start_datetime, end_datetime_suffix, chunk_id);
196 if (ret >= GENERATED_CHUNK_NAME_LEN || ret == -1) {
197 ERR("Failed to format trace chunk name");
198 goto error;
199 }
200
201 return new_name;
202 error:
203 free(new_name);
204 return NULL;
205 }
206
207 static
208 void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk)
209 {
210 urcu_ref_init(&chunk->ref);
211 pthread_mutex_init(&chunk->lock, NULL);
212 lttng_dynamic_pointer_array_init(&chunk->top_level_directories);
213 }
214
215 static
216 void lttng_trace_chunk_fini(struct lttng_trace_chunk *chunk)
217 {
218 if (chunk->session_output_directory.is_set) {
219 lttng_directory_handle_fini(
220 &chunk->session_output_directory.value);
221 }
222 if (chunk->chunk_directory.is_set) {
223 lttng_directory_handle_fini(&chunk->chunk_directory.value);
224 }
225 free(chunk->name);
226 chunk->name = NULL;
227 lttng_dynamic_pointer_array_reset(&chunk->top_level_directories, free);
228 pthread_mutex_destroy(&chunk->lock);
229 }
230
231 static
232 struct lttng_trace_chunk *lttng_trace_chunk_allocate(void)
233 {
234 struct lttng_trace_chunk *chunk = NULL;
235
236 chunk = zmalloc(sizeof(*chunk));
237 if (!chunk) {
238 ERR("Failed to allocate trace chunk");
239 goto end;
240 }
241 lttng_trace_chunk_init(chunk);
242 end:
243 return chunk;
244 }
245
246 LTTNG_HIDDEN
247 struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous(void)
248 {
249 DBG("Creating anonymous trace chunk");
250 return lttng_trace_chunk_allocate();
251 }
252
253 LTTNG_HIDDEN
254 struct lttng_trace_chunk *lttng_trace_chunk_create(
255 uint64_t chunk_id, time_t chunk_creation_time)
256 {
257 struct lttng_trace_chunk *chunk;
258 char chunk_creation_datetime_buf[16] = {};
259 const char *chunk_creation_datetime_str = "(formatting error)";
260 struct tm timeinfo_buf, *timeinfo;
261
262 timeinfo = localtime_r(&chunk_creation_time, &timeinfo_buf);
263 if (timeinfo) {
264 size_t strftime_ret;
265
266 /* Don't fail because of this; it is only used for logging. */
267 strftime_ret = strftime(chunk_creation_datetime_buf,
268 sizeof(chunk_creation_datetime_buf),
269 "%Y%m%d-%H%M%S", timeinfo);
270 if (strftime_ret) {
271 chunk_creation_datetime_str =
272 chunk_creation_datetime_buf;
273 }
274 }
275
276 DBG("Creating trace chunk: chunk_id = %" PRIu64 ", creation time = %s",
277 chunk_id, chunk_creation_datetime_str);
278 chunk = lttng_trace_chunk_allocate();
279 if (!chunk) {
280 goto end;
281 }
282
283 LTTNG_OPTIONAL_SET(&chunk->id, chunk_id);
284 LTTNG_OPTIONAL_SET(&chunk->timestamp_creation, chunk_creation_time);
285 if (chunk_id != 0) {
286 chunk->name = generate_chunk_name(chunk_id,
287 chunk_creation_time, NULL);
288 if (!chunk->name) {
289 ERR("Failed to allocate trace chunk name storage");
290 goto error;
291 }
292 }
293
294 DBG("Chunk name set to \"%s\"", chunk->name ? : "(none)");
295 end:
296 return chunk;
297 error:
298 lttng_trace_chunk_put(chunk);
299 return NULL;
300 }
301
302 LTTNG_HIDDEN
303 enum lttng_trace_chunk_status lttng_trace_chunk_get_id(
304 struct lttng_trace_chunk *chunk, uint64_t *id)
305 {
306 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
307
308 pthread_mutex_lock(&chunk->lock);
309 if (chunk->id.is_set) {
310 *id = chunk->id.value;
311 } else {
312 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
313 }
314 pthread_mutex_unlock(&chunk->lock);
315 return status;
316 }
317
318 LTTNG_HIDDEN
319 enum lttng_trace_chunk_status lttng_trace_chunk_get_creation_timestamp(
320 struct lttng_trace_chunk *chunk, time_t *creation_ts)
321
322 {
323 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
324
325 pthread_mutex_lock(&chunk->lock);
326 if (chunk->timestamp_creation.is_set) {
327 *creation_ts = chunk->timestamp_creation.value;
328 } else {
329 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
330 }
331 pthread_mutex_unlock(&chunk->lock);
332 return status;
333 }
334
335 LTTNG_HIDDEN
336 enum lttng_trace_chunk_status lttng_trace_chunk_get_close_timestamp(
337 struct lttng_trace_chunk *chunk, time_t *close_ts)
338 {
339 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
340
341 pthread_mutex_lock(&chunk->lock);
342 if (chunk->timestamp_close.is_set) {
343 *close_ts = chunk->timestamp_close.value;
344 } else {
345 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
346 }
347 pthread_mutex_unlock(&chunk->lock);
348 return status;
349 }
350
351 LTTNG_HIDDEN
352 enum lttng_trace_chunk_status lttng_trace_chunk_set_close_timestamp(
353 struct lttng_trace_chunk *chunk, time_t close_ts)
354 {
355 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
356
357 pthread_mutex_lock(&chunk->lock);
358 if (!chunk->timestamp_creation.is_set) {
359 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
360 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
361 goto end;
362 }
363 if (chunk->timestamp_creation.value > close_ts) {
364 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
365 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
366 goto end;
367 }
368 LTTNG_OPTIONAL_SET(&chunk->timestamp_close, close_ts);
369 free(chunk->name);
370 chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id),
371 LTTNG_OPTIONAL_GET(chunk->timestamp_creation),
372 &close_ts);
373 if (!chunk->name) {
374 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
375 }
376 end:
377 pthread_mutex_unlock(&chunk->lock);
378 return status;
379 }
380
381 LTTNG_HIDDEN
382 enum lttng_trace_chunk_status lttng_trace_chunk_get_name(
383 struct lttng_trace_chunk *chunk, const char **name,
384 bool *name_overriden)
385 {
386 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
387
388 pthread_mutex_lock(&chunk->lock);
389 if (name_overriden) {
390 *name_overriden = chunk->name_overriden;
391 }
392 if (!chunk->name) {
393 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
394 goto end;
395 }
396 *name = chunk->name;
397 end:
398 pthread_mutex_unlock(&chunk->lock);
399 return status;
400 }
401
402 LTTNG_HIDDEN
403 enum lttng_trace_chunk_status lttng_trace_chunk_override_name(
404 struct lttng_trace_chunk *chunk, const char *name)
405
406 {
407 char *new_name;
408 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
409
410 if (!name || !*name || strnlen(name, LTTNG_NAME_MAX) == LTTNG_NAME_MAX) {
411 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
412 name ? : "NULL");
413 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
414 goto end;
415 }
416
417 pthread_mutex_lock(&chunk->lock);
418 if (!chunk->id.is_set) {
419 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
420 name);
421 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
422 goto end_unlock;
423 }
424 new_name = strdup(name);
425 if (!new_name) {
426 ERR("Failed to allocate new trace chunk name");
427 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
428 goto end_unlock;
429 }
430 free(chunk->name);
431 chunk->name = new_name;
432 chunk->name_overriden = true;
433 end_unlock:
434 pthread_mutex_unlock(&chunk->lock);
435 end:
436 return status;
437 }
438
439 LTTNG_HIDDEN
440 enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials(
441 struct lttng_trace_chunk *chunk,
442 struct lttng_credentials *credentials)
443 {
444 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
445
446 pthread_mutex_lock(&chunk->lock);
447 if (chunk->credentials.is_set) {
448 if (chunk->credentials.value.use_current_user) {
449 credentials->uid = geteuid();
450 credentials->gid = getegid();
451 } else {
452 *credentials = chunk->credentials.value.user;
453 }
454 } else {
455 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
456 }
457 pthread_mutex_unlock(&chunk->lock);
458 return status;
459 }
460
461 LTTNG_HIDDEN
462 enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials(
463 struct lttng_trace_chunk *chunk,
464 const struct lttng_credentials *user_credentials)
465 {
466 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
467 const struct chunk_credentials credentials = {
468 .user = *user_credentials,
469 .use_current_user = false,
470 };
471
472 pthread_mutex_lock(&chunk->lock);
473 if (chunk->credentials.is_set) {
474 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
475 goto end;
476 }
477 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
478 end:
479 pthread_mutex_unlock(&chunk->lock);
480 return status;
481 }
482
483 LTTNG_HIDDEN
484 enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials_current_user(
485 struct lttng_trace_chunk *chunk)
486 {
487 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
488 const struct chunk_credentials credentials = {
489 .use_current_user = true,
490 };
491
492 pthread_mutex_lock(&chunk->lock);
493 if (chunk->credentials.is_set) {
494 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
495 goto end;
496 }
497 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
498 end:
499 pthread_mutex_unlock(&chunk->lock);
500 return status;
501 }
502
503
504 LTTNG_HIDDEN
505 enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner(
506 struct lttng_trace_chunk *chunk,
507 struct lttng_directory_handle *session_output_directory)
508 {
509 int ret;
510 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
511 struct lttng_directory_handle chunk_directory_handle;
512
513 pthread_mutex_lock(&chunk->lock);
514 if (chunk->mode.is_set) {
515 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
516 goto end;
517 }
518 if (!chunk->credentials.is_set) {
519 /*
520 * Fatal error, credentials must be set before a
521 * directory is created.
522 */
523 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
524 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
525 goto end;
526 }
527
528 if (chunk->name) {
529 /*
530 * A nameless chunk does not need its own output directory.
531 * The session's output directory will be used.
532 */
533 ret = lttng_directory_handle_create_subdirectory_as_user(
534 session_output_directory,
535 chunk->name,
536 DIR_CREATION_MODE,
537 !chunk->credentials.value.use_current_user ?
538 &chunk->credentials.value.user : NULL);
539 if (ret) {
540 PERROR("Failed to create chunk output directory \"%s\"",
541 chunk->name);
542 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
543 goto end;
544 }
545 }
546 ret = lttng_directory_handle_init_from_handle(&chunk_directory_handle,
547 chunk->name,
548 session_output_directory);
549 if (ret) {
550 /* The function already logs on all error paths. */
551 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
552 goto end;
553 }
554 LTTNG_OPTIONAL_SET(&chunk->session_output_directory,
555 lttng_directory_handle_move(session_output_directory));
556 LTTNG_OPTIONAL_SET(&chunk->chunk_directory,
557 lttng_directory_handle_move(&chunk_directory_handle));
558 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER);
559 end:
560 pthread_mutex_unlock(&chunk->lock);
561 return status;
562 }
563
564 LTTNG_HIDDEN
565 enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user(
566 struct lttng_trace_chunk *chunk,
567 struct lttng_directory_handle *chunk_directory)
568 {
569 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
570
571 pthread_mutex_lock(&chunk->lock);
572 if (chunk->mode.is_set) {
573 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
574 goto end;
575 }
576 if (!chunk->credentials.is_set) {
577 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
578 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
579 goto end;
580 }
581 LTTNG_OPTIONAL_SET(&chunk->chunk_directory,
582 lttng_directory_handle_move(chunk_directory));
583 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER);
584 end:
585 pthread_mutex_unlock(&chunk->lock);
586 return status;
587 }
588
589 LTTNG_HIDDEN
590 enum lttng_trace_chunk_status lttng_trace_chunk_get_chunk_directory_handle(
591 struct lttng_trace_chunk *chunk,
592 const struct lttng_directory_handle **handle)
593 {
594 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
595
596 pthread_mutex_lock(&chunk->lock);
597 if (!chunk->chunk_directory.is_set) {
598 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
599 goto end;
600 }
601
602 *handle = &chunk->chunk_directory.value;
603 end:
604 pthread_mutex_unlock(&chunk->lock);
605 return status;
606 }
607
608 /* Add a top-level directory to the trace chunk if it was previously unknown. */
609 static
610 int add_top_level_directory_unique(struct lttng_trace_chunk *chunk,
611 const char *new_path)
612 {
613 int ret = 0;
614 bool found = false;
615 size_t i, count = lttng_dynamic_pointer_array_get_count(
616 &chunk->top_level_directories);
617 const char *new_path_separator_pos = strchr(new_path, '/');
618 const ptrdiff_t new_path_top_level_len = new_path_separator_pos ?
619 new_path_separator_pos - new_path : strlen(new_path);
620
621 for (i = 0; i < count; i++) {
622 const char *path = lttng_dynamic_pointer_array_get_pointer(
623 &chunk->top_level_directories, i);
624 const ptrdiff_t path_top_level_len = strlen(path);
625
626 if (path_top_level_len != new_path_top_level_len) {
627 continue;
628 }
629 if (!strncmp(path, new_path, path_top_level_len)) {
630 found = true;
631 break;
632 }
633 }
634
635 if (!found) {
636 char *copy = strndup(new_path, new_path_top_level_len);
637
638 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
639 new_path, chunk->name ? : "(unnamed)");
640 if (!copy) {
641 PERROR("Failed to copy path");
642 ret = -1;
643 goto end;
644 }
645 ret = lttng_dynamic_pointer_array_add_pointer(
646 &chunk->top_level_directories, copy);
647 if (ret) {
648 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
649 free(copy);
650 goto end;
651 }
652 }
653 end:
654 return ret;
655 }
656
657 LTTNG_HIDDEN
658 enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory(
659 struct lttng_trace_chunk *chunk,
660 const char *path)
661 {
662 int ret;
663 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
664
665 DBG("Creating trace chunk subdirectory \"%s\"", path);
666 pthread_mutex_lock(&chunk->lock);
667 if (!chunk->credentials.is_set) {
668 /*
669 * Fatal error, credentials must be set before a
670 * directory is created.
671 */
672 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
673 path);
674 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
675 goto end;
676 }
677 if (!chunk->mode.is_set ||
678 chunk->mode.value != TRACE_CHUNK_MODE_OWNER) {
679 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
680 path);
681 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
682 goto end;
683 }
684 if (!chunk->chunk_directory.is_set) {
685 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
686 path);
687 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
688 goto end;
689 }
690 if (*path == '/') {
691 ERR("Refusing to create absolute trace chunk directory \"%s\"",
692 path);
693 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
694 goto end;
695 }
696 ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
697 &chunk->chunk_directory.value, path,
698 DIR_CREATION_MODE,
699 chunk->credentials.value.use_current_user ?
700 NULL : &chunk->credentials.value.user);
701 if (ret) {
702 PERROR("Failed to create trace chunk subdirectory \"%s\"",
703 path);
704 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
705 goto end;
706 }
707 ret = add_top_level_directory_unique(chunk, path);
708 if (ret) {
709 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
710 goto end;
711 }
712 end:
713 pthread_mutex_unlock(&chunk->lock);
714 return status;
715 }
716
717 LTTNG_HIDDEN
718 enum lttng_trace_chunk_status lttng_trace_chunk_open_file(
719 struct lttng_trace_chunk *chunk, const char *file_path,
720 int flags, mode_t mode, int *out_fd)
721 {
722 int ret;
723 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
724
725 DBG("Opening trace chunk file \"%s\"", file_path);
726 pthread_mutex_lock(&chunk->lock);
727 if (!chunk->credentials.is_set) {
728 /*
729 * Fatal error, credentials must be set before a
730 * file is created.
731 */
732 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
733 file_path);
734 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
735 goto end;
736 }
737 if (!chunk->chunk_directory.is_set) {
738 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
739 file_path);
740 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
741 goto end;
742 }
743 ret = lttng_directory_handle_open_file_as_user(
744 &chunk->chunk_directory.value, file_path, flags, mode,
745 chunk->credentials.value.use_current_user ?
746 NULL : &chunk->credentials.value.user);
747 if (ret < 0) {
748 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
749 goto end;
750 }
751 *out_fd = ret;
752 end:
753 pthread_mutex_unlock(&chunk->lock);
754 return status;
755 }
756
757 LTTNG_HIDDEN
758 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk,
759 const char *file_path)
760 {
761 int ret;
762 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
763
764 DBG("Unlinking trace chunk file \"%s\"", file_path);
765 pthread_mutex_lock(&chunk->lock);
766 if (!chunk->credentials.is_set) {
767 /*
768 * Fatal error, credentials must be set before a
769 * directory is created.
770 */
771 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
772 file_path);
773 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
774 goto end;
775 }
776 if (!chunk->chunk_directory.is_set) {
777 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
778 file_path);
779 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
780 goto end;
781 }
782 ret = lttng_directory_handle_unlink_file_as_user(
783 &chunk->chunk_directory.value, file_path,
784 chunk->credentials.value.use_current_user ?
785 NULL : &chunk->credentials.value.user);
786 if (ret < 0) {
787 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
788 goto end;
789 }
790 end:
791 pthread_mutex_unlock(&chunk->lock);
792 return status;
793 }
794
795 static
796 void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk)
797 {
798 int ret;
799 char *directory_to_rename = NULL;
800 bool free_directory_to_rename = false;
801 const int session_dirfd =
802 trace_chunk->session_output_directory.value.dirfd;
803 char *archived_chunk_name = NULL;
804 const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id);
805 const time_t creation_timestamp =
806 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation);
807 const time_t close_timestamp =
808 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close);
809 LTTNG_OPTIONAL(struct lttng_directory_handle) archived_chunks_directory;
810
811 assert(trace_chunk->mode.is_set);
812 assert(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
813 assert(!trace_chunk->name_overriden);
814
815 /*
816 * The fist trace chunk of a session is directly output to the
817 * session's output folder. In this case, the top level directories
818 * must be moved to a temporary folder before that temporary directory
819 * is renamed to match the chunk's name.
820 */
821 if (chunk_id == 0) {
822 struct lttng_directory_handle temporary_rename_directory;
823 size_t i, count = lttng_dynamic_pointer_array_get_count(
824 &trace_chunk->top_level_directories);
825
826 ret = lttng_directory_handle_create_subdirectory_as_user(
827 &trace_chunk->session_output_directory.value,
828 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY,
829 DIR_CREATION_MODE,
830 !trace_chunk->credentials.value.use_current_user ?
831 &trace_chunk->credentials.value.user : NULL);
832 if (ret) {
833 PERROR("Failed to create temporary trace chunk rename directory \"%s\"",
834 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY);
835 }
836
837 ret = lttng_directory_handle_init_from_handle(&temporary_rename_directory,
838 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY,
839 &trace_chunk->session_output_directory.value);
840 if (ret) {
841 ERR("Failed to get handle to temporary trace chunk rename directory");
842 goto end;
843 }
844
845 for (i = 0; i < count; i++) {
846 const int temp_dirfd = temporary_rename_directory.dirfd;
847 const char *top_level_name =
848 lttng_dynamic_pointer_array_get_pointer(
849 &trace_chunk->top_level_directories, i);
850
851 /*
852 * FIXME replace renamat() use by directory handle
853 * wrapper for non-POSIX 2008 systems.
854 */
855 ret = renameat(session_dirfd, top_level_name,
856 temp_dirfd, top_level_name);
857 if (ret) {
858 PERROR("Failed to move \"%s\" to temporary trace chunk rename directory",
859 top_level_name);
860 lttng_directory_handle_fini(
861 &temporary_rename_directory);
862 goto end;
863 }
864 }
865 lttng_directory_handle_fini(&temporary_rename_directory);
866 directory_to_rename = DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY;
867 free_directory_to_rename = false;
868 } else {
869 directory_to_rename = generate_chunk_name(chunk_id,
870 creation_timestamp, NULL);
871 if (!directory_to_rename) {
872 ERR("Failed to generate initial trace chunk name while renaming trace chunk");
873 }
874 free_directory_to_rename = true;
875 }
876
877 archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp,
878 &close_timestamp);
879 if (!archived_chunk_name) {
880 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
881 goto end;
882 }
883
884 ret = lttng_directory_handle_create_subdirectory_as_user(
885 &trace_chunk->session_output_directory.value,
886 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
887 DIR_CREATION_MODE,
888 !trace_chunk->credentials.value.use_current_user ?
889 &trace_chunk->credentials.value.user :
890 NULL);
891 if (ret) {
892 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
893 "\" directory for archived trace chunks");
894 goto end;
895 }
896
897 ret = lttng_directory_handle_init_from_handle(
898 &archived_chunks_directory.value,
899 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
900 &trace_chunk->session_output_directory.value);
901 if (ret) {
902 PERROR("Failed to get handle to archived trace chunks directory");
903 goto end;
904 }
905 archived_chunks_directory.is_set = true;
906
907 /*
908 * FIXME replace renamat() use by directory handle
909 * wrapper for non-POSIX 2008 systems.
910 */
911 ret = renameat(session_dirfd, directory_to_rename,
912 archived_chunks_directory.value.dirfd,
913 archived_chunk_name);
914 if (ret) {
915 PERROR("Failed to rename folder \"%s\" to \"%s\"",
916 directory_to_rename, archived_chunk_name);
917 }
918
919 end:
920 if (archived_chunks_directory.is_set) {
921 lttng_directory_handle_fini(&archived_chunks_directory.value);
922 }
923 free(archived_chunk_name);
924 if (free_directory_to_rename) {
925 free(directory_to_rename);
926 }
927 }
928
929 LTTNG_HIDDEN
930 enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
931 struct lttng_trace_chunk *chunk,
932 enum lttng_trace_chunk_command_type close_command)
933 {
934 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
935
936 if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
937 close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
938 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
939 goto end_unlock;
940 }
941
942 pthread_mutex_lock(&chunk->lock);
943 if (chunk->close_command.is_set) {
944 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
945 close_command_names[chunk->close_command.value],
946 close_command_names[close_command]);
947 } else {
948 DBG("Setting trace chunk close command to \"%s\"",
949 close_command_names[close_command]);
950 }
951 LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
952 pthread_mutex_unlock(&chunk->lock);
953 end_unlock:
954 return status;
955 }
956
957 LTTNG_HIDDEN
958 bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
959 {
960 return urcu_ref_get_unless_zero(&chunk->ref);
961 }
962
963 static
964 void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
965 {
966 struct lttng_trace_chunk_registry_element *element =
967 container_of(node, typeof(*element), rcu_node);
968
969 lttng_trace_chunk_fini(&element->chunk);
970 free(element);
971 }
972
973 static
974 void lttng_trace_chunk_release(struct urcu_ref *ref)
975 {
976 struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk),
977 ref);
978
979 if (chunk->close_command.is_set) {
980 close_command_funcs[chunk->close_command.value](chunk);
981 }
982
983 if (chunk->in_registry_element) {
984 struct lttng_trace_chunk_registry_element *element;
985
986 element = container_of(chunk, typeof(*element), chunk);
987 if (element->registry) {
988 rcu_read_lock();
989 cds_lfht_del(element->registry->ht,
990 &element->trace_chunk_registry_ht_node);
991 rcu_read_unlock();
992 call_rcu(&element->rcu_node,
993 free_lttng_trace_chunk_registry_element);
994 } else {
995 /* Never published, can be free'd immediately. */
996 free_lttng_trace_chunk_registry_element(
997 &element->rcu_node);
998 }
999 } else {
1000 /* Not RCU-protected, free immediately. */
1001 lttng_trace_chunk_fini(chunk);
1002 free(chunk);
1003 }
1004 }
1005
1006 LTTNG_HIDDEN
1007 void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
1008 {
1009 if (!chunk) {
1010 return;
1011 }
1012 assert(chunk->ref.refcount);
1013 urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
1014 }
1015
1016 LTTNG_HIDDEN
1017 struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void)
1018 {
1019 struct lttng_trace_chunk_registry *registry;
1020
1021 registry = zmalloc(sizeof(*registry));
1022 if (!registry) {
1023 goto end;
1024 }
1025
1026 registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
1027 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
1028 if (!registry->ht) {
1029 goto error;
1030 }
1031 end:
1032 return registry;
1033 error:
1034 lttng_trace_chunk_registry_destroy(registry);
1035 goto end;
1036 }
1037
1038 LTTNG_HIDDEN
1039 void lttng_trace_chunk_registry_destroy(
1040 struct lttng_trace_chunk_registry *registry)
1041 {
1042 if (!registry) {
1043 return;
1044 }
1045 if (registry->ht) {
1046 int ret = cds_lfht_destroy(registry->ht, NULL);
1047 assert(!ret);
1048 }
1049 free(registry);
1050 }
1051
1052 static
1053 struct lttng_trace_chunk_registry_element *
1054 lttng_trace_chunk_registry_element_create_from_chunk(
1055 struct lttng_trace_chunk *chunk, uint64_t session_id)
1056 {
1057 struct lttng_trace_chunk_registry_element *element =
1058 zmalloc(sizeof(*element));
1059
1060 if (!element) {
1061 goto end;
1062 }
1063 cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
1064 element->session_id = session_id;
1065
1066 element->chunk = *chunk;
1067 lttng_trace_chunk_init(&element->chunk);
1068 if (chunk->session_output_directory.is_set) {
1069 element->chunk.session_output_directory.value =
1070 lttng_directory_handle_move(
1071 &chunk->session_output_directory.value);
1072 }
1073 if (chunk->chunk_directory.is_set) {
1074 element->chunk.chunk_directory.value =
1075 lttng_directory_handle_move(
1076 &chunk->chunk_directory.value);
1077 }
1078 /*
1079 * The original chunk becomes invalid; the name attribute is transferred
1080 * to the new chunk instance.
1081 */
1082 chunk->name = NULL;
1083 element->chunk.in_registry_element = true;
1084 end:
1085 return element;
1086 }
1087
1088 LTTNG_HIDDEN
1089 struct lttng_trace_chunk *
1090 lttng_trace_chunk_registry_publish_chunk(
1091 struct lttng_trace_chunk_registry *registry,
1092 uint64_t session_id, struct lttng_trace_chunk *chunk)
1093 {
1094 struct lttng_trace_chunk_registry_element *element;
1095 unsigned long element_hash;
1096
1097 pthread_mutex_lock(&chunk->lock);
1098 element = lttng_trace_chunk_registry_element_create_from_chunk(chunk,
1099 session_id);
1100 pthread_mutex_unlock(&chunk->lock);
1101 if (!element) {
1102 goto end;
1103 }
1104 /*
1105 * chunk is now invalid, the only valid operation is a 'put' from the
1106 * caller.
1107 */
1108 chunk = NULL;
1109 element_hash = lttng_trace_chunk_registry_element_hash(element);
1110
1111 rcu_read_lock();
1112 while (1) {
1113 struct cds_lfht_node *published_node;
1114 struct lttng_trace_chunk *published_chunk;
1115 struct lttng_trace_chunk_registry_element *published_element;
1116
1117 published_node = cds_lfht_add_unique(registry->ht,
1118 element_hash,
1119 lttng_trace_chunk_registry_element_match,
1120 element,
1121 &element->trace_chunk_registry_ht_node);
1122 if (published_node == &element->trace_chunk_registry_ht_node) {
1123 /* Successfully published the new element. */
1124 element->registry = registry;
1125 /* Acquire a reference for the caller. */
1126 if (lttng_trace_chunk_get(&element->chunk)) {
1127 break;
1128 } else {
1129 /*
1130 * Another thread concurrently unpublished the
1131 * trace chunk. This is currently unexpected.
1132 *
1133 * Re-attempt to publish.
1134 */
1135 ERR("Attemp to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1136 continue;
1137 }
1138 }
1139
1140 /*
1141 * An equivalent trace chunk was published before this trace
1142 * chunk. Attempt to acquire a reference to the one that was
1143 * already published and release the reference to the copy we
1144 * created if successful.
1145 */
1146 published_element = container_of(published_node,
1147 typeof(*published_element),
1148 trace_chunk_registry_ht_node);
1149 published_chunk = &published_element->chunk;
1150 if (lttng_trace_chunk_get(published_chunk)) {
1151 lttng_trace_chunk_put(&element->chunk);
1152 element = published_element;
1153 break;
1154 }
1155 /*
1156 * A reference to the previously published trace chunk could not
1157 * be acquired. Hence, retry to publish our copy of the trace
1158 * chunk.
1159 */
1160 }
1161 rcu_read_unlock();
1162 end:
1163 return element ? &element->chunk : NULL;
1164 }
1165
1166 /*
1167 * Note that the caller must be registered as an RCU thread.
1168 * However, it does not need to hold the RCU read lock. The RCU read lock is
1169 * acquired to perform the look-up in the registry's hash table and held until
1170 * after a reference to the "found" trace chunk is acquired.
1171 *
1172 * IOW, holding a reference guarantees the existence of the object for the
1173 * caller.
1174 */
1175 static
1176 struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
1177 const struct lttng_trace_chunk_registry *registry,
1178 uint64_t session_id, uint64_t *chunk_id)
1179 {
1180 const struct lttng_trace_chunk_registry_element target_element = {
1181 .chunk.id.is_set = !!chunk_id,
1182 .chunk.id.value = chunk_id ? *chunk_id : 0,
1183 .session_id = session_id,
1184 };
1185 const unsigned long element_hash =
1186 lttng_trace_chunk_registry_element_hash(
1187 &target_element);
1188 struct cds_lfht_node *published_node;
1189 struct lttng_trace_chunk_registry_element *published_element;
1190 struct lttng_trace_chunk *published_chunk = NULL;
1191 struct cds_lfht_iter iter;
1192
1193 rcu_read_lock();
1194 cds_lfht_lookup(registry->ht,
1195 element_hash,
1196 lttng_trace_chunk_registry_element_match,
1197 &target_element,
1198 &iter);
1199 published_node = cds_lfht_iter_get_node(&iter);
1200 if (!published_node) {
1201 goto end;
1202 }
1203
1204 published_element = container_of(published_node,
1205 typeof(*published_element),
1206 trace_chunk_registry_ht_node);
1207 if (lttng_trace_chunk_get(&published_element->chunk)) {
1208 published_chunk = &published_element->chunk;
1209 }
1210 end:
1211 rcu_read_unlock();
1212 return published_chunk;
1213 }
1214
1215 LTTNG_HIDDEN
1216 struct lttng_trace_chunk *
1217 lttng_trace_chunk_registry_find_chunk(
1218 const struct lttng_trace_chunk_registry *registry,
1219 uint64_t session_id, uint64_t chunk_id)
1220 {
1221 return _lttng_trace_chunk_registry_find_chunk(registry,
1222 session_id, &chunk_id);
1223 }
1224
1225 LTTNG_HIDDEN
1226 struct lttng_trace_chunk *
1227 lttng_trace_chunk_registry_find_anonymous_chunk(
1228 const struct lttng_trace_chunk_registry *registry,
1229 uint64_t session_id)
1230 {
1231 return _lttng_trace_chunk_registry_find_chunk(registry,
1232 session_id, NULL);
1233 }
This page took 0.096978 seconds and 5 git commands to generate.