Use directory handle to rename trace chunk directory
[lttng-tools.git] / src / common / trace-chunk.c
CommitLineData
1545fd17
JG
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
47enum 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 */
58typedef void (*chunk_close_command)(struct lttng_trace_chunk *trace_chunk);
59
60/* Move a completed trace chunk to the 'completed' trace archive folder. */
61static
62void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk);
63
64struct chunk_credentials {
65 bool use_current_user;
66 struct lttng_credentials user;
67};
68
69struct 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. */
93struct 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
103struct lttng_trace_chunk_registry {
104 struct cds_lfht *ht;
105};
106
107const char *close_command_names[] = {
108 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
109 "move to completed chunk folder",
110};
111
112chunk_close_command close_command_funcs[] = {
113 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
114 lttng_trace_chunk_move_to_completed,
115};
116
117static
118bool 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;
132not_equal:
133 return false;
134}
135
136static
137int 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
148static
149unsigned 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
162static
163char *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;
202error:
203 free(new_name);
204 return NULL;
205}
206
207static
208void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk)
209{
210 urcu_ref_init(&chunk->ref);
211 pthread_mutex_init(&chunk->lock, NULL);
fcde531a 212 lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free);
1545fd17
JG
213}
214
215static
216void 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;
fcde531a 227 lttng_dynamic_pointer_array_reset(&chunk->top_level_directories);
1545fd17
JG
228 pthread_mutex_destroy(&chunk->lock);
229}
230
231static
232struct 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);
242end:
243 return chunk;
244}
245
246LTTNG_HIDDEN
247struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous(void)
248{
249 DBG("Creating anonymous trace chunk");
250 return lttng_trace_chunk_allocate();
251}
252
253LTTNG_HIDDEN
254struct 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)");
295end:
296 return chunk;
297error:
298 lttng_trace_chunk_put(chunk);
299 return NULL;
300}
301
302LTTNG_HIDDEN
303enum 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
318LTTNG_HIDDEN
319enum 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
335LTTNG_HIDDEN
336enum 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
351LTTNG_HIDDEN
352enum 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 }
376end:
377 pthread_mutex_unlock(&chunk->lock);
378 return status;
379}
380
381LTTNG_HIDDEN
382enum 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;
397end:
398 pthread_mutex_unlock(&chunk->lock);
399 return status;
400}
401
402LTTNG_HIDDEN
403enum 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;
433end_unlock:
434 pthread_mutex_unlock(&chunk->lock);
435end:
436 return status;
437}
438
439LTTNG_HIDDEN
440enum 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
461LTTNG_HIDDEN
462enum 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);
478end:
479 pthread_mutex_unlock(&chunk->lock);
480 return status;
481}
482
483LTTNG_HIDDEN
484enum 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);
498end:
499 pthread_mutex_unlock(&chunk->lock);
500 return status;
501}
502
503
504LTTNG_HIDDEN
505enum 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);
559end:
560 pthread_mutex_unlock(&chunk->lock);
561 return status;
562}
563
564LTTNG_HIDDEN
565enum 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);
584end:
585 pthread_mutex_unlock(&chunk->lock);
586 return status;
587}
588
589LTTNG_HIDDEN
590enum 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;
603end:
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. */
609static
610int 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 }
653end:
654 return ret;
655}
656
657LTTNG_HIDDEN
658enum 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 }
712end:
713 pthread_mutex_unlock(&chunk->lock);
714 return status;
715}
716
717LTTNG_HIDDEN
718enum 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) {
e5148e25
JG
748 ERR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
749 file_path, flags, (int) mode);
1545fd17
JG
750 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
751 goto end;
752 }
753 *out_fd = ret;
754end:
755 pthread_mutex_unlock(&chunk->lock);
756 return status;
757}
758
759LTTNG_HIDDEN
760int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk,
761 const char *file_path)
762{
763 int ret;
764 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
765
766 DBG("Unlinking trace chunk file \"%s\"", file_path);
767 pthread_mutex_lock(&chunk->lock);
768 if (!chunk->credentials.is_set) {
769 /*
770 * Fatal error, credentials must be set before a
771 * directory is created.
772 */
773 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
774 file_path);
775 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
776 goto end;
777 }
778 if (!chunk->chunk_directory.is_set) {
779 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
780 file_path);
781 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
782 goto end;
783 }
784 ret = lttng_directory_handle_unlink_file_as_user(
785 &chunk->chunk_directory.value, file_path,
786 chunk->credentials.value.use_current_user ?
787 NULL : &chunk->credentials.value.user);
788 if (ret < 0) {
789 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
790 goto end;
791 }
792end:
793 pthread_mutex_unlock(&chunk->lock);
794 return status;
795}
796
797static
798void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk)
799{
800 int ret;
801 char *directory_to_rename = NULL;
802 bool free_directory_to_rename = false;
1545fd17
JG
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++) {
1545fd17
JG
846 const char *top_level_name =
847 lttng_dynamic_pointer_array_get_pointer(
848 &trace_chunk->top_level_directories, i);
849
dcea4aea
JG
850 ret = lttng_directory_handle_rename_as_user(
851 &trace_chunk->session_output_directory.value,
852 top_level_name,
853 &temporary_rename_directory,
854 top_level_name,
855 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
856 NULL :
857 &trace_chunk->credentials.value.user);
1545fd17
JG
858 if (ret) {
859 PERROR("Failed to move \"%s\" to temporary trace chunk rename directory",
860 top_level_name);
861 lttng_directory_handle_fini(
862 &temporary_rename_directory);
863 goto end;
864 }
865 }
866 lttng_directory_handle_fini(&temporary_rename_directory);
867 directory_to_rename = DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY;
868 free_directory_to_rename = false;
869 } else {
870 directory_to_rename = generate_chunk_name(chunk_id,
871 creation_timestamp, NULL);
872 if (!directory_to_rename) {
873 ERR("Failed to generate initial trace chunk name while renaming trace chunk");
874 }
875 free_directory_to_rename = true;
876 }
877
878 archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp,
879 &close_timestamp);
880 if (!archived_chunk_name) {
881 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
882 goto end;
883 }
884
885 ret = lttng_directory_handle_create_subdirectory_as_user(
886 &trace_chunk->session_output_directory.value,
887 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
888 DIR_CREATION_MODE,
889 !trace_chunk->credentials.value.use_current_user ?
890 &trace_chunk->credentials.value.user :
891 NULL);
892 if (ret) {
893 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
894 "\" directory for archived trace chunks");
895 goto end;
896 }
897
898 ret = lttng_directory_handle_init_from_handle(
899 &archived_chunks_directory.value,
900 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
901 &trace_chunk->session_output_directory.value);
902 if (ret) {
903 PERROR("Failed to get handle to archived trace chunks directory");
904 goto end;
905 }
906 archived_chunks_directory.is_set = true;
907
dcea4aea
JG
908 ret = lttng_directory_handle_rename_as_user(
909 &trace_chunk->session_output_directory.value,
910 directory_to_rename,
911 &archived_chunks_directory.value,
912 archived_chunk_name,
913 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
914 NULL :
915 &trace_chunk->credentials.value.user);
1545fd17
JG
916 if (ret) {
917 PERROR("Failed to rename folder \"%s\" to \"%s\"",
918 directory_to_rename, archived_chunk_name);
919 }
920
921end:
922 if (archived_chunks_directory.is_set) {
923 lttng_directory_handle_fini(&archived_chunks_directory.value);
924 }
925 free(archived_chunk_name);
926 if (free_directory_to_rename) {
927 free(directory_to_rename);
928 }
929}
930
931LTTNG_HIDDEN
932enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
933 struct lttng_trace_chunk *chunk,
934 enum lttng_trace_chunk_command_type close_command)
935{
936 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
937
938 if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
939 close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
940 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
941 goto end_unlock;
942 }
943
944 pthread_mutex_lock(&chunk->lock);
945 if (chunk->close_command.is_set) {
946 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
947 close_command_names[chunk->close_command.value],
948 close_command_names[close_command]);
949 } else {
950 DBG("Setting trace chunk close command to \"%s\"",
951 close_command_names[close_command]);
952 }
953 LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
954 pthread_mutex_unlock(&chunk->lock);
955end_unlock:
956 return status;
957}
958
959LTTNG_HIDDEN
960bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
961{
962 return urcu_ref_get_unless_zero(&chunk->ref);
963}
964
965static
966void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
967{
968 struct lttng_trace_chunk_registry_element *element =
969 container_of(node, typeof(*element), rcu_node);
970
971 lttng_trace_chunk_fini(&element->chunk);
972 free(element);
973}
974
975static
976void lttng_trace_chunk_release(struct urcu_ref *ref)
977{
978 struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk),
979 ref);
980
981 if (chunk->close_command.is_set) {
982 close_command_funcs[chunk->close_command.value](chunk);
983 }
984
985 if (chunk->in_registry_element) {
986 struct lttng_trace_chunk_registry_element *element;
987
988 element = container_of(chunk, typeof(*element), chunk);
989 if (element->registry) {
990 rcu_read_lock();
991 cds_lfht_del(element->registry->ht,
992 &element->trace_chunk_registry_ht_node);
993 rcu_read_unlock();
994 call_rcu(&element->rcu_node,
995 free_lttng_trace_chunk_registry_element);
996 } else {
997 /* Never published, can be free'd immediately. */
998 free_lttng_trace_chunk_registry_element(
999 &element->rcu_node);
1000 }
1001 } else {
1002 /* Not RCU-protected, free immediately. */
1003 lttng_trace_chunk_fini(chunk);
1004 free(chunk);
1005 }
1006}
1007
1008LTTNG_HIDDEN
1009void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
1010{
1011 if (!chunk) {
1012 return;
1013 }
1014 assert(chunk->ref.refcount);
1015 urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
1016}
1017
1018LTTNG_HIDDEN
1019struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void)
1020{
1021 struct lttng_trace_chunk_registry *registry;
1022
1023 registry = zmalloc(sizeof(*registry));
1024 if (!registry) {
1025 goto end;
1026 }
1027
1028 registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
1029 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
1030 if (!registry->ht) {
1031 goto error;
1032 }
1033end:
1034 return registry;
1035error:
1036 lttng_trace_chunk_registry_destroy(registry);
1037 goto end;
1038}
1039
1040LTTNG_HIDDEN
1041void lttng_trace_chunk_registry_destroy(
1042 struct lttng_trace_chunk_registry *registry)
1043{
1044 if (!registry) {
1045 return;
1046 }
1047 if (registry->ht) {
1048 int ret = cds_lfht_destroy(registry->ht, NULL);
1049 assert(!ret);
1050 }
1051 free(registry);
1052}
1053
1054static
1055struct lttng_trace_chunk_registry_element *
1056lttng_trace_chunk_registry_element_create_from_chunk(
1057 struct lttng_trace_chunk *chunk, uint64_t session_id)
1058{
1059 struct lttng_trace_chunk_registry_element *element =
1060 zmalloc(sizeof(*element));
1061
1062 if (!element) {
1063 goto end;
1064 }
1065 cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
1066 element->session_id = session_id;
1067
1068 element->chunk = *chunk;
1069 lttng_trace_chunk_init(&element->chunk);
1070 if (chunk->session_output_directory.is_set) {
1071 element->chunk.session_output_directory.value =
1072 lttng_directory_handle_move(
1073 &chunk->session_output_directory.value);
1074 }
1075 if (chunk->chunk_directory.is_set) {
1076 element->chunk.chunk_directory.value =
1077 lttng_directory_handle_move(
1078 &chunk->chunk_directory.value);
1079 }
1080 /*
1081 * The original chunk becomes invalid; the name attribute is transferred
1082 * to the new chunk instance.
1083 */
1084 chunk->name = NULL;
1085 element->chunk.in_registry_element = true;
1086end:
1087 return element;
1088}
1089
1090LTTNG_HIDDEN
1091struct lttng_trace_chunk *
1092lttng_trace_chunk_registry_publish_chunk(
1093 struct lttng_trace_chunk_registry *registry,
1094 uint64_t session_id, struct lttng_trace_chunk *chunk)
1095{
1096 struct lttng_trace_chunk_registry_element *element;
1097 unsigned long element_hash;
1098
1099 pthread_mutex_lock(&chunk->lock);
1100 element = lttng_trace_chunk_registry_element_create_from_chunk(chunk,
1101 session_id);
1102 pthread_mutex_unlock(&chunk->lock);
1103 if (!element) {
1104 goto end;
1105 }
1106 /*
1107 * chunk is now invalid, the only valid operation is a 'put' from the
1108 * caller.
1109 */
1110 chunk = NULL;
1111 element_hash = lttng_trace_chunk_registry_element_hash(element);
1112
1113 rcu_read_lock();
1114 while (1) {
1115 struct cds_lfht_node *published_node;
1116 struct lttng_trace_chunk *published_chunk;
1117 struct lttng_trace_chunk_registry_element *published_element;
1118
1119 published_node = cds_lfht_add_unique(registry->ht,
1120 element_hash,
1121 lttng_trace_chunk_registry_element_match,
1122 element,
1123 &element->trace_chunk_registry_ht_node);
1124 if (published_node == &element->trace_chunk_registry_ht_node) {
1125 /* Successfully published the new element. */
1126 element->registry = registry;
1127 /* Acquire a reference for the caller. */
1128 if (lttng_trace_chunk_get(&element->chunk)) {
1129 break;
1130 } else {
1131 /*
1132 * Another thread concurrently unpublished the
1133 * trace chunk. This is currently unexpected.
1134 *
1135 * Re-attempt to publish.
1136 */
1137 ERR("Attemp to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1138 continue;
1139 }
1140 }
1141
1142 /*
1143 * An equivalent trace chunk was published before this trace
1144 * chunk. Attempt to acquire a reference to the one that was
1145 * already published and release the reference to the copy we
1146 * created if successful.
1147 */
1148 published_element = container_of(published_node,
1149 typeof(*published_element),
1150 trace_chunk_registry_ht_node);
1151 published_chunk = &published_element->chunk;
1152 if (lttng_trace_chunk_get(published_chunk)) {
1153 lttng_trace_chunk_put(&element->chunk);
1154 element = published_element;
1155 break;
1156 }
1157 /*
1158 * A reference to the previously published trace chunk could not
1159 * be acquired. Hence, retry to publish our copy of the trace
1160 * chunk.
1161 */
1162 }
1163 rcu_read_unlock();
1164end:
1165 return element ? &element->chunk : NULL;
1166}
1167
1168/*
1169 * Note that the caller must be registered as an RCU thread.
1170 * However, it does not need to hold the RCU read lock. The RCU read lock is
1171 * acquired to perform the look-up in the registry's hash table and held until
1172 * after a reference to the "found" trace chunk is acquired.
1173 *
1174 * IOW, holding a reference guarantees the existence of the object for the
1175 * caller.
1176 */
1177static
1178struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
1179 const struct lttng_trace_chunk_registry *registry,
1180 uint64_t session_id, uint64_t *chunk_id)
1181{
1182 const struct lttng_trace_chunk_registry_element target_element = {
1183 .chunk.id.is_set = !!chunk_id,
1184 .chunk.id.value = chunk_id ? *chunk_id : 0,
1185 .session_id = session_id,
1186 };
1187 const unsigned long element_hash =
1188 lttng_trace_chunk_registry_element_hash(
1189 &target_element);
1190 struct cds_lfht_node *published_node;
1191 struct lttng_trace_chunk_registry_element *published_element;
1192 struct lttng_trace_chunk *published_chunk = NULL;
1193 struct cds_lfht_iter iter;
1194
1195 rcu_read_lock();
1196 cds_lfht_lookup(registry->ht,
1197 element_hash,
1198 lttng_trace_chunk_registry_element_match,
1199 &target_element,
1200 &iter);
1201 published_node = cds_lfht_iter_get_node(&iter);
1202 if (!published_node) {
1203 goto end;
1204 }
1205
1206 published_element = container_of(published_node,
1207 typeof(*published_element),
1208 trace_chunk_registry_ht_node);
1209 if (lttng_trace_chunk_get(&published_element->chunk)) {
1210 published_chunk = &published_element->chunk;
1211 }
1212end:
1213 rcu_read_unlock();
1214 return published_chunk;
1215}
1216
1217LTTNG_HIDDEN
1218struct lttng_trace_chunk *
1219lttng_trace_chunk_registry_find_chunk(
1220 const struct lttng_trace_chunk_registry *registry,
1221 uint64_t session_id, uint64_t chunk_id)
1222{
1223 return _lttng_trace_chunk_registry_find_chunk(registry,
1224 session_id, &chunk_id);
1225}
1226
1227LTTNG_HIDDEN
1228struct lttng_trace_chunk *
1229lttng_trace_chunk_registry_find_anonymous_chunk(
1230 const struct lttng_trace_chunk_registry *registry,
1231 uint64_t session_id)
1232{
1233 return _lttng_trace_chunk_registry_find_chunk(registry,
1234 session_id, NULL);
1235}
This page took 0.075663 seconds and 4 git commands to generate.