relayd: add remote trace chunk close command
[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, free);
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);
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 static
403 bool is_valid_chunk_name(const char *name)
404 {
405 size_t len;
406
407 if (!name) {
408 return false;
409 }
410
411 len = strnlen(name, LTTNG_NAME_MAX);
412 if (len == 0 || len == LTTNG_NAME_MAX) {
413 return false;
414 }
415
416 if (strchr(name, '/') || strchr(name, '.')) {
417 return false;
418 }
419
420 return true;
421 }
422
423 LTTNG_HIDDEN
424 enum lttng_trace_chunk_status lttng_trace_chunk_override_name(
425 struct lttng_trace_chunk *chunk, const char *name)
426
427 {
428 char *new_name;
429 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
430
431 if (!is_valid_chunk_name(name)) {
432 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
433 name ? : "NULL");
434 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
435 goto end;
436 }
437
438 pthread_mutex_lock(&chunk->lock);
439 if (!chunk->id.is_set) {
440 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
441 name);
442 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
443 goto end_unlock;
444 }
445 new_name = strdup(name);
446 if (!new_name) {
447 ERR("Failed to allocate new trace chunk name");
448 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
449 goto end_unlock;
450 }
451 free(chunk->name);
452 chunk->name = new_name;
453 chunk->name_overriden = true;
454 end_unlock:
455 pthread_mutex_unlock(&chunk->lock);
456 end:
457 return status;
458 }
459
460 LTTNG_HIDDEN
461 enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials(
462 struct lttng_trace_chunk *chunk,
463 struct lttng_credentials *credentials)
464 {
465 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
466
467 pthread_mutex_lock(&chunk->lock);
468 if (chunk->credentials.is_set) {
469 if (chunk->credentials.value.use_current_user) {
470 credentials->uid = geteuid();
471 credentials->gid = getegid();
472 } else {
473 *credentials = chunk->credentials.value.user;
474 }
475 } else {
476 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
477 }
478 pthread_mutex_unlock(&chunk->lock);
479 return status;
480 }
481
482 LTTNG_HIDDEN
483 enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials(
484 struct lttng_trace_chunk *chunk,
485 const struct lttng_credentials *user_credentials)
486 {
487 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
488 const struct chunk_credentials credentials = {
489 .user = *user_credentials,
490 .use_current_user = false,
491 };
492
493 pthread_mutex_lock(&chunk->lock);
494 if (chunk->credentials.is_set) {
495 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
496 goto end;
497 }
498 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
499 end:
500 pthread_mutex_unlock(&chunk->lock);
501 return status;
502 }
503
504 LTTNG_HIDDEN
505 enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials_current_user(
506 struct lttng_trace_chunk *chunk)
507 {
508 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
509 const struct chunk_credentials credentials = {
510 .use_current_user = true,
511 };
512
513 pthread_mutex_lock(&chunk->lock);
514 if (chunk->credentials.is_set) {
515 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
516 goto end;
517 }
518 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
519 end:
520 pthread_mutex_unlock(&chunk->lock);
521 return status;
522 }
523
524
525 LTTNG_HIDDEN
526 enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner(
527 struct lttng_trace_chunk *chunk,
528 struct lttng_directory_handle *session_output_directory)
529 {
530 int ret;
531 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
532 struct lttng_directory_handle chunk_directory_handle;
533
534 pthread_mutex_lock(&chunk->lock);
535 if (chunk->mode.is_set) {
536 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
537 goto end;
538 }
539 if (!chunk->credentials.is_set) {
540 /*
541 * Fatal error, credentials must be set before a
542 * directory is created.
543 */
544 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
545 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
546 goto end;
547 }
548
549 if (chunk->name) {
550 /*
551 * A nameless chunk does not need its own output directory.
552 * The session's output directory will be used.
553 */
554 ret = lttng_directory_handle_create_subdirectory_as_user(
555 session_output_directory,
556 chunk->name,
557 DIR_CREATION_MODE,
558 !chunk->credentials.value.use_current_user ?
559 &chunk->credentials.value.user : NULL);
560 if (ret) {
561 PERROR("Failed to create chunk output directory \"%s\"",
562 chunk->name);
563 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
564 goto end;
565 }
566 }
567 ret = lttng_directory_handle_init_from_handle(&chunk_directory_handle,
568 chunk->name,
569 session_output_directory);
570 if (ret) {
571 /* The function already logs on all error paths. */
572 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
573 goto end;
574 }
575 LTTNG_OPTIONAL_SET(&chunk->session_output_directory,
576 lttng_directory_handle_move(session_output_directory));
577 LTTNG_OPTIONAL_SET(&chunk->chunk_directory,
578 lttng_directory_handle_move(&chunk_directory_handle));
579 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER);
580 end:
581 pthread_mutex_unlock(&chunk->lock);
582 return status;
583 }
584
585 LTTNG_HIDDEN
586 enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user(
587 struct lttng_trace_chunk *chunk,
588 struct lttng_directory_handle *chunk_directory)
589 {
590 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
591
592 pthread_mutex_lock(&chunk->lock);
593 if (chunk->mode.is_set) {
594 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
595 goto end;
596 }
597 if (!chunk->credentials.is_set) {
598 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
599 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
600 goto end;
601 }
602 LTTNG_OPTIONAL_SET(&chunk->chunk_directory,
603 lttng_directory_handle_move(chunk_directory));
604 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER);
605 end:
606 pthread_mutex_unlock(&chunk->lock);
607 return status;
608 }
609
610 LTTNG_HIDDEN
611 enum lttng_trace_chunk_status lttng_trace_chunk_get_chunk_directory_handle(
612 struct lttng_trace_chunk *chunk,
613 const struct lttng_directory_handle **handle)
614 {
615 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
616
617 pthread_mutex_lock(&chunk->lock);
618 if (!chunk->chunk_directory.is_set) {
619 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
620 goto end;
621 }
622
623 *handle = &chunk->chunk_directory.value;
624 end:
625 pthread_mutex_unlock(&chunk->lock);
626 return status;
627 }
628
629 /* Add a top-level directory to the trace chunk if it was previously unknown. */
630 static
631 int add_top_level_directory_unique(struct lttng_trace_chunk *chunk,
632 const char *new_path)
633 {
634 int ret = 0;
635 bool found = false;
636 size_t i, count = lttng_dynamic_pointer_array_get_count(
637 &chunk->top_level_directories);
638 const char *new_path_separator_pos = strchr(new_path, '/');
639 const ptrdiff_t new_path_top_level_len = new_path_separator_pos ?
640 new_path_separator_pos - new_path : strlen(new_path);
641
642 for (i = 0; i < count; i++) {
643 const char *path = lttng_dynamic_pointer_array_get_pointer(
644 &chunk->top_level_directories, i);
645 const ptrdiff_t path_top_level_len = strlen(path);
646
647 if (path_top_level_len != new_path_top_level_len) {
648 continue;
649 }
650 if (!strncmp(path, new_path, path_top_level_len)) {
651 found = true;
652 break;
653 }
654 }
655
656 if (!found) {
657 char *copy = strndup(new_path, new_path_top_level_len);
658
659 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
660 new_path, chunk->name ? : "(unnamed)");
661 if (!copy) {
662 PERROR("Failed to copy path");
663 ret = -1;
664 goto end;
665 }
666 ret = lttng_dynamic_pointer_array_add_pointer(
667 &chunk->top_level_directories, copy);
668 if (ret) {
669 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
670 free(copy);
671 goto end;
672 }
673 }
674 end:
675 return ret;
676 }
677
678 LTTNG_HIDDEN
679 enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory(
680 struct lttng_trace_chunk *chunk,
681 const char *path)
682 {
683 int ret;
684 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
685
686 DBG("Creating trace chunk subdirectory \"%s\"", path);
687 pthread_mutex_lock(&chunk->lock);
688 if (!chunk->credentials.is_set) {
689 /*
690 * Fatal error, credentials must be set before a
691 * directory is created.
692 */
693 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
694 path);
695 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
696 goto end;
697 }
698 if (!chunk->mode.is_set ||
699 chunk->mode.value != TRACE_CHUNK_MODE_OWNER) {
700 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
701 path);
702 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
703 goto end;
704 }
705 if (!chunk->chunk_directory.is_set) {
706 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
707 path);
708 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
709 goto end;
710 }
711 if (*path == '/') {
712 ERR("Refusing to create absolute trace chunk directory \"%s\"",
713 path);
714 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
715 goto end;
716 }
717 ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
718 &chunk->chunk_directory.value, path,
719 DIR_CREATION_MODE,
720 chunk->credentials.value.use_current_user ?
721 NULL : &chunk->credentials.value.user);
722 if (ret) {
723 PERROR("Failed to create trace chunk subdirectory \"%s\"",
724 path);
725 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
726 goto end;
727 }
728 ret = add_top_level_directory_unique(chunk, path);
729 if (ret) {
730 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
731 goto end;
732 }
733 end:
734 pthread_mutex_unlock(&chunk->lock);
735 return status;
736 }
737
738 LTTNG_HIDDEN
739 enum lttng_trace_chunk_status lttng_trace_chunk_open_file(
740 struct lttng_trace_chunk *chunk, const char *file_path,
741 int flags, mode_t mode, int *out_fd)
742 {
743 int ret;
744 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
745
746 DBG("Opening trace chunk file \"%s\"", file_path);
747 pthread_mutex_lock(&chunk->lock);
748 if (!chunk->credentials.is_set) {
749 /*
750 * Fatal error, credentials must be set before a
751 * file is created.
752 */
753 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
754 file_path);
755 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
756 goto end;
757 }
758 if (!chunk->chunk_directory.is_set) {
759 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
760 file_path);
761 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
762 goto end;
763 }
764 ret = lttng_directory_handle_open_file_as_user(
765 &chunk->chunk_directory.value, file_path, flags, mode,
766 chunk->credentials.value.use_current_user ?
767 NULL : &chunk->credentials.value.user);
768 if (ret < 0) {
769 ERR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
770 file_path, flags, (int) mode);
771 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
772 goto end;
773 }
774 *out_fd = ret;
775 end:
776 pthread_mutex_unlock(&chunk->lock);
777 return status;
778 }
779
780 LTTNG_HIDDEN
781 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk,
782 const char *file_path)
783 {
784 int ret;
785 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
786
787 DBG("Unlinking trace chunk file \"%s\"", file_path);
788 pthread_mutex_lock(&chunk->lock);
789 if (!chunk->credentials.is_set) {
790 /*
791 * Fatal error, credentials must be set before a
792 * directory is created.
793 */
794 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
795 file_path);
796 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
797 goto end;
798 }
799 if (!chunk->chunk_directory.is_set) {
800 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
801 file_path);
802 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
803 goto end;
804 }
805 ret = lttng_directory_handle_unlink_file_as_user(
806 &chunk->chunk_directory.value, file_path,
807 chunk->credentials.value.use_current_user ?
808 NULL : &chunk->credentials.value.user);
809 if (ret < 0) {
810 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
811 goto end;
812 }
813 end:
814 pthread_mutex_unlock(&chunk->lock);
815 return status;
816 }
817
818 static
819 void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk)
820 {
821 int ret;
822 char *directory_to_rename = NULL;
823 bool free_directory_to_rename = false;
824 char *archived_chunk_name = NULL;
825 const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id);
826 const time_t creation_timestamp =
827 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation);
828 const time_t close_timestamp =
829 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close);
830 LTTNG_OPTIONAL(struct lttng_directory_handle) archived_chunks_directory;
831
832 if (!trace_chunk->mode.is_set ||
833 trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER ||
834 !trace_chunk->session_output_directory.is_set) {
835 /*
836 * This command doesn't need to run if the output is remote
837 * or if the trace chunk is not owned by this process.
838 */
839 goto end;
840 }
841
842 assert(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
843 assert(!trace_chunk->name_overriden);
844
845 /*
846 * The fist trace chunk of a session is directly output to the
847 * session's output folder. In this case, the top level directories
848 * must be moved to a temporary folder before that temporary directory
849 * is renamed to match the chunk's name.
850 */
851 if (chunk_id == 0) {
852 struct lttng_directory_handle temporary_rename_directory;
853 size_t i, count = lttng_dynamic_pointer_array_get_count(
854 &trace_chunk->top_level_directories);
855
856 ret = lttng_directory_handle_create_subdirectory_as_user(
857 &trace_chunk->session_output_directory.value,
858 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY,
859 DIR_CREATION_MODE,
860 !trace_chunk->credentials.value.use_current_user ?
861 &trace_chunk->credentials.value.user : NULL);
862 if (ret) {
863 PERROR("Failed to create temporary trace chunk rename directory \"%s\"",
864 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY);
865 }
866
867 ret = lttng_directory_handle_init_from_handle(&temporary_rename_directory,
868 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY,
869 &trace_chunk->session_output_directory.value);
870 if (ret) {
871 ERR("Failed to get handle to temporary trace chunk rename directory");
872 goto end;
873 }
874
875 for (i = 0; i < count; i++) {
876 const char *top_level_name =
877 lttng_dynamic_pointer_array_get_pointer(
878 &trace_chunk->top_level_directories, i);
879
880 ret = lttng_directory_handle_rename_as_user(
881 &trace_chunk->session_output_directory.value,
882 top_level_name,
883 &temporary_rename_directory,
884 top_level_name,
885 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
886 NULL :
887 &trace_chunk->credentials.value.user);
888 if (ret) {
889 PERROR("Failed to move \"%s\" to temporary trace chunk rename directory",
890 top_level_name);
891 lttng_directory_handle_fini(
892 &temporary_rename_directory);
893 goto end;
894 }
895 }
896 lttng_directory_handle_fini(&temporary_rename_directory);
897 directory_to_rename = DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY;
898 free_directory_to_rename = false;
899 } else {
900 directory_to_rename = generate_chunk_name(chunk_id,
901 creation_timestamp, NULL);
902 if (!directory_to_rename) {
903 ERR("Failed to generate initial trace chunk name while renaming trace chunk");
904 }
905 free_directory_to_rename = true;
906 }
907
908 archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp,
909 &close_timestamp);
910 if (!archived_chunk_name) {
911 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
912 goto end;
913 }
914
915 ret = lttng_directory_handle_create_subdirectory_as_user(
916 &trace_chunk->session_output_directory.value,
917 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
918 DIR_CREATION_MODE,
919 !trace_chunk->credentials.value.use_current_user ?
920 &trace_chunk->credentials.value.user :
921 NULL);
922 if (ret) {
923 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
924 "\" directory for archived trace chunks");
925 goto end;
926 }
927
928 ret = lttng_directory_handle_init_from_handle(
929 &archived_chunks_directory.value,
930 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
931 &trace_chunk->session_output_directory.value);
932 if (ret) {
933 PERROR("Failed to get handle to archived trace chunks directory");
934 goto end;
935 }
936 archived_chunks_directory.is_set = true;
937
938 ret = lttng_directory_handle_rename_as_user(
939 &trace_chunk->session_output_directory.value,
940 directory_to_rename,
941 &archived_chunks_directory.value,
942 archived_chunk_name,
943 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
944 NULL :
945 &trace_chunk->credentials.value.user);
946 if (ret) {
947 PERROR("Failed to rename folder \"%s\" to \"%s\"",
948 directory_to_rename, archived_chunk_name);
949 }
950
951 end:
952 if (archived_chunks_directory.is_set) {
953 lttng_directory_handle_fini(&archived_chunks_directory.value);
954 }
955 free(archived_chunk_name);
956 if (free_directory_to_rename) {
957 free(directory_to_rename);
958 }
959 }
960
961 LTTNG_HIDDEN
962 enum lttng_trace_chunk_status lttng_trace_chunk_get_close_command(
963 struct lttng_trace_chunk *chunk,
964 enum lttng_trace_chunk_command_type *command_type)
965 {
966 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
967
968 pthread_mutex_lock(&chunk->lock);
969 if (chunk->close_command.is_set) {
970 *command_type = chunk->close_command.value;
971 status = LTTNG_TRACE_CHUNK_STATUS_OK;
972 } else {
973 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
974 }
975 pthread_mutex_unlock(&chunk->lock);
976 return status;
977 }
978
979 LTTNG_HIDDEN
980 enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
981 struct lttng_trace_chunk *chunk,
982 enum lttng_trace_chunk_command_type close_command)
983 {
984 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
985
986 if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
987 close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
988 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
989 goto end_unlock;
990 }
991
992 pthread_mutex_lock(&chunk->lock);
993 if (chunk->close_command.is_set) {
994 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
995 close_command_names[chunk->close_command.value],
996 close_command_names[close_command]);
997 } else {
998 DBG("Setting trace chunk close command to \"%s\"",
999 close_command_names[close_command]);
1000 }
1001 LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
1002 pthread_mutex_unlock(&chunk->lock);
1003 end_unlock:
1004 return status;
1005 }
1006
1007 LTTNG_HIDDEN
1008 const char *lttng_trace_chunk_command_type_get_name(
1009 enum lttng_trace_chunk_command_type command)
1010 {
1011 switch (command) {
1012 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
1013 return "move to completed trace chunk folder";
1014 default:
1015 abort();
1016 }
1017 }
1018
1019 LTTNG_HIDDEN
1020 bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
1021 {
1022 return urcu_ref_get_unless_zero(&chunk->ref);
1023 }
1024
1025 static
1026 void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
1027 {
1028 struct lttng_trace_chunk_registry_element *element =
1029 container_of(node, typeof(*element), rcu_node);
1030
1031 lttng_trace_chunk_fini(&element->chunk);
1032 free(element);
1033 }
1034
1035 static
1036 void lttng_trace_chunk_release(struct urcu_ref *ref)
1037 {
1038 struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk),
1039 ref);
1040
1041 if (chunk->close_command.is_set) {
1042 close_command_funcs[chunk->close_command.value](chunk);
1043 }
1044
1045 if (chunk->in_registry_element) {
1046 struct lttng_trace_chunk_registry_element *element;
1047
1048 element = container_of(chunk, typeof(*element), chunk);
1049 if (element->registry) {
1050 rcu_read_lock();
1051 cds_lfht_del(element->registry->ht,
1052 &element->trace_chunk_registry_ht_node);
1053 rcu_read_unlock();
1054 call_rcu(&element->rcu_node,
1055 free_lttng_trace_chunk_registry_element);
1056 } else {
1057 /* Never published, can be free'd immediately. */
1058 free_lttng_trace_chunk_registry_element(
1059 &element->rcu_node);
1060 }
1061 } else {
1062 /* Not RCU-protected, free immediately. */
1063 lttng_trace_chunk_fini(chunk);
1064 free(chunk);
1065 }
1066 }
1067
1068 LTTNG_HIDDEN
1069 void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
1070 {
1071 if (!chunk) {
1072 return;
1073 }
1074 assert(chunk->ref.refcount);
1075 urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
1076 }
1077
1078 LTTNG_HIDDEN
1079 struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void)
1080 {
1081 struct lttng_trace_chunk_registry *registry;
1082
1083 registry = zmalloc(sizeof(*registry));
1084 if (!registry) {
1085 goto end;
1086 }
1087
1088 registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
1089 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
1090 if (!registry->ht) {
1091 goto error;
1092 }
1093 end:
1094 return registry;
1095 error:
1096 lttng_trace_chunk_registry_destroy(registry);
1097 goto end;
1098 }
1099
1100 LTTNG_HIDDEN
1101 void lttng_trace_chunk_registry_destroy(
1102 struct lttng_trace_chunk_registry *registry)
1103 {
1104 if (!registry) {
1105 return;
1106 }
1107 if (registry->ht) {
1108 int ret = cds_lfht_destroy(registry->ht, NULL);
1109 assert(!ret);
1110 }
1111 free(registry);
1112 }
1113
1114 static
1115 struct lttng_trace_chunk_registry_element *
1116 lttng_trace_chunk_registry_element_create_from_chunk(
1117 struct lttng_trace_chunk *chunk, uint64_t session_id)
1118 {
1119 struct lttng_trace_chunk_registry_element *element =
1120 zmalloc(sizeof(*element));
1121
1122 if (!element) {
1123 goto end;
1124 }
1125 cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
1126 element->session_id = session_id;
1127
1128 element->chunk = *chunk;
1129 lttng_trace_chunk_init(&element->chunk);
1130 if (chunk->session_output_directory.is_set) {
1131 element->chunk.session_output_directory.value =
1132 lttng_directory_handle_move(
1133 &chunk->session_output_directory.value);
1134 }
1135 if (chunk->chunk_directory.is_set) {
1136 element->chunk.chunk_directory.value =
1137 lttng_directory_handle_move(
1138 &chunk->chunk_directory.value);
1139 }
1140 /*
1141 * The original chunk becomes invalid; the name attribute is transferred
1142 * to the new chunk instance.
1143 */
1144 chunk->name = NULL;
1145 element->chunk.in_registry_element = true;
1146 end:
1147 return element;
1148 }
1149
1150 LTTNG_HIDDEN
1151 struct lttng_trace_chunk *
1152 lttng_trace_chunk_registry_publish_chunk(
1153 struct lttng_trace_chunk_registry *registry,
1154 uint64_t session_id, struct lttng_trace_chunk *chunk)
1155 {
1156 struct lttng_trace_chunk_registry_element *element;
1157 unsigned long element_hash;
1158
1159 pthread_mutex_lock(&chunk->lock);
1160 element = lttng_trace_chunk_registry_element_create_from_chunk(chunk,
1161 session_id);
1162 pthread_mutex_unlock(&chunk->lock);
1163 if (!element) {
1164 goto end;
1165 }
1166 /*
1167 * chunk is now invalid, the only valid operation is a 'put' from the
1168 * caller.
1169 */
1170 chunk = NULL;
1171 element_hash = lttng_trace_chunk_registry_element_hash(element);
1172
1173 rcu_read_lock();
1174 while (1) {
1175 struct cds_lfht_node *published_node;
1176 struct lttng_trace_chunk *published_chunk;
1177 struct lttng_trace_chunk_registry_element *published_element;
1178
1179 published_node = cds_lfht_add_unique(registry->ht,
1180 element_hash,
1181 lttng_trace_chunk_registry_element_match,
1182 element,
1183 &element->trace_chunk_registry_ht_node);
1184 if (published_node == &element->trace_chunk_registry_ht_node) {
1185 /* Successfully published the new element. */
1186 element->registry = registry;
1187 /* Acquire a reference for the caller. */
1188 if (lttng_trace_chunk_get(&element->chunk)) {
1189 break;
1190 } else {
1191 /*
1192 * Another thread concurrently unpublished the
1193 * trace chunk. This is currently unexpected.
1194 *
1195 * Re-attempt to publish.
1196 */
1197 ERR("Attemp to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1198 continue;
1199 }
1200 }
1201
1202 /*
1203 * An equivalent trace chunk was published before this trace
1204 * chunk. Attempt to acquire a reference to the one that was
1205 * already published and release the reference to the copy we
1206 * created if successful.
1207 */
1208 published_element = container_of(published_node,
1209 typeof(*published_element),
1210 trace_chunk_registry_ht_node);
1211 published_chunk = &published_element->chunk;
1212 if (lttng_trace_chunk_get(published_chunk)) {
1213 lttng_trace_chunk_put(&element->chunk);
1214 element = published_element;
1215 break;
1216 }
1217 /*
1218 * A reference to the previously published trace chunk could not
1219 * be acquired. Hence, retry to publish our copy of the trace
1220 * chunk.
1221 */
1222 }
1223 rcu_read_unlock();
1224 end:
1225 return element ? &element->chunk : NULL;
1226 }
1227
1228 /*
1229 * Note that the caller must be registered as an RCU thread.
1230 * However, it does not need to hold the RCU read lock. The RCU read lock is
1231 * acquired to perform the look-up in the registry's hash table and held until
1232 * after a reference to the "found" trace chunk is acquired.
1233 *
1234 * IOW, holding a reference guarantees the existence of the object for the
1235 * caller.
1236 */
1237 static
1238 struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
1239 const struct lttng_trace_chunk_registry *registry,
1240 uint64_t session_id, uint64_t *chunk_id)
1241 {
1242 const struct lttng_trace_chunk_registry_element target_element = {
1243 .chunk.id.is_set = !!chunk_id,
1244 .chunk.id.value = chunk_id ? *chunk_id : 0,
1245 .session_id = session_id,
1246 };
1247 const unsigned long element_hash =
1248 lttng_trace_chunk_registry_element_hash(
1249 &target_element);
1250 struct cds_lfht_node *published_node;
1251 struct lttng_trace_chunk_registry_element *published_element;
1252 struct lttng_trace_chunk *published_chunk = NULL;
1253 struct cds_lfht_iter iter;
1254
1255 rcu_read_lock();
1256 cds_lfht_lookup(registry->ht,
1257 element_hash,
1258 lttng_trace_chunk_registry_element_match,
1259 &target_element,
1260 &iter);
1261 published_node = cds_lfht_iter_get_node(&iter);
1262 if (!published_node) {
1263 goto end;
1264 }
1265
1266 published_element = container_of(published_node,
1267 typeof(*published_element),
1268 trace_chunk_registry_ht_node);
1269 if (lttng_trace_chunk_get(&published_element->chunk)) {
1270 published_chunk = &published_element->chunk;
1271 }
1272 end:
1273 rcu_read_unlock();
1274 return published_chunk;
1275 }
1276
1277 LTTNG_HIDDEN
1278 struct lttng_trace_chunk *
1279 lttng_trace_chunk_registry_find_chunk(
1280 const struct lttng_trace_chunk_registry *registry,
1281 uint64_t session_id, uint64_t chunk_id)
1282 {
1283 return _lttng_trace_chunk_registry_find_chunk(registry,
1284 session_id, &chunk_id);
1285 }
1286
1287 LTTNG_HIDDEN
1288 struct lttng_trace_chunk *
1289 lttng_trace_chunk_registry_find_anonymous_chunk(
1290 const struct lttng_trace_chunk_registry *registry,
1291 uint64_t session_id)
1292 {
1293 return _lttng_trace_chunk_registry_find_chunk(registry,
1294 session_id, NULL);
1295 }
This page took 0.07726 seconds and 6 git commands to generate.