Fix: validate that session, host and basepath are legal
[lttng-tools.git] / src / common / trace-chunk.c
CommitLineData
2c5ff4e4
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;
913a542b 80 bool name_overridden;
2c5ff4e4
JG
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);
93bed9fe 212 lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free);
2c5ff4e4
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;
93bed9fe 227 lttng_dynamic_pointer_array_reset(&chunk->top_level_directories);
2c5ff4e4
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,
913a542b 384 bool *name_overridden)
2c5ff4e4
JG
385{
386 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
387
388 pthread_mutex_lock(&chunk->lock);
913a542b
MD
389 if (name_overridden) {
390 *name_overridden = chunk->name_overridden;
2c5ff4e4
JG
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
84fa4db5
JG
402static
403bool 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
2c5ff4e4
JG
423LTTNG_HIDDEN
424enum 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
84fa4db5 431 if (!is_valid_chunk_name(name)) {
2c5ff4e4
JG
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;
913a542b 453 chunk->name_overridden = true;
2c5ff4e4
JG
454end_unlock:
455 pthread_mutex_unlock(&chunk->lock);
456end:
457 return status;
458}
459
460LTTNG_HIDDEN
461enum 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
482LTTNG_HIDDEN
483enum 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);
499end:
500 pthread_mutex_unlock(&chunk->lock);
501 return status;
502}
503
504LTTNG_HIDDEN
505enum 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);
519end:
520 pthread_mutex_unlock(&chunk->lock);
521 return status;
522}
523
524
525LTTNG_HIDDEN
526enum 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);
580end:
581 pthread_mutex_unlock(&chunk->lock);
582 return status;
583}
584
585LTTNG_HIDDEN
586enum 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);
605end:
606 pthread_mutex_unlock(&chunk->lock);
607 return status;
608}
609
610LTTNG_HIDDEN
611enum 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;
624end:
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. */
630static
631int 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 }
674end:
675 return ret;
676}
677
678LTTNG_HIDDEN
679enum 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 }
733end:
734 pthread_mutex_unlock(&chunk->lock);
735 return status;
736}
737
738LTTNG_HIDDEN
739enum 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) {
d2956687
JG
769 ERR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
770 file_path, flags, (int) mode);
2c5ff4e4
JG
771 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
772 goto end;
773 }
774 *out_fd = ret;
775end:
776 pthread_mutex_unlock(&chunk->lock);
777 return status;
778}
779
780LTTNG_HIDDEN
781int 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 }
813end:
814 pthread_mutex_unlock(&chunk->lock);
815 return status;
816}
817
818static
819void 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;
2c5ff4e4
JG
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);
c35f9726 830 LTTNG_OPTIONAL(struct lttng_directory_handle) archived_chunks_directory = {};
2c5ff4e4 831
bbc4768c
JG
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
2c5ff4e4 842 assert(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
913a542b 843 assert(!trace_chunk->name_overridden);
2c5ff4e4
JG
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++) {
2c5ff4e4
JG
876 const char *top_level_name =
877 lttng_dynamic_pointer_array_get_pointer(
878 &trace_chunk->top_level_directories, i);
879
9de831f8
JG
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);
2c5ff4e4
JG
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
9de831f8
JG
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);
2c5ff4e4
JG
946 if (ret) {
947 PERROR("Failed to rename folder \"%s\" to \"%s\"",
948 directory_to_rename, archived_chunk_name);
949 }
950
951end:
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
bbc4768c
JG
961LTTNG_HIDDEN
962enum 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
2c5ff4e4
JG
979LTTNG_HIDDEN
980enum 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);
1003end_unlock:
1004 return status;
1005}
1006
bbc4768c
JG
1007LTTNG_HIDDEN
1008const 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
2c5ff4e4
JG
1019LTTNG_HIDDEN
1020bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
1021{
1022 return urcu_ref_get_unless_zero(&chunk->ref);
1023}
1024
1025static
1026void 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
1035static
1036void 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
1068LTTNG_HIDDEN
1069void 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
1078LTTNG_HIDDEN
1079struct 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 }
1093end:
1094 return registry;
1095error:
1096 lttng_trace_chunk_registry_destroy(registry);
1097 goto end;
1098}
1099
1100LTTNG_HIDDEN
1101void 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
1114static
1115struct lttng_trace_chunk_registry_element *
1116lttng_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;
1146end:
1147 return element;
1148}
1149
1150LTTNG_HIDDEN
1151struct lttng_trace_chunk *
1152lttng_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();
1224end:
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 */
1237static
1238struct 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 }
1272end:
1273 rcu_read_unlock();
1274 return published_chunk;
1275}
1276
1277LTTNG_HIDDEN
1278struct lttng_trace_chunk *
1279lttng_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
1287LTTNG_HIDDEN
1288struct lttng_trace_chunk *
1289lttng_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.070787 seconds and 4 git commands to generate.