trace-chunk: Introduce chunk "path", relayd session "ongoing_rotation", sessiond...
[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 */
a7ceb342 58typedef int (*chunk_command)(struct lttng_trace_chunk *trace_chunk);
2c5ff4e4
JG
59
60/* Move a completed trace chunk to the 'completed' trace archive folder. */
61static
a7ceb342
MD
62int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk *trace_chunk);
63static
64enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock(
65 struct lttng_trace_chunk *chunk, const char *path);
2c5ff4e4
JG
66
67struct chunk_credentials {
68 bool use_current_user;
69 struct lttng_credentials user;
70};
71
a7ceb342
MD
72/*
73 * NOTE: Make sure to update:
74 * - lttng_trace_chunk_copy(),
75 * - lttng_trace_chunk_registry_element_create_from_chunk()
76 * if you modify this structure.
77 */
2c5ff4e4
JG
78struct lttng_trace_chunk {
79 pthread_mutex_t lock;
80 struct urcu_ref ref;
81 LTTNG_OPTIONAL(enum trace_chunk_mode) mode;
82 /*
83 * First-level directories created within the trace chunk.
84 * Elements are of type 'char *'.
1a414e3a
JG
85 *
86 * Only used by _owner_ mode chunks.
2c5ff4e4
JG
87 */
88 struct lttng_dynamic_pointer_array top_level_directories;
6cb32e5a
MD
89 /*
90 * All files contained within the trace chunk.
91 * Array of paths (char *).
92 */
93 struct lttng_dynamic_pointer_array files;
2c5ff4e4
JG
94 /* Is contained within an lttng_trace_chunk_registry_element? */
95 bool in_registry_element;
913a542b 96 bool name_overridden;
2c5ff4e4 97 char *name;
a7ceb342 98 char *path;
2c5ff4e4
JG
99 /* An unset id means the chunk is anonymous. */
100 LTTNG_OPTIONAL(uint64_t) id;
101 LTTNG_OPTIONAL(time_t) timestamp_creation;
102 LTTNG_OPTIONAL(time_t) timestamp_close;
103 LTTNG_OPTIONAL(struct chunk_credentials) credentials;
cbf53d23
JG
104 struct lttng_directory_handle *session_output_directory;
105 struct lttng_directory_handle *chunk_directory;
2c5ff4e4
JG
106 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type) close_command;
107};
108
109/* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
110struct lttng_trace_chunk_registry_element {
2c5ff4e4 111 struct lttng_trace_chunk chunk;
1f2292f6 112 uint64_t session_id;
2c5ff4e4
JG
113 /* Weak and only set when added. */
114 struct lttng_trace_chunk_registry *registry;
115 struct cds_lfht_node trace_chunk_registry_ht_node;
116 /* call_rcu delayed reclaim. */
117 struct rcu_head rcu_node;
118};
119
120struct lttng_trace_chunk_registry {
121 struct cds_lfht *ht;
122};
123
606846ba
JG
124static const
125char *close_command_names[] = {
2c5ff4e4
JG
126 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
127 "move to completed chunk folder",
343defc2
MD
128 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION] =
129 "no operation",
130 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE] =
131 "delete",
2c5ff4e4
JG
132};
133
606846ba 134static const
a7ceb342 135chunk_command close_command_post_release_funcs[] = {
2c5ff4e4 136 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
a7ceb342 137 lttng_trace_chunk_move_to_completed_post_release,
2c5ff4e4
JG
138};
139
140static
141bool lttng_trace_chunk_registry_element_equals(
142 const struct lttng_trace_chunk_registry_element *a,
143 const struct lttng_trace_chunk_registry_element *b)
144{
145 if (a->session_id != b->session_id) {
146 goto not_equal;
147 }
148 if (a->chunk.id.is_set != b->chunk.id.is_set) {
149 goto not_equal;
150 }
151 if (a->chunk.id.is_set && a->chunk.id.value != b->chunk.id.value) {
152 goto not_equal;
153 }
154 return true;
155not_equal:
156 return false;
157}
158
159static
160int lttng_trace_chunk_registry_element_match(struct cds_lfht_node *node,
161 const void *key)
162{
163 const struct lttng_trace_chunk_registry_element *element_a, *element_b;
164
165 element_a = (const struct lttng_trace_chunk_registry_element *) key;
166 element_b = caa_container_of(node, typeof(*element_b),
167 trace_chunk_registry_ht_node);
168 return lttng_trace_chunk_registry_element_equals(element_a, element_b);
169}
170
171static
172unsigned long lttng_trace_chunk_registry_element_hash(
173 const struct lttng_trace_chunk_registry_element *element)
174{
175 unsigned long hash = hash_key_u64(&element->session_id,
176 lttng_ht_seed);
177
178 if (element->chunk.id.is_set) {
179 hash ^= hash_key_u64(&element->chunk.id.value, lttng_ht_seed);
180 }
181
182 return hash;
183}
184
185static
186char *generate_chunk_name(uint64_t chunk_id, time_t creation_timestamp,
187 const time_t *close_timestamp)
188{
189 int ret = 0;
190 char *new_name= NULL;
6e7e5048
JG
191 char start_datetime[ISO8601_STR_LEN] = {};
192 /* Add 1 for a '-' prefix. */
193 char end_datetime_suffix[ISO8601_STR_LEN + 1] = {};
2c5ff4e4
JG
194
195 ret = time_to_iso8601_str(
196 creation_timestamp,
197 start_datetime, sizeof(start_datetime));
198 if (ret) {
199 ERR("Failed to format trace chunk start date time");
200 goto error;
201 }
202 if (close_timestamp) {
203 *end_datetime_suffix = '-';
204 ret = time_to_iso8601_str(
205 *close_timestamp,
206 end_datetime_suffix + 1,
6e7e5048 207 sizeof(end_datetime_suffix) - 1);
2c5ff4e4
JG
208 if (ret) {
209 ERR("Failed to format trace chunk end date time");
210 goto error;
211 }
212 }
213 new_name = zmalloc(GENERATED_CHUNK_NAME_LEN);
214 if (!new_name) {
215 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
216 goto error;
217 }
218 ret = snprintf(new_name, GENERATED_CHUNK_NAME_LEN, "%s%s-%" PRIu64,
219 start_datetime, end_datetime_suffix, chunk_id);
220 if (ret >= GENERATED_CHUNK_NAME_LEN || ret == -1) {
221 ERR("Failed to format trace chunk name");
222 goto error;
223 }
224
225 return new_name;
226error:
227 free(new_name);
228 return NULL;
229}
230
231static
232void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk)
233{
234 urcu_ref_init(&chunk->ref);
235 pthread_mutex_init(&chunk->lock, NULL);
93bed9fe 236 lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free);
6cb32e5a 237 lttng_dynamic_pointer_array_init(&chunk->files, free);
2c5ff4e4
JG
238}
239
240static
241void lttng_trace_chunk_fini(struct lttng_trace_chunk *chunk)
242{
cbf53d23
JG
243 if (chunk->session_output_directory) {
244 lttng_directory_handle_put(
245 chunk->session_output_directory);
246 chunk->session_output_directory = NULL;
2c5ff4e4 247 }
cbf53d23
JG
248 if (chunk->chunk_directory) {
249 lttng_directory_handle_put(chunk->chunk_directory);
250 chunk->chunk_directory = NULL;
2c5ff4e4
JG
251 }
252 free(chunk->name);
253 chunk->name = NULL;
a7ceb342
MD
254 free(chunk->path);
255 chunk->path = NULL;
93bed9fe 256 lttng_dynamic_pointer_array_reset(&chunk->top_level_directories);
6cb32e5a 257 lttng_dynamic_pointer_array_reset(&chunk->files);
2c5ff4e4
JG
258 pthread_mutex_destroy(&chunk->lock);
259}
260
261static
262struct lttng_trace_chunk *lttng_trace_chunk_allocate(void)
263{
264 struct lttng_trace_chunk *chunk = NULL;
265
266 chunk = zmalloc(sizeof(*chunk));
267 if (!chunk) {
268 ERR("Failed to allocate trace chunk");
269 goto end;
270 }
271 lttng_trace_chunk_init(chunk);
272end:
273 return chunk;
274}
275
276LTTNG_HIDDEN
277struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous(void)
278{
279 DBG("Creating anonymous trace chunk");
280 return lttng_trace_chunk_allocate();
281}
282
283LTTNG_HIDDEN
284struct lttng_trace_chunk *lttng_trace_chunk_create(
a7ceb342 285 uint64_t chunk_id, time_t chunk_creation_time, const char *path)
2c5ff4e4
JG
286{
287 struct lttng_trace_chunk *chunk;
288 char chunk_creation_datetime_buf[16] = {};
289 const char *chunk_creation_datetime_str = "(formatting error)";
290 struct tm timeinfo_buf, *timeinfo;
291
292 timeinfo = localtime_r(&chunk_creation_time, &timeinfo_buf);
293 if (timeinfo) {
294 size_t strftime_ret;
295
296 /* Don't fail because of this; it is only used for logging. */
297 strftime_ret = strftime(chunk_creation_datetime_buf,
298 sizeof(chunk_creation_datetime_buf),
299 "%Y%m%d-%H%M%S", timeinfo);
300 if (strftime_ret) {
301 chunk_creation_datetime_str =
302 chunk_creation_datetime_buf;
303 }
304 }
305
306 DBG("Creating trace chunk: chunk_id = %" PRIu64 ", creation time = %s",
307 chunk_id, chunk_creation_datetime_str);
308 chunk = lttng_trace_chunk_allocate();
309 if (!chunk) {
310 goto end;
311 }
312
313 LTTNG_OPTIONAL_SET(&chunk->id, chunk_id);
314 LTTNG_OPTIONAL_SET(&chunk->timestamp_creation, chunk_creation_time);
315 if (chunk_id != 0) {
316 chunk->name = generate_chunk_name(chunk_id,
317 chunk_creation_time, NULL);
318 if (!chunk->name) {
319 ERR("Failed to allocate trace chunk name storage");
320 goto error;
321 }
322 }
a7ceb342
MD
323 if (path) {
324 chunk->path = strdup(path);
325 if (!chunk->path) {
326 goto error;
327 }
328 } else {
329 if (chunk->name) {
330 chunk->path = strdup(chunk->name);
331 if (!chunk->path) {
332 goto error;
333 }
334 }
335 }
2c5ff4e4
JG
336
337 DBG("Chunk name set to \"%s\"", chunk->name ? : "(none)");
338end:
339 return chunk;
340error:
341 lttng_trace_chunk_put(chunk);
342 return NULL;
343}
344
1a414e3a
JG
345LTTNG_HIDDEN
346struct lttng_trace_chunk *lttng_trace_chunk_copy(
347 struct lttng_trace_chunk *source_chunk)
348{
349 struct lttng_trace_chunk *new_chunk = lttng_trace_chunk_allocate();
350
351 if (!new_chunk) {
352 goto end;
353 }
354
355 pthread_mutex_lock(&source_chunk->lock);
356 /*
357 * A new chunk is always a user; it shall create no new trace
358 * subdirectories.
359 */
360 new_chunk->mode = (typeof(new_chunk->mode)) {
361 .is_set = true,
362 .value = TRACE_CHUNK_MODE_USER,
363 };
364 /*
365 * top_level_directories is not copied as it is never used
366 * by _user_ mode chunks.
367 */
368 /* The new chunk is not part of a registry (yet, at least). */
369 new_chunk->in_registry_element = false;
370 new_chunk->name_overridden = source_chunk->name_overridden;
371 if (source_chunk->name) {
372 new_chunk->name = strdup(source_chunk->name);
373 if (!new_chunk->name) {
374 ERR("Failed to copy source trace chunk name in %s()",
375 __FUNCTION__);
376 goto error_unlock;
377 }
378 }
a7ceb342
MD
379 if (source_chunk->path) {
380 new_chunk->path = strdup(source_chunk->path);
381 if (!new_chunk->path) {
382 ERR("Failed to copy source trace chunk path in %s()",
383 __FUNCTION__);
384 }
385 }
1a414e3a
JG
386 new_chunk->id = source_chunk->id;
387 new_chunk->timestamp_creation = source_chunk->timestamp_creation;
388 new_chunk->timestamp_close = source_chunk->timestamp_close;
389 new_chunk->credentials = source_chunk->credentials;
cbf53d23
JG
390 if (source_chunk->session_output_directory) {
391 const bool reference_acquired = lttng_directory_handle_get(
392 source_chunk->session_output_directory);
393
394 assert(reference_acquired);
395 new_chunk->session_output_directory =
396 source_chunk->session_output_directory;
1a414e3a 397 }
cbf53d23
JG
398 if (source_chunk->chunk_directory) {
399 const bool reference_acquired = lttng_directory_handle_get(
400 source_chunk->chunk_directory);
401
402 assert(reference_acquired);
403 new_chunk->chunk_directory = source_chunk->chunk_directory;
1a414e3a
JG
404 }
405 new_chunk->close_command = source_chunk->close_command;
406 pthread_mutex_unlock(&source_chunk->lock);
407end:
408 return new_chunk;
409error_unlock:
410 pthread_mutex_unlock(&source_chunk->lock);
41d2ab71 411 lttng_trace_chunk_put(new_chunk);
1a414e3a
JG
412 return NULL;
413}
414
2c5ff4e4
JG
415LTTNG_HIDDEN
416enum lttng_trace_chunk_status lttng_trace_chunk_get_id(
417 struct lttng_trace_chunk *chunk, uint64_t *id)
418{
419 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
420
421 pthread_mutex_lock(&chunk->lock);
422 if (chunk->id.is_set) {
423 *id = chunk->id.value;
424 } else {
425 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
426 }
427 pthread_mutex_unlock(&chunk->lock);
428 return status;
429}
430
431LTTNG_HIDDEN
432enum lttng_trace_chunk_status lttng_trace_chunk_get_creation_timestamp(
433 struct lttng_trace_chunk *chunk, time_t *creation_ts)
434
435{
436 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
437
438 pthread_mutex_lock(&chunk->lock);
439 if (chunk->timestamp_creation.is_set) {
440 *creation_ts = chunk->timestamp_creation.value;
441 } else {
442 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
443 }
444 pthread_mutex_unlock(&chunk->lock);
445 return status;
446}
447
448LTTNG_HIDDEN
449enum lttng_trace_chunk_status lttng_trace_chunk_get_close_timestamp(
450 struct lttng_trace_chunk *chunk, time_t *close_ts)
451{
452 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
453
454 pthread_mutex_lock(&chunk->lock);
455 if (chunk->timestamp_close.is_set) {
456 *close_ts = chunk->timestamp_close.value;
457 } else {
458 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
459 }
460 pthread_mutex_unlock(&chunk->lock);
461 return status;
462}
463
464LTTNG_HIDDEN
465enum lttng_trace_chunk_status lttng_trace_chunk_set_close_timestamp(
466 struct lttng_trace_chunk *chunk, time_t close_ts)
467{
468 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
469
470 pthread_mutex_lock(&chunk->lock);
471 if (!chunk->timestamp_creation.is_set) {
472 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
473 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
474 goto end;
475 }
476 if (chunk->timestamp_creation.value > close_ts) {
477 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
478 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
479 goto end;
480 }
481 LTTNG_OPTIONAL_SET(&chunk->timestamp_close, close_ts);
ecd1a12f
MD
482 if (!chunk->name_overridden) {
483 free(chunk->name);
484 chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id),
485 LTTNG_OPTIONAL_GET(chunk->timestamp_creation),
486 &close_ts);
487 if (!chunk->name) {
488 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
489 }
2c5ff4e4
JG
490 }
491end:
492 pthread_mutex_unlock(&chunk->lock);
493 return status;
494}
495
496LTTNG_HIDDEN
497enum lttng_trace_chunk_status lttng_trace_chunk_get_name(
498 struct lttng_trace_chunk *chunk, const char **name,
913a542b 499 bool *name_overridden)
2c5ff4e4
JG
500{
501 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
502
503 pthread_mutex_lock(&chunk->lock);
913a542b
MD
504 if (name_overridden) {
505 *name_overridden = chunk->name_overridden;
2c5ff4e4
JG
506 }
507 if (!chunk->name) {
508 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
509 goto end;
510 }
511 *name = chunk->name;
512end:
513 pthread_mutex_unlock(&chunk->lock);
514 return status;
515}
516
0e2d816a
MD
517LTTNG_HIDDEN
518bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk *chunk)
519{
520 bool name_overridden;
521
522 pthread_mutex_lock(&chunk->lock);
523 name_overridden = chunk->name_overridden;
524 pthread_mutex_unlock(&chunk->lock);
525 return name_overridden;
526}
527
84fa4db5
JG
528static
529bool is_valid_chunk_name(const char *name)
530{
531 size_t len;
532
533 if (!name) {
534 return false;
535 }
536
f7399c50 537 len = lttng_strnlen(name, LTTNG_NAME_MAX);
84fa4db5
JG
538 if (len == 0 || len == LTTNG_NAME_MAX) {
539 return false;
540 }
541
542 if (strchr(name, '/') || strchr(name, '.')) {
543 return false;
544 }
545
546 return true;
547}
548
2c5ff4e4
JG
549LTTNG_HIDDEN
550enum lttng_trace_chunk_status lttng_trace_chunk_override_name(
551 struct lttng_trace_chunk *chunk, const char *name)
552
553{
2c5ff4e4 554 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
a7ceb342 555 char *new_name, *new_path;
2c5ff4e4 556
a7ceb342 557 DBG("Override trace chunk name from %s to %s", chunk->name, name);
84fa4db5 558 if (!is_valid_chunk_name(name)) {
2c5ff4e4
JG
559 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
560 name ? : "NULL");
561 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
562 goto end;
563 }
564
565 pthread_mutex_lock(&chunk->lock);
566 if (!chunk->id.is_set) {
567 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
568 name);
569 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
570 goto end_unlock;
571 }
a7ceb342 572
2c5ff4e4
JG
573 new_name = strdup(name);
574 if (!new_name) {
575 ERR("Failed to allocate new trace chunk name");
576 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
577 goto end_unlock;
578 }
579 free(chunk->name);
580 chunk->name = new_name;
a7ceb342
MD
581
582 new_path = strdup(name);
583 if (!new_path) {
584 ERR("Failed to allocate new trace chunk path");
585 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
586 goto end_unlock;
587 }
588 free(chunk->path);
589 chunk->path = new_path;
590
913a542b 591 chunk->name_overridden = true;
a7ceb342 592end_unlock:
2c5ff4e4
JG
593 pthread_mutex_unlock(&chunk->lock);
594end:
595 return status;
596}
597
a7ceb342
MD
598static
599enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock(
600 struct lttng_trace_chunk *chunk, const char *path)
601
602{
603 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
604 struct lttng_directory_handle *rename_directory = NULL;
605 char *new_path, *old_path;
606 int ret;
607
608 if (chunk->name_overridden) {
609 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
610 goto end;
611 }
612
613 old_path = chunk->path;
614 DBG("lttng_trace_chunk_rename_path from %s to %s", old_path, path);
615
616 if ((!old_path && !path) ||
617 (old_path && path && !strcmp(old_path, path))) {
618 goto end;
619 }
620 /*
621 * Use chunk name as path if NULL path is specified.
622 */
623 if (!path) {
624 path = chunk->name;
625 }
626
627 /* Renaming from "" to "" is not accepted. */
628 if (path[0] == '\0' && old_path[0] == '\0') {
629 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
630 goto end;
631 }
632
633 /*
634 * If a rename is performed on a chunk for which the chunk_directory
635 * is not set (yet), or the session_output_directory is not set
636 * (interacting with a relay daemon), there is no rename to perform.
637 */
638 if (!chunk->chunk_directory ||
639 !chunk->session_output_directory) {
640 goto skip_move;
641 }
642
643 if (old_path[0] != '\0' && path[0] != '\0') {
644 /* Rename chunk directory. */
645 ret = lttng_directory_handle_rename_as_user(
646 chunk->session_output_directory,
647 old_path,
648 chunk->session_output_directory,
649 path,
650 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
651 NULL :
652 &chunk->credentials.value.user);
653 if (ret) {
654 PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
655 old_path, path);
656 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
657 goto end;
658 }
659 rename_directory = lttng_directory_handle_create_from_handle(
660 path,
661 chunk->session_output_directory);
662 if (!rename_directory) {
663 ERR("Failed to get handle to trace chunk rename directory");
664 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
665 goto end;
666 }
667
668 /* Release old handle. */
669 lttng_directory_handle_put(chunk->chunk_directory);
670 /*
671 * Transfer new handle reference to chunk as the current chunk
672 * handle.
673 */
674 chunk->chunk_directory = rename_directory;
675 rename_directory = NULL;
676 } else if (old_path[0] == '\0') {
677 size_t i, count = lttng_dynamic_pointer_array_get_count(
678 &chunk->top_level_directories);
679
680 ret = lttng_directory_handle_create_subdirectory_as_user(
681 chunk->session_output_directory,
682 path,
683 DIR_CREATION_MODE,
684 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
685 NULL :
686 &chunk->credentials.value.user);
687 if (ret) {
688 PERROR("Failed to create trace chunk rename directory \"%s\"",
689 path);
690 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
691 goto end;
692 }
693
694 rename_directory = lttng_directory_handle_create_from_handle(
695 path, chunk->session_output_directory);
696 if (!rename_directory) {
697 ERR("Failed to get handle to trace chunk rename directory");
698 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
699 goto end;
700 }
701
702 /* Move toplevel directories. */
703 for (i = 0; i < count; i++) {
704 const char *top_level_name =
705 lttng_dynamic_pointer_array_get_pointer(
706 &chunk->top_level_directories, i);
707
708 ret = lttng_directory_handle_rename_as_user(
709 chunk->chunk_directory,
710 top_level_name,
711 rename_directory,
712 top_level_name,
713 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
714 NULL :
715 &chunk->credentials.value.user);
716 if (ret) {
717 PERROR("Failed to move \"%s\" to trace chunk rename directory",
718 top_level_name);
719 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
720 goto end;
721 }
722 }
723 /* Release old handle. */
724 lttng_directory_handle_put(chunk->chunk_directory);
725 /*
726 * Transfer new handle reference to chunk as the current chunk
727 * handle.
728 */
729 chunk->chunk_directory = rename_directory;
730 rename_directory = NULL;
731 } else {
732 size_t i, count = lttng_dynamic_pointer_array_get_count(
733 &chunk->top_level_directories);
734 const bool reference_acquired = lttng_directory_handle_get(
735 chunk->session_output_directory);
736
737 assert(reference_acquired);
738 rename_directory = chunk->session_output_directory;
739
740 /* Move toplevel directories. */
741 for (i = 0; i < count; i++) {
742 const char *top_level_name =
743 lttng_dynamic_pointer_array_get_pointer(
744 &chunk->top_level_directories, i);
745
746 ret = lttng_directory_handle_rename_as_user(
747 chunk->chunk_directory,
748 top_level_name,
749 rename_directory,
750 top_level_name,
751 LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
752 NULL :
753 &chunk->credentials.value.user);
754 if (ret) {
755 PERROR("Failed to move \"%s\" to trace chunk rename directory",
756 top_level_name);
757 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
758 goto end;
759 }
760 }
761 /* Release old handle. */
762 lttng_directory_handle_put(chunk->chunk_directory);
763 /*
764 * Transfer new handle reference to chunk as the current chunk
765 * handle.
766 */
767 chunk->chunk_directory = rename_directory;
768 rename_directory = NULL;
769
770 /* Remove old directory. */
771 status = lttng_directory_handle_remove_subdirectory(
772 chunk->session_output_directory,
773 old_path);
774 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
775 ERR("Error removing subdirectory '%s' file when deleting chunk",
776 old_path);
777 ret = -1;
778 goto end;
779 }
780 }
781
782skip_move:
783 if (path) {
784 new_path = strdup(path);
785 if (!new_path) {
786 ERR("Failed to allocate new trace chunk path");
787 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
788 goto end;
789 }
790 } else {
791 new_path = NULL;
792 }
793 free(chunk->path);
794 chunk->path = new_path;
795end:
796 lttng_directory_handle_put(rename_directory);
797 return status;
798}
799
800LTTNG_HIDDEN
801enum lttng_trace_chunk_status lttng_trace_chunk_rename_path(
802 struct lttng_trace_chunk *chunk, const char *path)
803
804{
805 enum lttng_trace_chunk_status status;
806
807 pthread_mutex_lock(&chunk->lock);
808 status = lttng_trace_chunk_rename_path_no_lock(chunk, path);
809 pthread_mutex_unlock(&chunk->lock);
810
811 return status;
812}
813
2c5ff4e4
JG
814LTTNG_HIDDEN
815enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials(
816 struct lttng_trace_chunk *chunk,
817 struct lttng_credentials *credentials)
818{
819 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
820
821 pthread_mutex_lock(&chunk->lock);
822 if (chunk->credentials.is_set) {
823 if (chunk->credentials.value.use_current_user) {
824 credentials->uid = geteuid();
825 credentials->gid = getegid();
826 } else {
827 *credentials = chunk->credentials.value.user;
828 }
829 } else {
830 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
831 }
832 pthread_mutex_unlock(&chunk->lock);
833 return status;
834}
835
836LTTNG_HIDDEN
837enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials(
838 struct lttng_trace_chunk *chunk,
839 const struct lttng_credentials *user_credentials)
840{
841 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
842 const struct chunk_credentials credentials = {
843 .user = *user_credentials,
844 .use_current_user = false,
845 };
846
847 pthread_mutex_lock(&chunk->lock);
848 if (chunk->credentials.is_set) {
849 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
850 goto end;
851 }
852 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
853end:
854 pthread_mutex_unlock(&chunk->lock);
855 return status;
856}
857
858LTTNG_HIDDEN
859enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials_current_user(
860 struct lttng_trace_chunk *chunk)
861{
862 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
863 const struct chunk_credentials credentials = {
864 .use_current_user = true,
865 };
866
867 pthread_mutex_lock(&chunk->lock);
868 if (chunk->credentials.is_set) {
869 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
870 goto end;
871 }
872 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
873end:
874 pthread_mutex_unlock(&chunk->lock);
875 return status;
876}
877
878
879LTTNG_HIDDEN
880enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner(
881 struct lttng_trace_chunk *chunk,
882 struct lttng_directory_handle *session_output_directory)
883{
884 int ret;
885 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
cbf53d23
JG
886 struct lttng_directory_handle *chunk_directory_handle = NULL;
887 bool reference_acquired;
2c5ff4e4
JG
888
889 pthread_mutex_lock(&chunk->lock);
890 if (chunk->mode.is_set) {
891 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
892 goto end;
893 }
894 if (!chunk->credentials.is_set) {
895 /*
896 * Fatal error, credentials must be set before a
897 * directory is created.
898 */
899 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
900 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
901 goto end;
902 }
a7ceb342 903 if (chunk->path[0] != '\0') {
2c5ff4e4
JG
904 ret = lttng_directory_handle_create_subdirectory_as_user(
905 session_output_directory,
a7ceb342 906 chunk->path,
2c5ff4e4
JG
907 DIR_CREATION_MODE,
908 !chunk->credentials.value.use_current_user ?
909 &chunk->credentials.value.user : NULL);
910 if (ret) {
911 PERROR("Failed to create chunk output directory \"%s\"",
a7ceb342 912 chunk->path);
2c5ff4e4
JG
913 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
914 goto end;
915 }
a7ceb342
MD
916 chunk_directory_handle =
917 lttng_directory_handle_create_from_handle(
918 chunk->path,
919 session_output_directory);
920 if (!chunk_directory_handle) {
921 /* The function already logs on all error paths. */
922 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
923 goto end;
924 }
925 } else {
926 /*
927 * A nameless chunk does not need its own output directory.
928 * The session's output directory will be used.
929 */
930 const bool reference_acquired =
931 lttng_directory_handle_get(
932 session_output_directory);
933
934 assert(reference_acquired);
935 chunk_directory_handle = session_output_directory;
2c5ff4e4 936 }
cbf53d23
JG
937 chunk->chunk_directory = chunk_directory_handle;
938 chunk_directory_handle = NULL;
939 reference_acquired = lttng_directory_handle_get(
940 session_output_directory);
941 assert(reference_acquired);
942 chunk->session_output_directory = session_output_directory;
2c5ff4e4
JG
943 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER);
944end:
945 pthread_mutex_unlock(&chunk->lock);
946 return status;
947}
948
949LTTNG_HIDDEN
950enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user(
951 struct lttng_trace_chunk *chunk,
952 struct lttng_directory_handle *chunk_directory)
953{
954 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
cbf53d23 955 bool reference_acquired;
2c5ff4e4
JG
956
957 pthread_mutex_lock(&chunk->lock);
958 if (chunk->mode.is_set) {
959 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
960 goto end;
961 }
962 if (!chunk->credentials.is_set) {
963 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
964 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
965 goto end;
966 }
cbf53d23
JG
967 reference_acquired = lttng_directory_handle_get(chunk_directory);
968 assert(reference_acquired);
969 chunk->chunk_directory = chunk_directory;
2c5ff4e4
JG
970 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER);
971end:
972 pthread_mutex_unlock(&chunk->lock);
973 return status;
974}
975
976LTTNG_HIDDEN
cbf53d23 977enum lttng_trace_chunk_status lttng_trace_chunk_borrow_chunk_directory_handle(
2c5ff4e4
JG
978 struct lttng_trace_chunk *chunk,
979 const struct lttng_directory_handle **handle)
980{
981 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
982
983 pthread_mutex_lock(&chunk->lock);
cbf53d23 984 if (!chunk->chunk_directory) {
2c5ff4e4
JG
985 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
986 goto end;
987 }
988
cbf53d23 989 *handle = chunk->chunk_directory;
2c5ff4e4
JG
990end:
991 pthread_mutex_unlock(&chunk->lock);
992 return status;
993}
994
995/* Add a top-level directory to the trace chunk if it was previously unknown. */
996static
997int add_top_level_directory_unique(struct lttng_trace_chunk *chunk,
998 const char *new_path)
999{
1000 int ret = 0;
1001 bool found = false;
1002 size_t i, count = lttng_dynamic_pointer_array_get_count(
1003 &chunk->top_level_directories);
1004 const char *new_path_separator_pos = strchr(new_path, '/');
1005 const ptrdiff_t new_path_top_level_len = new_path_separator_pos ?
1006 new_path_separator_pos - new_path : strlen(new_path);
1007
1008 for (i = 0; i < count; i++) {
1009 const char *path = lttng_dynamic_pointer_array_get_pointer(
1010 &chunk->top_level_directories, i);
1011 const ptrdiff_t path_top_level_len = strlen(path);
1012
1013 if (path_top_level_len != new_path_top_level_len) {
1014 continue;
1015 }
1016 if (!strncmp(path, new_path, path_top_level_len)) {
1017 found = true;
1018 break;
1019 }
1020 }
1021
1022 if (!found) {
c36a763b 1023 char *copy = lttng_strndup(new_path, new_path_top_level_len);
2c5ff4e4
JG
1024
1025 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
1026 new_path, chunk->name ? : "(unnamed)");
1027 if (!copy) {
1028 PERROR("Failed to copy path");
1029 ret = -1;
1030 goto end;
1031 }
1032 ret = lttng_dynamic_pointer_array_add_pointer(
1033 &chunk->top_level_directories, copy);
1034 if (ret) {
1035 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
1036 free(copy);
1037 goto end;
1038 }
1039 }
1040end:
1041 return ret;
1042}
1043
1044LTTNG_HIDDEN
1045enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory(
1046 struct lttng_trace_chunk *chunk,
1047 const char *path)
1048{
1049 int ret;
1050 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1051
1052 DBG("Creating trace chunk subdirectory \"%s\"", path);
1053 pthread_mutex_lock(&chunk->lock);
1054 if (!chunk->credentials.is_set) {
1055 /*
1056 * Fatal error, credentials must be set before a
1057 * directory is created.
1058 */
1059 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
1060 path);
1061 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1062 goto end;
1063 }
1064 if (!chunk->mode.is_set ||
1065 chunk->mode.value != TRACE_CHUNK_MODE_OWNER) {
1066 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
1067 path);
1068 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
1069 goto end;
1070 }
cbf53d23 1071 if (!chunk->chunk_directory) {
2c5ff4e4
JG
1072 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
1073 path);
1074 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1075 goto end;
1076 }
1077 if (*path == '/') {
1078 ERR("Refusing to create absolute trace chunk directory \"%s\"",
1079 path);
1080 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
1081 goto end;
1082 }
1083 ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
cbf53d23 1084 chunk->chunk_directory, path,
2c5ff4e4
JG
1085 DIR_CREATION_MODE,
1086 chunk->credentials.value.use_current_user ?
1087 NULL : &chunk->credentials.value.user);
1088 if (ret) {
1089 PERROR("Failed to create trace chunk subdirectory \"%s\"",
1090 path);
1091 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1092 goto end;
1093 }
1094 ret = add_top_level_directory_unique(chunk, path);
1095 if (ret) {
1096 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1097 goto end;
1098 }
1099end:
1100 pthread_mutex_unlock(&chunk->lock);
1101 return status;
1102}
1103
6cb32e5a
MD
1104/*
1105 * TODO: Implement O(1) lookup.
1106 */
1107static
1108bool lttng_trace_chunk_find_file(struct lttng_trace_chunk *chunk,
1109 const char *path, size_t *index)
1110{
1111 size_t i, count;
1112
1113 count = lttng_dynamic_pointer_array_get_count(&chunk->files);
1114 for (i = 0; i < count; i++) {
1115 const char *iter_path =
1116 lttng_dynamic_pointer_array_get_pointer(
1117 &chunk->files, i);
1118 if (!strcmp(iter_path, path)) {
1119 if (index) {
1120 *index = i;
1121 }
1122 return true;
1123 }
1124 }
1125 return false;
1126}
1127
1128static
1129enum lttng_trace_chunk_status lttng_trace_chunk_add_file(
1130 struct lttng_trace_chunk *chunk,
1131 const char *path)
1132{
1133 char *copy;
1134 int ret;
1135 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1136
1137 if (lttng_trace_chunk_find_file(chunk, path, NULL)) {
1138 return LTTNG_TRACE_CHUNK_STATUS_OK;
1139 }
1140 DBG("Adding new file \"%s\" to trace chunk \"%s\"",
1141 path, chunk->name ? : "(unnamed)");
1142 copy = strdup(path);
1143 if (!copy) {
1144 PERROR("Failed to copy path");
1145 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1146 goto end;
1147 }
1148 ret = lttng_dynamic_pointer_array_add_pointer(
1149 &chunk->files, copy);
1150 if (ret) {
1151 ERR("Allocation failure while adding file to a trace chunk");
1152 free(copy);
1153 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1154 goto end;
1155 }
1156end:
1157 return status;
1158}
1159
1160static
1161void lttng_trace_chunk_remove_file(
1162 struct lttng_trace_chunk *chunk,
1163 const char *path)
1164{
1165 size_t index;
1166 bool found;
1167 int ret;
1168
1169 found = lttng_trace_chunk_find_file(chunk, path, &index);
1170 if (!found) {
1171 return;
1172 }
1173 ret = lttng_dynamic_pointer_array_remove_pointer(
1174 &chunk->files, index);
1175 assert(!ret);
1176}
1177
2c5ff4e4
JG
1178LTTNG_HIDDEN
1179enum lttng_trace_chunk_status lttng_trace_chunk_open_file(
1180 struct lttng_trace_chunk *chunk, const char *file_path,
3ff5c5db 1181 int flags, mode_t mode, int *out_fd, bool expect_no_file)
2c5ff4e4
JG
1182{
1183 int ret;
1184 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1185
1186 DBG("Opening trace chunk file \"%s\"", file_path);
1187 pthread_mutex_lock(&chunk->lock);
1188 if (!chunk->credentials.is_set) {
1189 /*
1190 * Fatal error, credentials must be set before a
1191 * file is created.
1192 */
1193 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
1194 file_path);
1195 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1196 goto end;
1197 }
cbf53d23 1198 if (!chunk->chunk_directory) {
2c5ff4e4
JG
1199 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
1200 file_path);
1201 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1202 goto end;
1203 }
6cb32e5a
MD
1204 status = lttng_trace_chunk_add_file(chunk, file_path);
1205 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1206 goto end;
1207 }
2c5ff4e4 1208 ret = lttng_directory_handle_open_file_as_user(
cbf53d23 1209 chunk->chunk_directory, file_path, flags, mode,
2c5ff4e4
JG
1210 chunk->credentials.value.use_current_user ?
1211 NULL : &chunk->credentials.value.user);
1212 if (ret < 0) {
3ff5c5db
MD
1213 if (errno == ENOENT && expect_no_file) {
1214 status = LTTNG_TRACE_CHUNK_STATUS_NO_FILE;
1215 } else {
1216 PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
d2956687 1217 file_path, flags, (int) mode);
3ff5c5db
MD
1218 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1219 }
6cb32e5a 1220 lttng_trace_chunk_remove_file(chunk, file_path);
2c5ff4e4
JG
1221 goto end;
1222 }
1223 *out_fd = ret;
1224end:
1225 pthread_mutex_unlock(&chunk->lock);
1226 return status;
1227}
1228
1229LTTNG_HIDDEN
1230int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk,
1231 const char *file_path)
1232{
1233 int ret;
1234 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1235
1236 DBG("Unlinking trace chunk file \"%s\"", file_path);
1237 pthread_mutex_lock(&chunk->lock);
1238 if (!chunk->credentials.is_set) {
1239 /*
1240 * Fatal error, credentials must be set before a
a7ceb342 1241 * file is unlinked.
2c5ff4e4
JG
1242 */
1243 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
1244 file_path);
1245 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1246 goto end;
1247 }
cbf53d23 1248 if (!chunk->chunk_directory) {
2c5ff4e4
JG
1249 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
1250 file_path);
1251 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1252 goto end;
1253 }
1254 ret = lttng_directory_handle_unlink_file_as_user(
cbf53d23 1255 chunk->chunk_directory, file_path,
2c5ff4e4
JG
1256 chunk->credentials.value.use_current_user ?
1257 NULL : &chunk->credentials.value.user);
1258 if (ret < 0) {
1259 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1260 goto end;
1261 }
6cb32e5a 1262 lttng_trace_chunk_remove_file(chunk, file_path);
2c5ff4e4
JG
1263end:
1264 pthread_mutex_unlock(&chunk->lock);
1265 return status;
1266}
1267
a7ceb342
MD
1268LTTNG_HIDDEN
1269int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk *chunk,
1270 const char *path)
2c5ff4e4
JG
1271{
1272 int ret;
a7ceb342
MD
1273 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1274
1275 DBG("Recursively removing trace chunk directory \"%s\"", path);
1276 pthread_mutex_lock(&chunk->lock);
1277 if (!chunk->credentials.is_set) {
1278 /*
1279 * Fatal error, credentials must be set before a
1280 * directory is removed.
1281 */
1282 ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
1283 path);
1284 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1285 goto end;
1286 }
1287 if (!chunk->chunk_directory) {
1288 ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
1289 path);
1290 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1291 goto end;
1292 }
1293 ret = lttng_directory_handle_remove_subdirectory_recursive_as_user(
1294 chunk->chunk_directory, path,
1295 chunk->credentials.value.use_current_user ?
1296 NULL : &chunk->credentials.value.user,
1297 LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
1298 if (ret < 0) {
1299 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
1300 goto end;
1301 }
1302end:
1303 pthread_mutex_unlock(&chunk->lock);
1304 return status;
1305}
1306
1307static
1308int lttng_trace_chunk_move_to_completed_post_release(
1309 struct lttng_trace_chunk *trace_chunk)
1310{
1311 int ret = 0;
2c5ff4e4
JG
1312 char *archived_chunk_name = NULL;
1313 const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id);
1314 const time_t creation_timestamp =
1315 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation);
1316 const time_t close_timestamp =
1317 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close);
cbf53d23 1318 struct lttng_directory_handle *archived_chunks_directory = NULL;
a7ceb342 1319 enum lttng_trace_chunk_status status;
2c5ff4e4 1320
bbc4768c
JG
1321 if (!trace_chunk->mode.is_set ||
1322 trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER ||
cbf53d23 1323 !trace_chunk->session_output_directory) {
bbc4768c
JG
1324 /*
1325 * This command doesn't need to run if the output is remote
1326 * or if the trace chunk is not owned by this process.
1327 */
1328 goto end;
1329 }
1330
2c5ff4e4 1331 assert(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
913a542b 1332 assert(!trace_chunk->name_overridden);
a7ceb342 1333 assert(trace_chunk->path);
2c5ff4e4
JG
1334
1335 archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp,
1336 &close_timestamp);
1337 if (!archived_chunk_name) {
1338 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
a7ceb342 1339 ret = -1;
2c5ff4e4
JG
1340 goto end;
1341 }
1342
1343 ret = lttng_directory_handle_create_subdirectory_as_user(
cbf53d23 1344 trace_chunk->session_output_directory,
2c5ff4e4
JG
1345 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
1346 DIR_CREATION_MODE,
1347 !trace_chunk->credentials.value.use_current_user ?
1348 &trace_chunk->credentials.value.user :
1349 NULL);
1350 if (ret) {
1351 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
1352 "\" directory for archived trace chunks");
1353 goto end;
1354 }
1355
cbf53d23 1356 archived_chunks_directory = lttng_directory_handle_create_from_handle(
2c5ff4e4 1357 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
cbf53d23
JG
1358 trace_chunk->session_output_directory);
1359 if (!archived_chunks_directory) {
2c5ff4e4 1360 PERROR("Failed to get handle to archived trace chunks directory");
a7ceb342 1361 ret = -1;
2c5ff4e4
JG
1362 goto end;
1363 }
2c5ff4e4 1364
a7ceb342
MD
1365 /*
1366 * Make sure chunk is renamed to old directory if not already done by
1367 * the creation of the next chunk. This happens if a rotation is
1368 * performed while tracing is stopped.
1369 */
1370 if (!trace_chunk->path || strcmp(trace_chunk->path,
1371 DEFAULT_CHUNK_TMP_OLD_DIRECTORY)) {
1372 status = lttng_trace_chunk_rename_path_no_lock(trace_chunk,
1373 DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
1374 if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
1375 ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
1376 ret = -1;
1377 goto end;
1378 }
1379 }
1380
9de831f8 1381 ret = lttng_directory_handle_rename_as_user(
cbf53d23 1382 trace_chunk->session_output_directory,
a7ceb342 1383 trace_chunk->path,
cbf53d23 1384 archived_chunks_directory,
9de831f8
JG
1385 archived_chunk_name,
1386 LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
1387 NULL :
1388 &trace_chunk->credentials.value.user);
2c5ff4e4
JG
1389 if (ret) {
1390 PERROR("Failed to rename folder \"%s\" to \"%s\"",
a7ceb342
MD
1391 trace_chunk->path,
1392 archived_chunk_name);
2c5ff4e4
JG
1393 }
1394
1395end:
cbf53d23 1396 lttng_directory_handle_put(archived_chunks_directory);
2c5ff4e4 1397 free(archived_chunk_name);
a7ceb342 1398 return ret;
2c5ff4e4
JG
1399}
1400
bbc4768c
JG
1401LTTNG_HIDDEN
1402enum lttng_trace_chunk_status lttng_trace_chunk_get_close_command(
1403 struct lttng_trace_chunk *chunk,
1404 enum lttng_trace_chunk_command_type *command_type)
1405{
1406 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1407
1408 pthread_mutex_lock(&chunk->lock);
1409 if (chunk->close_command.is_set) {
1410 *command_type = chunk->close_command.value;
1411 status = LTTNG_TRACE_CHUNK_STATUS_OK;
1412 } else {
1413 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
1414 }
1415 pthread_mutex_unlock(&chunk->lock);
1416 return status;
1417}
1418
2c5ff4e4
JG
1419LTTNG_HIDDEN
1420enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
1421 struct lttng_trace_chunk *chunk,
1422 enum lttng_trace_chunk_command_type close_command)
1423{
1424 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
1425
1426 if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
1427 close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
1428 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
6def6cd7 1429 goto end;
2c5ff4e4
JG
1430 }
1431
1432 pthread_mutex_lock(&chunk->lock);
1433 if (chunk->close_command.is_set) {
1434 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
1435 close_command_names[chunk->close_command.value],
1436 close_command_names[close_command]);
1437 } else {
1438 DBG("Setting trace chunk close command to \"%s\"",
1439 close_command_names[close_command]);
1440 }
343defc2
MD
1441 /*
1442 * Unset close command for no-op for backward compatibility with relayd
1443 * 2.11.
1444 */
1445 if (close_command != LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) {
1446 LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
1447 } else {
1448 LTTNG_OPTIONAL_UNSET(&chunk->close_command);
1449 }
2c5ff4e4 1450 pthread_mutex_unlock(&chunk->lock);
6def6cd7 1451end:
2c5ff4e4
JG
1452 return status;
1453}
1454
bbc4768c
JG
1455LTTNG_HIDDEN
1456const char *lttng_trace_chunk_command_type_get_name(
1457 enum lttng_trace_chunk_command_type command)
1458{
1459 switch (command) {
1460 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
1461 return "move to completed trace chunk folder";
343defc2
MD
1462 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
1463 return "no operation";
1464 case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
1465 return "delete";
bbc4768c
JG
1466 default:
1467 abort();
1468 }
1469}
1470
2c5ff4e4
JG
1471LTTNG_HIDDEN
1472bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
1473{
1474 return urcu_ref_get_unless_zero(&chunk->ref);
1475}
1476
1477static
1478void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
1479{
1480 struct lttng_trace_chunk_registry_element *element =
1481 container_of(node, typeof(*element), rcu_node);
1482
1483 lttng_trace_chunk_fini(&element->chunk);
1484 free(element);
1485}
1486
1487static
1488void lttng_trace_chunk_release(struct urcu_ref *ref)
1489{
1490 struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk),
1491 ref);
1492
1493 if (chunk->close_command.is_set) {
a7ceb342
MD
1494 if (close_command_post_release_funcs[
1495 chunk->close_command.value](chunk)) {
1496 ERR("Trace chunk post-release command %s has failed.",
1497 close_command_names[chunk->close_command.value]);
1498 }
2c5ff4e4
JG
1499 }
1500
1501 if (chunk->in_registry_element) {
1502 struct lttng_trace_chunk_registry_element *element;
1503
1504 element = container_of(chunk, typeof(*element), chunk);
1505 if (element->registry) {
1506 rcu_read_lock();
1507 cds_lfht_del(element->registry->ht,
1508 &element->trace_chunk_registry_ht_node);
1509 rcu_read_unlock();
1510 call_rcu(&element->rcu_node,
1511 free_lttng_trace_chunk_registry_element);
1512 } else {
1513 /* Never published, can be free'd immediately. */
1514 free_lttng_trace_chunk_registry_element(
1515 &element->rcu_node);
1516 }
1517 } else {
1518 /* Not RCU-protected, free immediately. */
1519 lttng_trace_chunk_fini(chunk);
1520 free(chunk);
1521 }
1522}
1523
1524LTTNG_HIDDEN
1525void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
1526{
1527 if (!chunk) {
1528 return;
1529 }
1530 assert(chunk->ref.refcount);
1531 urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
1532}
1533
1534LTTNG_HIDDEN
1535struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void)
1536{
1537 struct lttng_trace_chunk_registry *registry;
1538
1539 registry = zmalloc(sizeof(*registry));
1540 if (!registry) {
1541 goto end;
1542 }
1543
1544 registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
1545 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
1546 if (!registry->ht) {
1547 goto error;
1548 }
1549end:
1550 return registry;
1551error:
1552 lttng_trace_chunk_registry_destroy(registry);
8243bf12 1553 return NULL;
2c5ff4e4
JG
1554}
1555
1556LTTNG_HIDDEN
1557void lttng_trace_chunk_registry_destroy(
1558 struct lttng_trace_chunk_registry *registry)
1559{
1560 if (!registry) {
1561 return;
1562 }
1563 if (registry->ht) {
1564 int ret = cds_lfht_destroy(registry->ht, NULL);
1565 assert(!ret);
1566 }
1567 free(registry);
1568}
1569
1570static
1571struct lttng_trace_chunk_registry_element *
1572lttng_trace_chunk_registry_element_create_from_chunk(
1573 struct lttng_trace_chunk *chunk, uint64_t session_id)
1574{
1575 struct lttng_trace_chunk_registry_element *element =
1576 zmalloc(sizeof(*element));
1577
1578 if (!element) {
1579 goto end;
1580 }
1581 cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
1582 element->session_id = session_id;
1583
1584 element->chunk = *chunk;
1585 lttng_trace_chunk_init(&element->chunk);
cbf53d23
JG
1586 if (chunk->session_output_directory) {
1587 /* Transferred ownership. */
1588 element->chunk.session_output_directory =
1589 chunk->session_output_directory;
1590 chunk->session_output_directory = NULL;
1591 }
1592 if (chunk->chunk_directory) {
1593 /* Transferred ownership. */
1594 element->chunk.chunk_directory = chunk->chunk_directory;
1595 chunk->chunk_directory = NULL;
2c5ff4e4
JG
1596 }
1597 /*
a7ceb342
MD
1598 * The original chunk becomes invalid; the name and path attributes are
1599 * transferred to the new chunk instance.
2c5ff4e4
JG
1600 */
1601 chunk->name = NULL;
a7ceb342 1602 chunk->path = NULL;
2c5ff4e4
JG
1603 element->chunk.in_registry_element = true;
1604end:
1605 return element;
1606}
1607
1608LTTNG_HIDDEN
1609struct lttng_trace_chunk *
1610lttng_trace_chunk_registry_publish_chunk(
1611 struct lttng_trace_chunk_registry *registry,
1612 uint64_t session_id, struct lttng_trace_chunk *chunk)
1613{
1614 struct lttng_trace_chunk_registry_element *element;
1615 unsigned long element_hash;
1616
1617 pthread_mutex_lock(&chunk->lock);
1618 element = lttng_trace_chunk_registry_element_create_from_chunk(chunk,
1619 session_id);
1620 pthread_mutex_unlock(&chunk->lock);
1621 if (!element) {
1622 goto end;
1623 }
1624 /*
1625 * chunk is now invalid, the only valid operation is a 'put' from the
1626 * caller.
1627 */
1628 chunk = NULL;
1629 element_hash = lttng_trace_chunk_registry_element_hash(element);
1630
1631 rcu_read_lock();
1632 while (1) {
1633 struct cds_lfht_node *published_node;
1634 struct lttng_trace_chunk *published_chunk;
1635 struct lttng_trace_chunk_registry_element *published_element;
1636
1637 published_node = cds_lfht_add_unique(registry->ht,
1638 element_hash,
1639 lttng_trace_chunk_registry_element_match,
1640 element,
1641 &element->trace_chunk_registry_ht_node);
1642 if (published_node == &element->trace_chunk_registry_ht_node) {
1643 /* Successfully published the new element. */
1644 element->registry = registry;
1645 /* Acquire a reference for the caller. */
1646 if (lttng_trace_chunk_get(&element->chunk)) {
1647 break;
1648 } else {
1649 /*
1650 * Another thread concurrently unpublished the
1651 * trace chunk. This is currently unexpected.
1652 *
1653 * Re-attempt to publish.
1654 */
87cee602 1655 ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
2c5ff4e4
JG
1656 continue;
1657 }
1658 }
1659
1660 /*
1661 * An equivalent trace chunk was published before this trace
1662 * chunk. Attempt to acquire a reference to the one that was
1663 * already published and release the reference to the copy we
1664 * created if successful.
1665 */
1666 published_element = container_of(published_node,
1667 typeof(*published_element),
1668 trace_chunk_registry_ht_node);
1669 published_chunk = &published_element->chunk;
1670 if (lttng_trace_chunk_get(published_chunk)) {
1671 lttng_trace_chunk_put(&element->chunk);
1672 element = published_element;
1673 break;
1674 }
1675 /*
1676 * A reference to the previously published trace chunk could not
1677 * be acquired. Hence, retry to publish our copy of the trace
1678 * chunk.
1679 */
1680 }
1681 rcu_read_unlock();
1682end:
1683 return element ? &element->chunk : NULL;
1684}
1685
1686/*
1687 * Note that the caller must be registered as an RCU thread.
1688 * However, it does not need to hold the RCU read lock. The RCU read lock is
1689 * acquired to perform the look-up in the registry's hash table and held until
1690 * after a reference to the "found" trace chunk is acquired.
1691 *
1692 * IOW, holding a reference guarantees the existence of the object for the
1693 * caller.
1694 */
1695static
1696struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
1697 const struct lttng_trace_chunk_registry *registry,
1698 uint64_t session_id, uint64_t *chunk_id)
1699{
1700 const struct lttng_trace_chunk_registry_element target_element = {
1701 .chunk.id.is_set = !!chunk_id,
1702 .chunk.id.value = chunk_id ? *chunk_id : 0,
1703 .session_id = session_id,
1704 };
1705 const unsigned long element_hash =
1706 lttng_trace_chunk_registry_element_hash(
1707 &target_element);
1708 struct cds_lfht_node *published_node;
1709 struct lttng_trace_chunk_registry_element *published_element;
1710 struct lttng_trace_chunk *published_chunk = NULL;
1711 struct cds_lfht_iter iter;
1712
1713 rcu_read_lock();
1714 cds_lfht_lookup(registry->ht,
1715 element_hash,
1716 lttng_trace_chunk_registry_element_match,
1717 &target_element,
1718 &iter);
1719 published_node = cds_lfht_iter_get_node(&iter);
1720 if (!published_node) {
1721 goto end;
1722 }
1723
1724 published_element = container_of(published_node,
1725 typeof(*published_element),
1726 trace_chunk_registry_ht_node);
1727 if (lttng_trace_chunk_get(&published_element->chunk)) {
1728 published_chunk = &published_element->chunk;
1729 }
1730end:
1731 rcu_read_unlock();
1732 return published_chunk;
1733}
1734
1735LTTNG_HIDDEN
1736struct lttng_trace_chunk *
1737lttng_trace_chunk_registry_find_chunk(
1738 const struct lttng_trace_chunk_registry *registry,
1739 uint64_t session_id, uint64_t chunk_id)
1740{
1741 return _lttng_trace_chunk_registry_find_chunk(registry,
1742 session_id, &chunk_id);
1743}
1744
6b584c2e
JG
1745LTTNG_HIDDEN
1746int lttng_trace_chunk_registry_chunk_exists(
1747 const struct lttng_trace_chunk_registry *registry,
1748 uint64_t session_id, uint64_t chunk_id, bool *chunk_exists)
1749{
1750 int ret = 0;
1751 const struct lttng_trace_chunk_registry_element target_element = {
1752 .chunk.id.is_set = true,
1753 .chunk.id.value = chunk_id,
1754 .session_id = session_id,
1755 };
1756 const unsigned long element_hash =
1757 lttng_trace_chunk_registry_element_hash(
1758 &target_element);
1759 struct cds_lfht_node *published_node;
1760 struct cds_lfht_iter iter;
1761
1762 rcu_read_lock();
1763 cds_lfht_lookup(registry->ht,
1764 element_hash,
1765 lttng_trace_chunk_registry_element_match,
1766 &target_element,
1767 &iter);
1768 published_node = cds_lfht_iter_get_node(&iter);
1769 if (!published_node) {
1770 *chunk_exists = false;
1771 goto end;
1772 }
1773
1774 *chunk_exists = !cds_lfht_is_node_deleted(published_node);
1775end:
1776 rcu_read_unlock();
1777 return ret;
1778}
1779
2c5ff4e4
JG
1780LTTNG_HIDDEN
1781struct lttng_trace_chunk *
1782lttng_trace_chunk_registry_find_anonymous_chunk(
1783 const struct lttng_trace_chunk_registry *registry,
1784 uint64_t session_id)
1785{
1786 return _lttng_trace_chunk_registry_find_chunk(registry,
1787 session_id, NULL);
1788}
e10aec8f
MD
1789
1790unsigned int lttng_trace_chunk_registry_put_each_chunk(
1791 struct lttng_trace_chunk_registry *registry)
1792{
1793 struct cds_lfht_iter iter;
1794 struct lttng_trace_chunk_registry_element *chunk_element;
1795 unsigned int trace_chunks_left = 0;
1796
1797 DBG("Releasing trace chunk registry to all trace chunks");
1798 rcu_read_lock();
1799 cds_lfht_for_each_entry(registry->ht,
1800 &iter, chunk_element, trace_chunk_registry_ht_node) {
1801 const char *chunk_id_str = "none";
1802 char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)];
1803
1804 pthread_mutex_lock(&chunk_element->chunk.lock);
1805 if (chunk_element->chunk.id.is_set) {
1806 int fmt_ret;
1807
1808 fmt_ret = snprintf(chunk_id_buf, sizeof(chunk_id_buf),
1809 "%" PRIu64,
1810 chunk_element->chunk.id.value);
1811 if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) {
1812 chunk_id_str = "formatting error";
1813 } else {
1814 chunk_id_str = chunk_id_buf;
1815 }
1816 }
1817
1818 DBG("Releasing reference to trace chunk: session_id = %" PRIu64
1819 "chunk_id = %s, name = \"%s\", status = %s",
1820 chunk_element->session_id,
1821 chunk_id_str,
1822 chunk_element->chunk.name ? : "none",
1823 chunk_element->chunk.close_command.is_set ?
1824 "open" : "closed");
1825 pthread_mutex_unlock(&chunk_element->chunk.lock);
1826 lttng_trace_chunk_put(&chunk_element->chunk);
1827 trace_chunks_left++;
1828 }
1829 rcu_read_unlock();
1830 DBG("Released reference to %u trace chunks in %s()", trace_chunks_left,
1831 __FUNCTION__);
1832
1833 return trace_chunks_left;
1834}
This page took 0.098452 seconds and 4 git commands to generate.