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