Add rmdirat and renameat to run-as commands
[lttng-tools.git] / src / common / trace-chunk.c
1 /*
2 * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, version 2.1 only,
6 * as published by the Free Software Foundation.
7 *
8 * This library is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18 #include <lttng/constant.h>
19 #include <common/string-utils/format.h>
20 #include <common/trace-chunk.h>
21 #include <common/trace-chunk-registry.h>
22 #include <common/hashtable/utils.h>
23 #include <common/hashtable/hashtable.h>
24 #include <common/error.h>
25 #include <common/utils.h>
26 #include <common/time.h>
27 #include <common/optional.h>
28 #include <common/compat/directory-handle.h>
29 #include <common/credentials.h>
30 #include <common/defaults.h>
31 #include <common/dynamic-array.h>
32
33 #include <urcu/ref.h>
34 #include <urcu/rculfhash.h>
35 #include <sys/stat.h>
36 #include <inttypes.h>
37 #include <pthread.h>
38 #include <stdio.h>
39
40 /*
41 * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
42 * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
43 */
44 #define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
45 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
46
47 enum trace_chunk_mode {
48 TRACE_CHUNK_MODE_USER,
49 TRACE_CHUNK_MODE_OWNER,
50 };
51
52 /*
53 * Callback to invoke on release of a trace chunk. Note that there is no
54 * need to 'lock' the trace chunk during the execution of these callbacks
55 * since only one thread may access a chunk during its destruction (the last
56 * to release its reference to the chunk).
57 */
58 typedef void (*chunk_close_command)(struct lttng_trace_chunk *trace_chunk);
59
60 /* Move a completed trace chunk to the 'completed' trace archive folder. */
61 static
62 void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk);
63
64 struct chunk_credentials {
65 bool use_current_user;
66 struct lttng_credentials user;
67 };
68
69 struct lttng_trace_chunk {
70 pthread_mutex_t lock;
71 struct urcu_ref ref;
72 LTTNG_OPTIONAL(enum trace_chunk_mode) mode;
73 /*
74 * First-level directories created within the trace chunk.
75 * Elements are of type 'char *'.
76 */
77 struct lttng_dynamic_pointer_array top_level_directories;
78 /* Is contained within an lttng_trace_chunk_registry_element? */
79 bool in_registry_element;
80 bool name_overriden;
81 char *name;
82 /* An unset id means the chunk is anonymous. */
83 LTTNG_OPTIONAL(uint64_t) id;
84 LTTNG_OPTIONAL(time_t) timestamp_creation;
85 LTTNG_OPTIONAL(time_t) timestamp_close;
86 LTTNG_OPTIONAL(struct chunk_credentials) credentials;
87 LTTNG_OPTIONAL(struct lttng_directory_handle) session_output_directory;
88 LTTNG_OPTIONAL(struct lttng_directory_handle) chunk_directory;
89 LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type) close_command;
90 };
91
92 /* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
93 struct lttng_trace_chunk_registry_element {
94 uint64_t session_id;
95 struct lttng_trace_chunk chunk;
96 /* Weak and only set when added. */
97 struct lttng_trace_chunk_registry *registry;
98 struct cds_lfht_node trace_chunk_registry_ht_node;
99 /* call_rcu delayed reclaim. */
100 struct rcu_head rcu_node;
101 };
102
103 struct lttng_trace_chunk_registry {
104 struct cds_lfht *ht;
105 };
106
107 const char *close_command_names[] = {
108 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
109 "move to completed chunk folder",
110 };
111
112 chunk_close_command close_command_funcs[] = {
113 [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
114 lttng_trace_chunk_move_to_completed,
115 };
116
117 static
118 bool lttng_trace_chunk_registry_element_equals(
119 const struct lttng_trace_chunk_registry_element *a,
120 const struct lttng_trace_chunk_registry_element *b)
121 {
122 if (a->session_id != b->session_id) {
123 goto not_equal;
124 }
125 if (a->chunk.id.is_set != b->chunk.id.is_set) {
126 goto not_equal;
127 }
128 if (a->chunk.id.is_set && a->chunk.id.value != b->chunk.id.value) {
129 goto not_equal;
130 }
131 return true;
132 not_equal:
133 return false;
134 }
135
136 static
137 int lttng_trace_chunk_registry_element_match(struct cds_lfht_node *node,
138 const void *key)
139 {
140 const struct lttng_trace_chunk_registry_element *element_a, *element_b;
141
142 element_a = (const struct lttng_trace_chunk_registry_element *) key;
143 element_b = caa_container_of(node, typeof(*element_b),
144 trace_chunk_registry_ht_node);
145 return lttng_trace_chunk_registry_element_equals(element_a, element_b);
146 }
147
148 static
149 unsigned long lttng_trace_chunk_registry_element_hash(
150 const struct lttng_trace_chunk_registry_element *element)
151 {
152 unsigned long hash = hash_key_u64(&element->session_id,
153 lttng_ht_seed);
154
155 if (element->chunk.id.is_set) {
156 hash ^= hash_key_u64(&element->chunk.id.value, lttng_ht_seed);
157 }
158
159 return hash;
160 }
161
162 static
163 char *generate_chunk_name(uint64_t chunk_id, time_t creation_timestamp,
164 const time_t *close_timestamp)
165 {
166 int ret = 0;
167 char *new_name= NULL;
168 char start_datetime[sizeof("YYYYmmddTHHMMSS+HHMM")] = {};
169 char end_datetime_suffix[sizeof("-YYYYmmddTHHMMSS+HHMM")] = {};
170
171 ret = time_to_iso8601_str(
172 creation_timestamp,
173 start_datetime, sizeof(start_datetime));
174 if (ret) {
175 ERR("Failed to format trace chunk start date time");
176 goto error;
177 }
178 if (close_timestamp) {
179 *end_datetime_suffix = '-';
180 ret = time_to_iso8601_str(
181 *close_timestamp,
182 end_datetime_suffix + 1,
183 sizeof(end_datetime_suffix));
184 if (ret) {
185 ERR("Failed to format trace chunk end date time");
186 goto error;
187 }
188 }
189 new_name = zmalloc(GENERATED_CHUNK_NAME_LEN);
190 if (!new_name) {
191 ERR("Failed to allocate buffer for automatically-generated trace chunk name");
192 goto error;
193 }
194 ret = snprintf(new_name, GENERATED_CHUNK_NAME_LEN, "%s%s-%" PRIu64,
195 start_datetime, end_datetime_suffix, chunk_id);
196 if (ret >= GENERATED_CHUNK_NAME_LEN || ret == -1) {
197 ERR("Failed to format trace chunk name");
198 goto error;
199 }
200
201 return new_name;
202 error:
203 free(new_name);
204 return NULL;
205 }
206
207 static
208 void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk)
209 {
210 urcu_ref_init(&chunk->ref);
211 pthread_mutex_init(&chunk->lock, NULL);
212 lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free);
213 }
214
215 static
216 void lttng_trace_chunk_fini(struct lttng_trace_chunk *chunk)
217 {
218 if (chunk->session_output_directory.is_set) {
219 lttng_directory_handle_fini(
220 &chunk->session_output_directory.value);
221 }
222 if (chunk->chunk_directory.is_set) {
223 lttng_directory_handle_fini(&chunk->chunk_directory.value);
224 }
225 free(chunk->name);
226 chunk->name = NULL;
227 lttng_dynamic_pointer_array_reset(&chunk->top_level_directories);
228 pthread_mutex_destroy(&chunk->lock);
229 }
230
231 static
232 struct lttng_trace_chunk *lttng_trace_chunk_allocate(void)
233 {
234 struct lttng_trace_chunk *chunk = NULL;
235
236 chunk = zmalloc(sizeof(*chunk));
237 if (!chunk) {
238 ERR("Failed to allocate trace chunk");
239 goto end;
240 }
241 lttng_trace_chunk_init(chunk);
242 end:
243 return chunk;
244 }
245
246 LTTNG_HIDDEN
247 struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous(void)
248 {
249 DBG("Creating anonymous trace chunk");
250 return lttng_trace_chunk_allocate();
251 }
252
253 LTTNG_HIDDEN
254 struct lttng_trace_chunk *lttng_trace_chunk_create(
255 uint64_t chunk_id, time_t chunk_creation_time)
256 {
257 struct lttng_trace_chunk *chunk;
258 char chunk_creation_datetime_buf[16] = {};
259 const char *chunk_creation_datetime_str = "(formatting error)";
260 struct tm timeinfo_buf, *timeinfo;
261
262 timeinfo = localtime_r(&chunk_creation_time, &timeinfo_buf);
263 if (timeinfo) {
264 size_t strftime_ret;
265
266 /* Don't fail because of this; it is only used for logging. */
267 strftime_ret = strftime(chunk_creation_datetime_buf,
268 sizeof(chunk_creation_datetime_buf),
269 "%Y%m%d-%H%M%S", timeinfo);
270 if (strftime_ret) {
271 chunk_creation_datetime_str =
272 chunk_creation_datetime_buf;
273 }
274 }
275
276 DBG("Creating trace chunk: chunk_id = %" PRIu64 ", creation time = %s",
277 chunk_id, chunk_creation_datetime_str);
278 chunk = lttng_trace_chunk_allocate();
279 if (!chunk) {
280 goto end;
281 }
282
283 LTTNG_OPTIONAL_SET(&chunk->id, chunk_id);
284 LTTNG_OPTIONAL_SET(&chunk->timestamp_creation, chunk_creation_time);
285 if (chunk_id != 0) {
286 chunk->name = generate_chunk_name(chunk_id,
287 chunk_creation_time, NULL);
288 if (!chunk->name) {
289 ERR("Failed to allocate trace chunk name storage");
290 goto error;
291 }
292 }
293
294 DBG("Chunk name set to \"%s\"", chunk->name ? : "(none)");
295 end:
296 return chunk;
297 error:
298 lttng_trace_chunk_put(chunk);
299 return NULL;
300 }
301
302 LTTNG_HIDDEN
303 enum lttng_trace_chunk_status lttng_trace_chunk_get_id(
304 struct lttng_trace_chunk *chunk, uint64_t *id)
305 {
306 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
307
308 pthread_mutex_lock(&chunk->lock);
309 if (chunk->id.is_set) {
310 *id = chunk->id.value;
311 } else {
312 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
313 }
314 pthread_mutex_unlock(&chunk->lock);
315 return status;
316 }
317
318 LTTNG_HIDDEN
319 enum lttng_trace_chunk_status lttng_trace_chunk_get_creation_timestamp(
320 struct lttng_trace_chunk *chunk, time_t *creation_ts)
321
322 {
323 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
324
325 pthread_mutex_lock(&chunk->lock);
326 if (chunk->timestamp_creation.is_set) {
327 *creation_ts = chunk->timestamp_creation.value;
328 } else {
329 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
330 }
331 pthread_mutex_unlock(&chunk->lock);
332 return status;
333 }
334
335 LTTNG_HIDDEN
336 enum lttng_trace_chunk_status lttng_trace_chunk_get_close_timestamp(
337 struct lttng_trace_chunk *chunk, time_t *close_ts)
338 {
339 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
340
341 pthread_mutex_lock(&chunk->lock);
342 if (chunk->timestamp_close.is_set) {
343 *close_ts = chunk->timestamp_close.value;
344 } else {
345 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
346 }
347 pthread_mutex_unlock(&chunk->lock);
348 return status;
349 }
350
351 LTTNG_HIDDEN
352 enum lttng_trace_chunk_status lttng_trace_chunk_set_close_timestamp(
353 struct lttng_trace_chunk *chunk, time_t close_ts)
354 {
355 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
356
357 pthread_mutex_lock(&chunk->lock);
358 if (!chunk->timestamp_creation.is_set) {
359 ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
360 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
361 goto end;
362 }
363 if (chunk->timestamp_creation.value > close_ts) {
364 ERR("Failed to set trace chunk close timestamp: close timestamp is before creation timestamp");
365 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
366 goto end;
367 }
368 LTTNG_OPTIONAL_SET(&chunk->timestamp_close, close_ts);
369 free(chunk->name);
370 chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id),
371 LTTNG_OPTIONAL_GET(chunk->timestamp_creation),
372 &close_ts);
373 if (!chunk->name) {
374 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
375 }
376 end:
377 pthread_mutex_unlock(&chunk->lock);
378 return status;
379 }
380
381 LTTNG_HIDDEN
382 enum lttng_trace_chunk_status lttng_trace_chunk_get_name(
383 struct lttng_trace_chunk *chunk, const char **name,
384 bool *name_overriden)
385 {
386 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
387
388 pthread_mutex_lock(&chunk->lock);
389 if (name_overriden) {
390 *name_overriden = chunk->name_overriden;
391 }
392 if (!chunk->name) {
393 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
394 goto end;
395 }
396 *name = chunk->name;
397 end:
398 pthread_mutex_unlock(&chunk->lock);
399 return status;
400 }
401
402 LTTNG_HIDDEN
403 enum lttng_trace_chunk_status lttng_trace_chunk_override_name(
404 struct lttng_trace_chunk *chunk, const char *name)
405
406 {
407 char *new_name;
408 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
409
410 if (!name || !*name || strnlen(name, LTTNG_NAME_MAX) == LTTNG_NAME_MAX) {
411 ERR("Attempted to set an invalid name on a trace chunk: name = %s",
412 name ? : "NULL");
413 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
414 goto end;
415 }
416
417 pthread_mutex_lock(&chunk->lock);
418 if (!chunk->id.is_set) {
419 ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
420 name);
421 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
422 goto end_unlock;
423 }
424 new_name = strdup(name);
425 if (!new_name) {
426 ERR("Failed to allocate new trace chunk name");
427 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
428 goto end_unlock;
429 }
430 free(chunk->name);
431 chunk->name = new_name;
432 chunk->name_overriden = true;
433 end_unlock:
434 pthread_mutex_unlock(&chunk->lock);
435 end:
436 return status;
437 }
438
439 LTTNG_HIDDEN
440 enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials(
441 struct lttng_trace_chunk *chunk,
442 struct lttng_credentials *credentials)
443 {
444 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
445
446 pthread_mutex_lock(&chunk->lock);
447 if (chunk->credentials.is_set) {
448 if (chunk->credentials.value.use_current_user) {
449 credentials->uid = geteuid();
450 credentials->gid = getegid();
451 } else {
452 *credentials = chunk->credentials.value.user;
453 }
454 } else {
455 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
456 }
457 pthread_mutex_unlock(&chunk->lock);
458 return status;
459 }
460
461 LTTNG_HIDDEN
462 enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials(
463 struct lttng_trace_chunk *chunk,
464 const struct lttng_credentials *user_credentials)
465 {
466 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
467 const struct chunk_credentials credentials = {
468 .user = *user_credentials,
469 .use_current_user = false,
470 };
471
472 pthread_mutex_lock(&chunk->lock);
473 if (chunk->credentials.is_set) {
474 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
475 goto end;
476 }
477 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
478 end:
479 pthread_mutex_unlock(&chunk->lock);
480 return status;
481 }
482
483 LTTNG_HIDDEN
484 enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials_current_user(
485 struct lttng_trace_chunk *chunk)
486 {
487 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
488 const struct chunk_credentials credentials = {
489 .use_current_user = true,
490 };
491
492 pthread_mutex_lock(&chunk->lock);
493 if (chunk->credentials.is_set) {
494 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
495 goto end;
496 }
497 LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
498 end:
499 pthread_mutex_unlock(&chunk->lock);
500 return status;
501 }
502
503
504 LTTNG_HIDDEN
505 enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner(
506 struct lttng_trace_chunk *chunk,
507 struct lttng_directory_handle *session_output_directory)
508 {
509 int ret;
510 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
511 struct lttng_directory_handle chunk_directory_handle;
512
513 pthread_mutex_lock(&chunk->lock);
514 if (chunk->mode.is_set) {
515 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
516 goto end;
517 }
518 if (!chunk->credentials.is_set) {
519 /*
520 * Fatal error, credentials must be set before a
521 * directory is created.
522 */
523 ERR("Credentials of trace chunk are unset: refusing to set session output directory");
524 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
525 goto end;
526 }
527
528 if (chunk->name) {
529 /*
530 * A nameless chunk does not need its own output directory.
531 * The session's output directory will be used.
532 */
533 ret = lttng_directory_handle_create_subdirectory_as_user(
534 session_output_directory,
535 chunk->name,
536 DIR_CREATION_MODE,
537 !chunk->credentials.value.use_current_user ?
538 &chunk->credentials.value.user : NULL);
539 if (ret) {
540 PERROR("Failed to create chunk output directory \"%s\"",
541 chunk->name);
542 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
543 goto end;
544 }
545 }
546 ret = lttng_directory_handle_init_from_handle(&chunk_directory_handle,
547 chunk->name,
548 session_output_directory);
549 if (ret) {
550 /* The function already logs on all error paths. */
551 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
552 goto end;
553 }
554 LTTNG_OPTIONAL_SET(&chunk->session_output_directory,
555 lttng_directory_handle_move(session_output_directory));
556 LTTNG_OPTIONAL_SET(&chunk->chunk_directory,
557 lttng_directory_handle_move(&chunk_directory_handle));
558 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER);
559 end:
560 pthread_mutex_unlock(&chunk->lock);
561 return status;
562 }
563
564 LTTNG_HIDDEN
565 enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user(
566 struct lttng_trace_chunk *chunk,
567 struct lttng_directory_handle *chunk_directory)
568 {
569 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
570
571 pthread_mutex_lock(&chunk->lock);
572 if (chunk->mode.is_set) {
573 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
574 goto end;
575 }
576 if (!chunk->credentials.is_set) {
577 ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
578 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
579 goto end;
580 }
581 LTTNG_OPTIONAL_SET(&chunk->chunk_directory,
582 lttng_directory_handle_move(chunk_directory));
583 LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER);
584 end:
585 pthread_mutex_unlock(&chunk->lock);
586 return status;
587 }
588
589 LTTNG_HIDDEN
590 enum lttng_trace_chunk_status lttng_trace_chunk_get_chunk_directory_handle(
591 struct lttng_trace_chunk *chunk,
592 const struct lttng_directory_handle **handle)
593 {
594 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
595
596 pthread_mutex_lock(&chunk->lock);
597 if (!chunk->chunk_directory.is_set) {
598 status = LTTNG_TRACE_CHUNK_STATUS_NONE;
599 goto end;
600 }
601
602 *handle = &chunk->chunk_directory.value;
603 end:
604 pthread_mutex_unlock(&chunk->lock);
605 return status;
606 }
607
608 /* Add a top-level directory to the trace chunk if it was previously unknown. */
609 static
610 int add_top_level_directory_unique(struct lttng_trace_chunk *chunk,
611 const char *new_path)
612 {
613 int ret = 0;
614 bool found = false;
615 size_t i, count = lttng_dynamic_pointer_array_get_count(
616 &chunk->top_level_directories);
617 const char *new_path_separator_pos = strchr(new_path, '/');
618 const ptrdiff_t new_path_top_level_len = new_path_separator_pos ?
619 new_path_separator_pos - new_path : strlen(new_path);
620
621 for (i = 0; i < count; i++) {
622 const char *path = lttng_dynamic_pointer_array_get_pointer(
623 &chunk->top_level_directories, i);
624 const ptrdiff_t path_top_level_len = strlen(path);
625
626 if (path_top_level_len != new_path_top_level_len) {
627 continue;
628 }
629 if (!strncmp(path, new_path, path_top_level_len)) {
630 found = true;
631 break;
632 }
633 }
634
635 if (!found) {
636 char *copy = strndup(new_path, new_path_top_level_len);
637
638 DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
639 new_path, chunk->name ? : "(unnamed)");
640 if (!copy) {
641 PERROR("Failed to copy path");
642 ret = -1;
643 goto end;
644 }
645 ret = lttng_dynamic_pointer_array_add_pointer(
646 &chunk->top_level_directories, copy);
647 if (ret) {
648 ERR("Allocation failure while adding top-level directory entry to a trace chunk");
649 free(copy);
650 goto end;
651 }
652 }
653 end:
654 return ret;
655 }
656
657 LTTNG_HIDDEN
658 enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory(
659 struct lttng_trace_chunk *chunk,
660 const char *path)
661 {
662 int ret;
663 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
664
665 DBG("Creating trace chunk subdirectory \"%s\"", path);
666 pthread_mutex_lock(&chunk->lock);
667 if (!chunk->credentials.is_set) {
668 /*
669 * Fatal error, credentials must be set before a
670 * directory is created.
671 */
672 ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
673 path);
674 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
675 goto end;
676 }
677 if (!chunk->mode.is_set ||
678 chunk->mode.value != TRACE_CHUNK_MODE_OWNER) {
679 ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
680 path);
681 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
682 goto end;
683 }
684 if (!chunk->chunk_directory.is_set) {
685 ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
686 path);
687 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
688 goto end;
689 }
690 if (*path == '/') {
691 ERR("Refusing to create absolute trace chunk directory \"%s\"",
692 path);
693 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
694 goto end;
695 }
696 ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
697 &chunk->chunk_directory.value, path,
698 DIR_CREATION_MODE,
699 chunk->credentials.value.use_current_user ?
700 NULL : &chunk->credentials.value.user);
701 if (ret) {
702 PERROR("Failed to create trace chunk subdirectory \"%s\"",
703 path);
704 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
705 goto end;
706 }
707 ret = add_top_level_directory_unique(chunk, path);
708 if (ret) {
709 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
710 goto end;
711 }
712 end:
713 pthread_mutex_unlock(&chunk->lock);
714 return status;
715 }
716
717 LTTNG_HIDDEN
718 enum lttng_trace_chunk_status lttng_trace_chunk_open_file(
719 struct lttng_trace_chunk *chunk, const char *file_path,
720 int flags, mode_t mode, int *out_fd)
721 {
722 int ret;
723 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
724
725 DBG("Opening trace chunk file \"%s\"", file_path);
726 pthread_mutex_lock(&chunk->lock);
727 if (!chunk->credentials.is_set) {
728 /*
729 * Fatal error, credentials must be set before a
730 * file is created.
731 */
732 ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
733 file_path);
734 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
735 goto end;
736 }
737 if (!chunk->chunk_directory.is_set) {
738 ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
739 file_path);
740 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
741 goto end;
742 }
743 ret = lttng_directory_handle_open_file_as_user(
744 &chunk->chunk_directory.value, file_path, flags, mode,
745 chunk->credentials.value.use_current_user ?
746 NULL : &chunk->credentials.value.user);
747 if (ret < 0) {
748 ERR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
749 file_path, flags, (int) mode);
750 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
751 goto end;
752 }
753 *out_fd = ret;
754 end:
755 pthread_mutex_unlock(&chunk->lock);
756 return status;
757 }
758
759 LTTNG_HIDDEN
760 int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk,
761 const char *file_path)
762 {
763 int ret;
764 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
765
766 DBG("Unlinking trace chunk file \"%s\"", file_path);
767 pthread_mutex_lock(&chunk->lock);
768 if (!chunk->credentials.is_set) {
769 /*
770 * Fatal error, credentials must be set before a
771 * directory is created.
772 */
773 ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
774 file_path);
775 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
776 goto end;
777 }
778 if (!chunk->chunk_directory.is_set) {
779 ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
780 file_path);
781 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
782 goto end;
783 }
784 ret = lttng_directory_handle_unlink_file_as_user(
785 &chunk->chunk_directory.value, file_path,
786 chunk->credentials.value.use_current_user ?
787 NULL : &chunk->credentials.value.user);
788 if (ret < 0) {
789 status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
790 goto end;
791 }
792 end:
793 pthread_mutex_unlock(&chunk->lock);
794 return status;
795 }
796
797 static
798 void lttng_trace_chunk_move_to_completed(struct lttng_trace_chunk *trace_chunk)
799 {
800 int ret;
801 char *directory_to_rename = NULL;
802 bool free_directory_to_rename = false;
803 const int session_dirfd =
804 trace_chunk->session_output_directory.value.dirfd;
805 char *archived_chunk_name = NULL;
806 const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id);
807 const time_t creation_timestamp =
808 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation);
809 const time_t close_timestamp =
810 LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close);
811 LTTNG_OPTIONAL(struct lttng_directory_handle) archived_chunks_directory;
812
813 assert(trace_chunk->mode.is_set);
814 assert(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
815 assert(!trace_chunk->name_overriden);
816
817 /*
818 * The fist trace chunk of a session is directly output to the
819 * session's output folder. In this case, the top level directories
820 * must be moved to a temporary folder before that temporary directory
821 * is renamed to match the chunk's name.
822 */
823 if (chunk_id == 0) {
824 struct lttng_directory_handle temporary_rename_directory;
825 size_t i, count = lttng_dynamic_pointer_array_get_count(
826 &trace_chunk->top_level_directories);
827
828 ret = lttng_directory_handle_create_subdirectory_as_user(
829 &trace_chunk->session_output_directory.value,
830 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY,
831 DIR_CREATION_MODE,
832 !trace_chunk->credentials.value.use_current_user ?
833 &trace_chunk->credentials.value.user : NULL);
834 if (ret) {
835 PERROR("Failed to create temporary trace chunk rename directory \"%s\"",
836 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY);
837 }
838
839 ret = lttng_directory_handle_init_from_handle(&temporary_rename_directory,
840 DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY,
841 &trace_chunk->session_output_directory.value);
842 if (ret) {
843 ERR("Failed to get handle to temporary trace chunk rename directory");
844 goto end;
845 }
846
847 for (i = 0; i < count; i++) {
848 const int temp_dirfd = temporary_rename_directory.dirfd;
849 const char *top_level_name =
850 lttng_dynamic_pointer_array_get_pointer(
851 &trace_chunk->top_level_directories, i);
852
853 /*
854 * FIXME replace renamat() use by directory handle
855 * wrapper for non-POSIX 2008 systems.
856 */
857 ret = renameat(session_dirfd, top_level_name,
858 temp_dirfd, top_level_name);
859 if (ret) {
860 PERROR("Failed to move \"%s\" to temporary trace chunk rename directory",
861 top_level_name);
862 lttng_directory_handle_fini(
863 &temporary_rename_directory);
864 goto end;
865 }
866 }
867 lttng_directory_handle_fini(&temporary_rename_directory);
868 directory_to_rename = DEFAULT_TEMPORARY_CHUNK_RENAME_DIRECTORY;
869 free_directory_to_rename = false;
870 } else {
871 directory_to_rename = generate_chunk_name(chunk_id,
872 creation_timestamp, NULL);
873 if (!directory_to_rename) {
874 ERR("Failed to generate initial trace chunk name while renaming trace chunk");
875 }
876 free_directory_to_rename = true;
877 }
878
879 archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp,
880 &close_timestamp);
881 if (!archived_chunk_name) {
882 ERR("Failed to generate archived trace chunk name while renaming trace chunk");
883 goto end;
884 }
885
886 ret = lttng_directory_handle_create_subdirectory_as_user(
887 &trace_chunk->session_output_directory.value,
888 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
889 DIR_CREATION_MODE,
890 !trace_chunk->credentials.value.use_current_user ?
891 &trace_chunk->credentials.value.user :
892 NULL);
893 if (ret) {
894 PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
895 "\" directory for archived trace chunks");
896 goto end;
897 }
898
899 ret = lttng_directory_handle_init_from_handle(
900 &archived_chunks_directory.value,
901 DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
902 &trace_chunk->session_output_directory.value);
903 if (ret) {
904 PERROR("Failed to get handle to archived trace chunks directory");
905 goto end;
906 }
907 archived_chunks_directory.is_set = true;
908
909 /*
910 * FIXME replace renamat() use by directory handle
911 * wrapper for non-POSIX 2008 systems.
912 */
913 ret = renameat(session_dirfd, directory_to_rename,
914 archived_chunks_directory.value.dirfd,
915 archived_chunk_name);
916 if (ret) {
917 PERROR("Failed to rename folder \"%s\" to \"%s\"",
918 directory_to_rename, archived_chunk_name);
919 }
920
921 end:
922 if (archived_chunks_directory.is_set) {
923 lttng_directory_handle_fini(&archived_chunks_directory.value);
924 }
925 free(archived_chunk_name);
926 if (free_directory_to_rename) {
927 free(directory_to_rename);
928 }
929 }
930
931 LTTNG_HIDDEN
932 enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
933 struct lttng_trace_chunk *chunk,
934 enum lttng_trace_chunk_command_type close_command)
935 {
936 enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
937
938 if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
939 close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
940 status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
941 goto end_unlock;
942 }
943
944 pthread_mutex_lock(&chunk->lock);
945 if (chunk->close_command.is_set) {
946 DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
947 close_command_names[chunk->close_command.value],
948 close_command_names[close_command]);
949 } else {
950 DBG("Setting trace chunk close command to \"%s\"",
951 close_command_names[close_command]);
952 }
953 LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
954 pthread_mutex_unlock(&chunk->lock);
955 end_unlock:
956 return status;
957 }
958
959 LTTNG_HIDDEN
960 bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
961 {
962 return urcu_ref_get_unless_zero(&chunk->ref);
963 }
964
965 static
966 void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
967 {
968 struct lttng_trace_chunk_registry_element *element =
969 container_of(node, typeof(*element), rcu_node);
970
971 lttng_trace_chunk_fini(&element->chunk);
972 free(element);
973 }
974
975 static
976 void lttng_trace_chunk_release(struct urcu_ref *ref)
977 {
978 struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk),
979 ref);
980
981 if (chunk->close_command.is_set) {
982 close_command_funcs[chunk->close_command.value](chunk);
983 }
984
985 if (chunk->in_registry_element) {
986 struct lttng_trace_chunk_registry_element *element;
987
988 element = container_of(chunk, typeof(*element), chunk);
989 if (element->registry) {
990 rcu_read_lock();
991 cds_lfht_del(element->registry->ht,
992 &element->trace_chunk_registry_ht_node);
993 rcu_read_unlock();
994 call_rcu(&element->rcu_node,
995 free_lttng_trace_chunk_registry_element);
996 } else {
997 /* Never published, can be free'd immediately. */
998 free_lttng_trace_chunk_registry_element(
999 &element->rcu_node);
1000 }
1001 } else {
1002 /* Not RCU-protected, free immediately. */
1003 lttng_trace_chunk_fini(chunk);
1004 free(chunk);
1005 }
1006 }
1007
1008 LTTNG_HIDDEN
1009 void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
1010 {
1011 if (!chunk) {
1012 return;
1013 }
1014 assert(chunk->ref.refcount);
1015 urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
1016 }
1017
1018 LTTNG_HIDDEN
1019 struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void)
1020 {
1021 struct lttng_trace_chunk_registry *registry;
1022
1023 registry = zmalloc(sizeof(*registry));
1024 if (!registry) {
1025 goto end;
1026 }
1027
1028 registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
1029 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
1030 if (!registry->ht) {
1031 goto error;
1032 }
1033 end:
1034 return registry;
1035 error:
1036 lttng_trace_chunk_registry_destroy(registry);
1037 goto end;
1038 }
1039
1040 LTTNG_HIDDEN
1041 void lttng_trace_chunk_registry_destroy(
1042 struct lttng_trace_chunk_registry *registry)
1043 {
1044 if (!registry) {
1045 return;
1046 }
1047 if (registry->ht) {
1048 int ret = cds_lfht_destroy(registry->ht, NULL);
1049 assert(!ret);
1050 }
1051 free(registry);
1052 }
1053
1054 static
1055 struct lttng_trace_chunk_registry_element *
1056 lttng_trace_chunk_registry_element_create_from_chunk(
1057 struct lttng_trace_chunk *chunk, uint64_t session_id)
1058 {
1059 struct lttng_trace_chunk_registry_element *element =
1060 zmalloc(sizeof(*element));
1061
1062 if (!element) {
1063 goto end;
1064 }
1065 cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
1066 element->session_id = session_id;
1067
1068 element->chunk = *chunk;
1069 lttng_trace_chunk_init(&element->chunk);
1070 if (chunk->session_output_directory.is_set) {
1071 element->chunk.session_output_directory.value =
1072 lttng_directory_handle_move(
1073 &chunk->session_output_directory.value);
1074 }
1075 if (chunk->chunk_directory.is_set) {
1076 element->chunk.chunk_directory.value =
1077 lttng_directory_handle_move(
1078 &chunk->chunk_directory.value);
1079 }
1080 /*
1081 * The original chunk becomes invalid; the name attribute is transferred
1082 * to the new chunk instance.
1083 */
1084 chunk->name = NULL;
1085 element->chunk.in_registry_element = true;
1086 end:
1087 return element;
1088 }
1089
1090 LTTNG_HIDDEN
1091 struct lttng_trace_chunk *
1092 lttng_trace_chunk_registry_publish_chunk(
1093 struct lttng_trace_chunk_registry *registry,
1094 uint64_t session_id, struct lttng_trace_chunk *chunk)
1095 {
1096 struct lttng_trace_chunk_registry_element *element;
1097 unsigned long element_hash;
1098
1099 pthread_mutex_lock(&chunk->lock);
1100 element = lttng_trace_chunk_registry_element_create_from_chunk(chunk,
1101 session_id);
1102 pthread_mutex_unlock(&chunk->lock);
1103 if (!element) {
1104 goto end;
1105 }
1106 /*
1107 * chunk is now invalid, the only valid operation is a 'put' from the
1108 * caller.
1109 */
1110 chunk = NULL;
1111 element_hash = lttng_trace_chunk_registry_element_hash(element);
1112
1113 rcu_read_lock();
1114 while (1) {
1115 struct cds_lfht_node *published_node;
1116 struct lttng_trace_chunk *published_chunk;
1117 struct lttng_trace_chunk_registry_element *published_element;
1118
1119 published_node = cds_lfht_add_unique(registry->ht,
1120 element_hash,
1121 lttng_trace_chunk_registry_element_match,
1122 element,
1123 &element->trace_chunk_registry_ht_node);
1124 if (published_node == &element->trace_chunk_registry_ht_node) {
1125 /* Successfully published the new element. */
1126 element->registry = registry;
1127 /* Acquire a reference for the caller. */
1128 if (lttng_trace_chunk_get(&element->chunk)) {
1129 break;
1130 } else {
1131 /*
1132 * Another thread concurrently unpublished the
1133 * trace chunk. This is currently unexpected.
1134 *
1135 * Re-attempt to publish.
1136 */
1137 ERR("Attemp to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
1138 continue;
1139 }
1140 }
1141
1142 /*
1143 * An equivalent trace chunk was published before this trace
1144 * chunk. Attempt to acquire a reference to the one that was
1145 * already published and release the reference to the copy we
1146 * created if successful.
1147 */
1148 published_element = container_of(published_node,
1149 typeof(*published_element),
1150 trace_chunk_registry_ht_node);
1151 published_chunk = &published_element->chunk;
1152 if (lttng_trace_chunk_get(published_chunk)) {
1153 lttng_trace_chunk_put(&element->chunk);
1154 element = published_element;
1155 break;
1156 }
1157 /*
1158 * A reference to the previously published trace chunk could not
1159 * be acquired. Hence, retry to publish our copy of the trace
1160 * chunk.
1161 */
1162 }
1163 rcu_read_unlock();
1164 end:
1165 return element ? &element->chunk : NULL;
1166 }
1167
1168 /*
1169 * Note that the caller must be registered as an RCU thread.
1170 * However, it does not need to hold the RCU read lock. The RCU read lock is
1171 * acquired to perform the look-up in the registry's hash table and held until
1172 * after a reference to the "found" trace chunk is acquired.
1173 *
1174 * IOW, holding a reference guarantees the existence of the object for the
1175 * caller.
1176 */
1177 static
1178 struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
1179 const struct lttng_trace_chunk_registry *registry,
1180 uint64_t session_id, uint64_t *chunk_id)
1181 {
1182 const struct lttng_trace_chunk_registry_element target_element = {
1183 .chunk.id.is_set = !!chunk_id,
1184 .chunk.id.value = chunk_id ? *chunk_id : 0,
1185 .session_id = session_id,
1186 };
1187 const unsigned long element_hash =
1188 lttng_trace_chunk_registry_element_hash(
1189 &target_element);
1190 struct cds_lfht_node *published_node;
1191 struct lttng_trace_chunk_registry_element *published_element;
1192 struct lttng_trace_chunk *published_chunk = NULL;
1193 struct cds_lfht_iter iter;
1194
1195 rcu_read_lock();
1196 cds_lfht_lookup(registry->ht,
1197 element_hash,
1198 lttng_trace_chunk_registry_element_match,
1199 &target_element,
1200 &iter);
1201 published_node = cds_lfht_iter_get_node(&iter);
1202 if (!published_node) {
1203 goto end;
1204 }
1205
1206 published_element = container_of(published_node,
1207 typeof(*published_element),
1208 trace_chunk_registry_ht_node);
1209 if (lttng_trace_chunk_get(&published_element->chunk)) {
1210 published_chunk = &published_element->chunk;
1211 }
1212 end:
1213 rcu_read_unlock();
1214 return published_chunk;
1215 }
1216
1217 LTTNG_HIDDEN
1218 struct lttng_trace_chunk *
1219 lttng_trace_chunk_registry_find_chunk(
1220 const struct lttng_trace_chunk_registry *registry,
1221 uint64_t session_id, uint64_t chunk_id)
1222 {
1223 return _lttng_trace_chunk_registry_find_chunk(registry,
1224 session_id, &chunk_id);
1225 }
1226
1227 LTTNG_HIDDEN
1228 struct lttng_trace_chunk *
1229 lttng_trace_chunk_registry_find_anonymous_chunk(
1230 const struct lttng_trace_chunk_registry *registry,
1231 uint64_t session_id)
1232 {
1233 return _lttng_trace_chunk_registry_find_chunk(registry,
1234 session_id, NULL);
1235 }
This page took 0.093474 seconds and 4 git commands to generate.