488d7ebc4ffffcf19938cdb5f1da4036311417e6
[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 assert(trace_chunk->mode.is_set);
833 assert(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
834 assert(!trace_chunk->name_overriden);
835
836 /*
837 * The fist trace chunk of a session is directly output to the
838 * session's output folder. In this case, the top level directories
839 * must be moved to a temporary folder before that temporary directory
840 * is renamed to match the chunk's name.
841 */
842 if (chunk_id == 0) {
843 struct lttng_directory_handle temporary_rename_directory;
844 size_t i, count = lttng_dynamic_pointer_array_get_count(
845 &trace_chunk->top_level_directories);
846
847 ret = lttng_directory_handle_create_subdirectory_as_user(
848 &trace_chunk->session_output_directory.value,
849 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY,
850 DIR_CREATION_MODE,
851 !trace_chunk->credentials.value.use_current_user ?
852 &trace_chunk->credentials.value.user : NULL);
853 if (ret) {
854 PERROR("Failed to create temporary trace chunk rename directory \"%s\"",
855 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY);
856 }
857
858 ret = lttng_directory_handle_init_from_handle(&temporary_rename_directory,
859 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY,
860 &trace_chunk->session_output_directory.value);
861 if (ret) {
862 ERR("Failed to get handle to temporary trace chunk rename directory");
863 goto end;
864 }
865
866 for (i = 0; i < count; i++) {
867 const char *top_level_name =
868 lttng_dynamic_pointer_array_get_pointer(
869 &trace_chunk->top_level_directories, i);
870
871 ret = lttng_directory_handle_rename_as_user(
872 &trace_chunk->session_output_directory.value,
873 top_level_name,
874 &temporary_rename_directory,
875 top_level_name,
876 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
877 NULL :
878 &trace_chunk->credentials.value.user);
879 if (ret) {
880 PERROR("Failed to move \"%s\" to temporary trace chunk rename directory",
881 top_level_name);
882 lttng_directory_handle_fini(
883 &temporary_rename_directory);
884 goto end;
885 }
886 }
887 lttng_directory_handle_fini(&temporary_rename_directory);
888 directory_to_rename = DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY;
889 free_directory_to_rename = false;
890 } else {
891 directory_to_rename = generate_chunk_name(chunk_id,
892 creation_timestamp, NULL);
893 if (!directory_to_rename) {
894 ERR("Failed to generate initial trace chunk name while renaming trace chunk");
895 }
896 free_directory_to_rename = true;
897 }
898
899 archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp,
900 &close_timestamp);
901 if (!archived_chunk_name) {
902 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
903 goto end;
904 }
905
906 ret = lttng_directory_handle_create_subdirectory_as_user(
907 &trace_chunk->session_output_directory.value,
908 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
909 DIR_CREATION_MODE,
910 !trace_chunk->credentials.value.use_current_user ?
911 &trace_chunk->credentials.value.user :
912 NULL);
913 if (ret) {
914 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
915 "\" directory for archived trace chunks");
916 goto end;
917 }
918
919 ret = lttng_directory_handle_init_from_handle(
920 &archived_chunks_directory.value,
921 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
922 &trace_chunk->session_output_directory.value);
923 if (ret) {
924 PERROR("Failed to get handle to archived trace chunks directory");
925 goto end;
926 }
927 archived_chunks_directory.is_set = true;
928
929 ret = lttng_directory_handle_rename_as_user(
930 &trace_chunk->session_output_directory.value,
931 directory_to_rename,
932 &archived_chunks_directory.value,
933 archived_chunk_name,
934 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
935 NULL :
936 &trace_chunk->credentials.value.user);
937 if (ret) {
938 PERROR("Failed to rename folder \"%s\" to \"%s\"",
939 directory_to_rename, archived_chunk_name);
940 }
941
942 end:
943 if (archived_chunks_directory.is_set) {
944 lttng_directory_handle_fini(&archived_chunks_directory.value);
945 }
946 free(archived_chunk_name);
947 if (free_directory_to_rename) {
948 free(directory_to_rename);
949 }
950 }
951
952 LTTNG_HIDDEN
953 enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
954 struct lttng_trace_chunk *chunk,
955 enum lttng_trace_chunk_command_type close_command)
956 {
957 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
958
959 if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
960 close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
961 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
962 goto end_unlock;
963 }
964
965 pthread_mutex_lock(&chunk->lock);
966 if (chunk->close_command.is_set) {
967 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
968 close_command_names[chunk->close_command.value],
969 close_command_names[close_command]);
970 } else {
971 DBG("Setting trace chunk close command to \"%s\"",
972 close_command_names[close_command]);
973 }
974 LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
975 pthread_mutex_unlock(&chunk->lock);
976 end_unlock:
977 return status;
978 }
979
980 LTTNG_HIDDEN
981 bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
982 {
983 return urcu_ref_get_unless_zero(&chunk->ref);
984 }
985
986 static
987 void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
988 {
989 struct lttng_trace_chunk_registry_element *element =
990 container_of(node, typeof(*element), rcu_node);
991
992 lttng_trace_chunk_fini(&element->chunk);
993 free(element);
994 }
995
996 static
997 void lttng_trace_chunk_release(struct urcu_ref *ref)
998 {
999 struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk),
1000 ref);
1001
1002 if (chunk->close_command.is_set) {
1003 close_command_funcs[chunk->close_command.value](chunk);
1004 }
1005
1006 if (chunk->in_registry_element) {
1007 struct lttng_trace_chunk_registry_element *element;
1008
1009 element = container_of(chunk, typeof(*element), chunk);
1010 if (element->registry) {
1011 rcu_read_lock();
1012 cds_lfht_del(element->registry->ht,
1013 &element->trace_chunk_registry_ht_node);
1014 rcu_read_unlock();
1015 call_rcu(&element->rcu_node,
1016 free_lttng_trace_chunk_registry_element);
1017 } else {
1018 /* Never published, can be free'd immediately. */
1019 free_lttng_trace_chunk_registry_element(
1020 &element->rcu_node);
1021 }
1022 } else {
1023 /* Not RCU-protected, free immediately. */
1024 lttng_trace_chunk_fini(chunk);
1025 free(chunk);
1026 }
1027 }
1028
1029 LTTNG_HIDDEN
1030 void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
1031 {
1032 if (!chunk) {
1033 return;
1034 }
1035 assert(chunk->ref.refcount);
1036 urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
1037 }
1038
1039 LTTNG_HIDDEN
1040 struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void)
1041 {
1042 struct lttng_trace_chunk_registry *registry;
1043
1044 registry = zmalloc(sizeof(*registry));
1045 if (!registry) {
1046 goto end;
1047 }
1048
1049 registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
1050 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
1051 if (!registry->ht) {
1052 goto error;
1053 }
1054 end:
1055 return registry;
1056 error:
1057 lttng_trace_chunk_registry_destroy(registry);
1058 goto end;
1059 }
1060
1061 LTTNG_HIDDEN
1062 void lttng_trace_chunk_registry_destroy(
1063 struct lttng_trace_chunk_registry *registry)
1064 {
1065 if (!registry) {
1066 return;
1067 }
1068 if (registry->ht) {
1069 int ret = cds_lfht_destroy(registry->ht, NULL);
1070 assert(!ret);
1071 }
1072 free(registry);
1073 }
1074
1075 static
1076 struct lttng_trace_chunk_registry_element *
1077 lttng_trace_chunk_registry_element_create_from_chunk(
1078 struct lttng_trace_chunk *chunk, uint64_t session_id)
1079 {
1080 struct lttng_trace_chunk_registry_element *element =
1081 zmalloc(sizeof(*element));
1082
1083 if (!element) {
1084 goto end;
1085 }
1086 cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
1087 element->session_id = session_id;
1088
1089 element->chunk = *chunk;
1090 lttng_trace_chunk_init(&element->chunk);
1091 if (chunk->session_output_directory.is_set) {
1092 element->chunk.session_output_directory.value =
1093 lttng_directory_handle_move(
1094 &chunk->session_output_directory.value);
1095 }
1096 if (chunk->chunk_directory.is_set) {
1097 element->chunk.chunk_directory.value =
1098 lttng_directory_handle_move(
1099 &chunk->chunk_directory.value);
1100 }
1101 /*
1102 * The original chunk becomes invalid; the name attribute is transferred
1103 * to the new chunk instance.
1104 */
1105 chunk->name = NULL;
1106 element->chunk.in_registry_element = true;
1107 end:
1108 return element;
1109 }
1110
1111 LTTNG_HIDDEN
1112 struct lttng_trace_chunk *
1113 lttng_trace_chunk_registry_publish_chunk(
1114 struct lttng_trace_chunk_registry *registry,
1115 uint64_t session_id, struct lttng_trace_chunk *chunk)
1116 {
1117 struct lttng_trace_chunk_registry_element *element;
1118 unsigned long element_hash;
1119
1120 pthread_mutex_lock(&chunk->lock);
1121 element = lttng_trace_chunk_registry_element_create_from_chunk(chunk,
1122 session_id);
1123 pthread_mutex_unlock(&chunk->lock);
1124 if (!element) {
1125 goto end;
1126 }
1127 /*
1128 * chunk is now invalid, the only valid operation is a 'put' from the
1129 * caller.
1130 */
1131 chunk = NULL;
1132 element_hash = lttng_trace_chunk_registry_element_hash(element);
1133
1134 rcu_read_lock();
1135 while (1) {
1136 struct cds_lfht_node *published_node;
1137 struct lttng_trace_chunk *published_chunk;
1138 struct lttng_trace_chunk_registry_element *published_element;
1139
1140 published_node = cds_lfht_add_unique(registry->ht,
1141 element_hash,
1142 lttng_trace_chunk_registry_element_match,
1143 element,
1144 &element->trace_chunk_registry_ht_node);
1145 if (published_node == &element->trace_chunk_registry_ht_node) {
1146 /* Successfully published the new element. */
1147 element->registry = registry;
1148 /* Acquire a reference for the caller. */
1149 if (lttng_trace_chunk_get(&element->chunk)) {
1150 break;
1151 } else {
1152 /*
1153 * Another thread concurrently unpublished the
1154 * trace chunk. This is currently unexpected.
1155 *
1156 * Re-attempt to publish.
1157 */
1158 ERR("Attemp to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1159 continue;
1160 }
1161 }
1162
1163 /*
1164 * An equivalent trace chunk was published before this trace
1165 * chunk. Attempt to acquire a reference to the one that was
1166 * already published and release the reference to the copy we
1167 * created if successful.
1168 */
1169 published_element = container_of(published_node,
1170 typeof(*published_element),
1171 trace_chunk_registry_ht_node);
1172 published_chunk = &published_element->chunk;
1173 if (lttng_trace_chunk_get(published_chunk)) {
1174 lttng_trace_chunk_put(&element->chunk);
1175 element = published_element;
1176 break;
1177 }
1178 /*
1179 * A reference to the previously published trace chunk could not
1180 * be acquired. Hence, retry to publish our copy of the trace
1181 * chunk.
1182 */
1183 }
1184 rcu_read_unlock();
1185 end:
1186 return element ? &element->chunk : NULL;
1187 }
1188
1189 /*
1190 * Note that the caller must be registered as an RCU thread.
1191 * However, it does not need to hold the RCU read lock. The RCU read lock is
1192 * acquired to perform the look-up in the registry's hash table and held until
1193 * after a reference to the "found" trace chunk is acquired.
1194 *
1195 * IOW, holding a reference guarantees the existence of the object for the
1196 * caller.
1197 */
1198 static
1199 struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
1200 const struct lttng_trace_chunk_registry *registry,
1201 uint64_t session_id, uint64_t *chunk_id)
1202 {
1203 const struct lttng_trace_chunk_registry_element target_element = {
1204 .chunk.id.is_set = !!chunk_id,
1205 .chunk.id.value = chunk_id ? *chunk_id : 0,
1206 .session_id = session_id,
1207 };
1208 const unsigned long element_hash =
1209 lttng_trace_chunk_registry_element_hash(
1210 &target_element);
1211 struct cds_lfht_node *published_node;
1212 struct lttng_trace_chunk_registry_element *published_element;
1213 struct lttng_trace_chunk *published_chunk = NULL;
1214 struct cds_lfht_iter iter;
1215
1216 rcu_read_lock();
1217 cds_lfht_lookup(registry->ht,
1218 element_hash,
1219 lttng_trace_chunk_registry_element_match,
1220 &target_element,
1221 &iter);
1222 published_node = cds_lfht_iter_get_node(&iter);
1223 if (!published_node) {
1224 goto end;
1225 }
1226
1227 published_element = container_of(published_node,
1228 typeof(*published_element),
1229 trace_chunk_registry_ht_node);
1230 if (lttng_trace_chunk_get(&published_element->chunk)) {
1231 published_chunk = &published_element->chunk;
1232 }
1233 end:
1234 rcu_read_unlock();
1235 return published_chunk;
1236 }
1237
1238 LTTNG_HIDDEN
1239 struct lttng_trace_chunk *
1240 lttng_trace_chunk_registry_find_chunk(
1241 const struct lttng_trace_chunk_registry *registry,
1242 uint64_t session_id, uint64_t chunk_id)
1243 {
1244 return _lttng_trace_chunk_registry_find_chunk(registry,
1245 session_id, &chunk_id);
1246 }
1247
1248 LTTNG_HIDDEN
1249 struct lttng_trace_chunk *
1250 lttng_trace_chunk_registry_find_anonymous_chunk(
1251 const struct lttng_trace_chunk_registry *registry,
1252 uint64_t session_id)
1253 {
1254 return _lttng_trace_chunk_registry_find_chunk(registry,
1255 session_id, NULL);
1256 }
This page took 0.057487 seconds and 3 git commands to generate.