Tests: Add test to check shared-memory FD leaks after relayd dies
[lttng-tools.git] / src / lib / lttng-ctl / rotate.c
1 /*
2 * Copyright (C) 2017 Julien Desfossez <jdesfossez@efficios.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only
5 *
6 */
7
8 #define _LGPL_SOURCE
9 #include <string.h>
10
11 #include <lttng/lttng-error.h>
12 #include <lttng/rotation.h>
13 #include <lttng/location-internal.h>
14 #include <lttng/rotate-internal.h>
15 #include <common/sessiond-comm/sessiond-comm.h>
16 #include <common/macros.h>
17
18 #include "lttng-ctl-helper.h"
19
20 static
21 enum lttng_rotation_status ask_rotation_info(
22 struct lttng_rotation_handle *rotation_handle,
23 struct lttng_rotation_get_info_return **info)
24 {
25 /* lsm.get_rotation_state.rotation_id */
26 struct lttcomm_session_msg lsm;
27 enum lttng_rotation_status status = LTTNG_ROTATION_STATUS_OK;
28 int ret;
29
30 if (!rotation_handle || !info) {
31 status = LTTNG_ROTATION_STATUS_INVALID;
32 goto end;
33 }
34
35 memset(&lsm, 0, sizeof(lsm));
36 lsm.cmd_type = LTTNG_ROTATION_GET_INFO;
37 lsm.u.get_rotation_info.rotation_id = rotation_handle->rotation_id;
38
39 ret = lttng_strncpy(lsm.session.name, rotation_handle->session_name,
40 sizeof(lsm.session.name));
41 if (ret) {
42 status = LTTNG_ROTATION_STATUS_INVALID;
43 goto end;
44 }
45
46 ret = lttng_ctl_ask_sessiond(&lsm, (void **) info);
47 if (ret < 0) {
48 status = LTTNG_ROTATION_STATUS_ERROR;
49 goto end;
50 }
51 end:
52 return status;
53
54 }
55
56 static
57 struct lttng_trace_archive_location *
58 create_trace_archive_location_from_get_info(
59 const struct lttng_rotation_get_info_return *info)
60 {
61 struct lttng_trace_archive_location *location;
62
63 switch (info->location_type) {
64 case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL:
65 location = lttng_trace_archive_location_local_create(
66 info->location.local.absolute_path);
67 break;
68 case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY:
69 location = lttng_trace_archive_location_relay_create(
70 info->location.relay.host,
71 info->location.relay.protocol,
72 info->location.relay.ports.control,
73 info->location.relay.ports.data,
74 info->location.relay.relative_path);
75 break;
76 default:
77 location = NULL;
78 break;
79 }
80 return location;
81 }
82
83 enum lttng_rotation_status lttng_rotation_handle_get_state(
84 struct lttng_rotation_handle *rotation_handle,
85 enum lttng_rotation_state *state)
86 {
87 enum lttng_rotation_status status = LTTNG_ROTATION_STATUS_OK;
88 struct lttng_rotation_get_info_return *info = NULL;
89
90 if (!rotation_handle || !state) {
91 status = LTTNG_ROTATION_STATUS_INVALID;
92 goto end;
93 }
94
95 status = ask_rotation_info(rotation_handle, &info);
96 if (status != LTTNG_ROTATION_STATUS_OK) {
97 goto end;
98 }
99
100 *state = (enum lttng_rotation_state) info->status;
101 if (rotation_handle->archive_location ||
102 *state != LTTNG_ROTATION_STATE_COMPLETED) {
103 /*
104 * The path is only provided by the sessiond once
105 * the session rotation is completed, but not expired.
106 */
107 goto end;
108 }
109
110 /*
111 * Cache the location since the rotation may expire before the user
112 * has a chance to query it.
113 */
114 rotation_handle->archive_location =
115 create_trace_archive_location_from_get_info(info);
116 if (!rotation_handle->archive_location) {
117 status = LTTNG_ROTATION_STATUS_ERROR;
118 goto end;
119 }
120 end:
121 free(info);
122 return status;
123 }
124
125 enum lttng_rotation_status lttng_rotation_handle_get_archive_location(
126 struct lttng_rotation_handle *rotation_handle,
127 const struct lttng_trace_archive_location **location)
128 {
129 enum lttng_rotation_status status = LTTNG_ROTATION_STATUS_OK;
130 struct lttng_rotation_get_info_return *info = NULL;
131
132 if (!rotation_handle || !location) {
133 status = LTTNG_ROTATION_STATUS_INVALID;
134 goto end;
135 }
136
137 /* Use the cached location we got from a previous query. */
138 if (rotation_handle->archive_location) {
139 *location = rotation_handle->archive_location;
140 goto end;
141 }
142
143 status = ask_rotation_info(rotation_handle, &info);
144 if (status != LTTNG_ROTATION_STATUS_OK) {
145 goto end;
146 }
147
148 if ((enum lttng_rotation_state) info->status !=
149 LTTNG_ROTATION_STATE_COMPLETED) {
150 status = LTTNG_ROTATION_STATUS_UNAVAILABLE;
151 goto end;
152 }
153
154 rotation_handle->archive_location =
155 create_trace_archive_location_from_get_info(info);
156 if (!rotation_handle->archive_location) {
157 status = LTTNG_ROTATION_STATUS_ERROR;
158 goto end;
159 }
160 end:
161 free(info);
162 return status;
163 }
164
165 void lttng_rotation_handle_destroy(
166 struct lttng_rotation_handle *rotation_handle)
167 {
168 if (!rotation_handle) {
169 return;
170 }
171 lttng_trace_archive_location_put(rotation_handle->archive_location);
172 free(rotation_handle);
173 }
174
175 static
176 int init_rotation_handle(struct lttng_rotation_handle *rotation_handle,
177 const char *session_name,
178 struct lttng_rotate_session_return *rotate_return)
179 {
180 int ret;
181
182 ret = lttng_strncpy(rotation_handle->session_name, session_name,
183 sizeof(rotation_handle->session_name));
184 if (ret) {
185 goto end;
186 }
187
188 rotation_handle->rotation_id = rotate_return->rotation_id;
189 end:
190 return ret;
191 }
192
193 /*
194 * Rotate the output folder of the session.
195 *
196 * Return 0 on success else a negative LTTng error code.
197 */
198 int lttng_rotate_session(const char *session_name,
199 struct lttng_rotation_immediate_descriptor *descriptor,
200 struct lttng_rotation_handle **rotation_handle)
201 {
202 struct lttcomm_session_msg lsm;
203 struct lttng_rotate_session_return *rotate_return = NULL;
204 int ret;
205 size_t session_name_len;
206
207 if (!session_name) {
208 ret = -LTTNG_ERR_INVALID;
209 goto end;
210 }
211
212 session_name_len = strlen(session_name);
213 if (session_name_len >= sizeof(lsm.session.name) ||
214 session_name_len >= member_sizeof(struct lttng_rotation_handle, session_name)) {
215 ret = -LTTNG_ERR_INVALID;
216 goto end;
217 }
218
219 memset(&lsm, 0, sizeof(lsm));
220 lsm.cmd_type = LTTNG_ROTATE_SESSION;
221
222 ret = lttng_strncpy(lsm.session.name, session_name,
223 sizeof(lsm.session.name));
224 /* Source length already validated. */
225 LTTNG_ASSERT(ret == 0);
226
227 ret = lttng_ctl_ask_sessiond(&lsm, (void **) &rotate_return);
228 if (ret <= 0) {
229 *rotation_handle = NULL;
230 goto end;
231 }
232
233 *rotation_handle = zmalloc(sizeof(struct lttng_rotation_handle));
234 if (!*rotation_handle) {
235 ret = -LTTNG_ERR_NOMEM;
236 goto end;
237 }
238
239 init_rotation_handle(*rotation_handle, session_name, rotate_return);
240
241 ret = 0;
242
243 end:
244 free(rotate_return);
245 return ret;
246 }
247
248 /*
249 * Update the automatic rotation parameters.
250 * 'add' as true enables the provided schedule, false removes the shedule.
251 *
252 * The external API makes it appear as though arbitrary schedules can
253 * be added or removed at will. However, the session daemon is
254 * currently limited to one schedule per type (per session).
255 *
256 * The additional flexibility of the public API is offered for future
257 * rotation schedules that could indicate more precise criteria than
258 * size and time (e.g. a domain) where it could make sense to add
259 * multiple schedules of a given type to a session.
260 *
261 * Hence, the exact schedule that the user wishes to remove (and not
262 * just its type) must be passed so that the session daemon can
263 * validate that is exists before clearing it.
264 */
265 static
266 enum lttng_rotation_status lttng_rotation_update_schedule(
267 const char *session_name,
268 const struct lttng_rotation_schedule *schedule,
269 bool add)
270 {
271 struct lttcomm_session_msg lsm;
272 enum lttng_rotation_status status = LTTNG_ROTATION_STATUS_OK;
273 int ret;
274
275 if (!session_name || !schedule) {
276 status = LTTNG_ROTATION_STATUS_INVALID;
277 goto end;
278 }
279
280 if (strlen(session_name) >= sizeof(lsm.session.name)) {
281 status = LTTNG_ROTATION_STATUS_INVALID;
282 goto end;
283 }
284
285 memset(&lsm, 0, sizeof(lsm));
286 lsm.cmd_type = LTTNG_ROTATION_SET_SCHEDULE;
287 ret = lttng_strncpy(lsm.session.name, session_name,
288 sizeof(lsm.session.name));
289 /* Source length already validated. */
290 LTTNG_ASSERT(ret == 0);
291
292 lsm.u.rotation_set_schedule.type = (uint32_t) schedule->type;
293 switch (schedule->type) {
294 case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD:
295 {
296 uint64_t threshold;
297
298 status = lttng_rotation_schedule_size_threshold_get_threshold(
299 schedule, &threshold);
300 if (status != LTTNG_ROTATION_STATUS_OK) {
301 if (status == LTTNG_ROTATION_STATUS_UNAVAILABLE) {
302 status = LTTNG_ROTATION_STATUS_INVALID;
303 }
304 goto end;
305 }
306 lsm.u.rotation_set_schedule.value = threshold;
307 lsm.u.rotation_set_schedule.set = !!add;
308 break;
309 }
310 case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC:
311 {
312 uint64_t period;
313
314 status = lttng_rotation_schedule_periodic_get_period(
315 schedule, &period);
316 if (status != LTTNG_ROTATION_STATUS_OK) {
317 if (status == LTTNG_ROTATION_STATUS_UNAVAILABLE) {
318 status = LTTNG_ROTATION_STATUS_INVALID;
319 }
320 goto end;
321 }
322 lsm.u.rotation_set_schedule.value = period;
323 lsm.u.rotation_set_schedule.set = !!add;
324 break;
325 }
326 default:
327 status = LTTNG_ROTATION_STATUS_INVALID;
328 goto end;
329 }
330
331 ret = lttng_ctl_ask_sessiond(&lsm, NULL);
332 if (ret >= 0) {
333 goto end;
334 }
335
336 switch (-ret) {
337 case LTTNG_ERR_ROTATION_SCHEDULE_SET:
338 status = LTTNG_ROTATION_STATUS_SCHEDULE_ALREADY_SET;
339 break;
340 case LTTNG_ERR_ROTATION_SCHEDULE_NOT_SET:
341 status = LTTNG_ROTATION_STATUS_INVALID;
342 break;
343 default:
344 status = LTTNG_ROTATION_STATUS_ERROR;
345 }
346 end:
347 return status;
348 }
349
350 static
351 struct lttng_rotation_schedules *lttng_rotation_schedules_create(void)
352 {
353 return zmalloc(sizeof(struct lttng_rotation_schedules));
354 }
355
356 static
357 void lttng_schedules_add(struct lttng_rotation_schedules *schedules,
358 struct lttng_rotation_schedule *schedule)
359 {
360 schedules->schedules[schedules->count++] = schedule;
361 }
362
363 static
364 int get_schedules(const char *session_name,
365 struct lttng_rotation_schedules **_schedules)
366 {
367 int ret;
368 struct lttcomm_session_msg lsm;
369 struct lttng_session_list_schedules_return *schedules_comm = NULL;
370 struct lttng_rotation_schedules *schedules = NULL;
371 struct lttng_rotation_schedule *periodic = NULL, *size = NULL;
372
373 if (!session_name) {
374 ret = -LTTNG_ERR_INVALID;
375 goto end;
376 }
377
378 memset(&lsm, 0, sizeof(lsm));
379 lsm.cmd_type = LTTNG_SESSION_LIST_ROTATION_SCHEDULES;
380 ret = lttng_strncpy(lsm.session.name, session_name,
381 sizeof(lsm.session.name));
382 if (ret) {
383 ret = -LTTNG_ERR_INVALID;
384 goto end;
385 }
386
387 ret = lttng_ctl_ask_sessiond(&lsm, (void **) &schedules_comm);
388 if (ret < 0) {
389 goto end;
390 }
391
392 schedules = lttng_rotation_schedules_create();
393 if (!schedules) {
394 ret = -LTTNG_ERR_NOMEM;
395 goto end;
396 }
397
398 if (schedules_comm->periodic.set == 1) {
399 enum lttng_rotation_status status;
400
401 periodic = lttng_rotation_schedule_periodic_create();
402 if (!periodic) {
403 ret = -LTTNG_ERR_NOMEM;
404 goto end;
405 }
406
407 status = lttng_rotation_schedule_periodic_set_period(
408 periodic, schedules_comm->periodic.value);
409 if (status != LTTNG_ROTATION_STATUS_OK) {
410 /*
411 * This would imply that the session daemon returned
412 * an invalid periodic rotation schedule value.
413 */
414 ret = -LTTNG_ERR_UNK;
415 goto end;
416 }
417
418 lttng_schedules_add(schedules, periodic);
419 periodic = NULL;
420 }
421
422 if (schedules_comm->size.set == 1) {
423 enum lttng_rotation_status status;
424
425 size = lttng_rotation_schedule_size_threshold_create();
426 if (!size) {
427 ret = -LTTNG_ERR_NOMEM;
428 goto end;
429 }
430
431 status = lttng_rotation_schedule_size_threshold_set_threshold(
432 size, schedules_comm->size.value);
433 if (status != LTTNG_ROTATION_STATUS_OK) {
434 /*
435 * This would imply that the session daemon returned
436 * an invalid size threshold schedule value.
437 */
438 ret = -LTTNG_ERR_UNK;
439 goto end;
440 }
441
442 lttng_schedules_add(schedules, size);
443 size = NULL;
444 }
445
446 ret = LTTNG_OK;
447 end:
448 free(schedules_comm);
449 free(periodic);
450 free(size);
451 *_schedules = schedules;
452 return ret;
453 }
454
455 enum lttng_rotation_schedule_type lttng_rotation_schedule_get_type(
456 const struct lttng_rotation_schedule *schedule)
457 {
458 return schedule ? schedule->type : LTTNG_ROTATION_SCHEDULE_TYPE_UNKNOWN;
459 }
460
461 struct lttng_rotation_schedule *
462 lttng_rotation_schedule_size_threshold_create(void)
463 {
464 struct lttng_rotation_schedule_size_threshold *schedule;
465
466 schedule = zmalloc(sizeof(*schedule));
467 if (!schedule) {
468 goto end;
469 }
470
471 schedule->parent.type = LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD;
472 end:
473 return &schedule->parent;
474 }
475
476 enum lttng_rotation_status
477 lttng_rotation_schedule_size_threshold_get_threshold(
478 const struct lttng_rotation_schedule *schedule,
479 uint64_t *size_threshold_bytes)
480 {
481 enum lttng_rotation_status status = LTTNG_ROTATION_STATUS_OK;
482 struct lttng_rotation_schedule_size_threshold *size_schedule;
483
484 if (!schedule || !size_threshold_bytes ||
485 schedule->type != LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD) {
486 status = LTTNG_ROTATION_STATUS_INVALID;
487 goto end;
488 }
489
490 size_schedule = container_of(schedule,
491 struct lttng_rotation_schedule_size_threshold,
492 parent);
493 if (size_schedule->size.set) {
494 *size_threshold_bytes = size_schedule->size.bytes;
495 } else {
496 status = LTTNG_ROTATION_STATUS_UNAVAILABLE;
497 goto end;
498 }
499 end:
500 return status;
501 }
502
503 enum lttng_rotation_status
504 lttng_rotation_schedule_size_threshold_set_threshold(
505 struct lttng_rotation_schedule *schedule,
506 uint64_t size_threshold_bytes)
507 {
508 enum lttng_rotation_status status = LTTNG_ROTATION_STATUS_OK;
509 struct lttng_rotation_schedule_size_threshold *size_schedule;
510
511 if (!schedule || size_threshold_bytes == 0 ||
512 size_threshold_bytes == -1ULL ||
513 schedule->type != LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD) {
514 status = LTTNG_ROTATION_STATUS_INVALID;
515 goto end;
516 }
517
518 size_schedule = container_of(schedule,
519 struct lttng_rotation_schedule_size_threshold,
520 parent);
521 size_schedule->size.bytes = size_threshold_bytes;
522 size_schedule->size.set = true;
523 end:
524 return status;
525 }
526
527 struct lttng_rotation_schedule *
528 lttng_rotation_schedule_periodic_create(void)
529 {
530 struct lttng_rotation_schedule_periodic *schedule;
531
532 schedule = zmalloc(sizeof(*schedule));
533 if (!schedule) {
534 goto end;
535 }
536
537 schedule->parent.type = LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC;
538 end:
539 return &schedule->parent;
540 }
541
542 enum lttng_rotation_status
543 lttng_rotation_schedule_periodic_get_period(
544 const struct lttng_rotation_schedule *schedule,
545 uint64_t *period_us)
546 {
547 enum lttng_rotation_status status = LTTNG_ROTATION_STATUS_OK;
548 struct lttng_rotation_schedule_periodic *periodic_schedule;
549
550 if (!schedule || !period_us ||
551 schedule->type != LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC) {
552 status = LTTNG_ROTATION_STATUS_INVALID;
553 goto end;
554 }
555
556 periodic_schedule = container_of(schedule,
557 struct lttng_rotation_schedule_periodic,
558 parent);
559 if (periodic_schedule->period.set) {
560 *period_us = periodic_schedule->period.us;
561 } else {
562 status = LTTNG_ROTATION_STATUS_UNAVAILABLE;
563 goto end;
564 }
565 end:
566 return status;
567 }
568
569 enum lttng_rotation_status
570 lttng_rotation_schedule_periodic_set_period(
571 struct lttng_rotation_schedule *schedule,
572 uint64_t period_us)
573 {
574 enum lttng_rotation_status status = LTTNG_ROTATION_STATUS_OK;
575 struct lttng_rotation_schedule_periodic *periodic_schedule;
576
577 if (!schedule || period_us == 0 || period_us == -1ULL ||
578 schedule->type != LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC) {
579 status = LTTNG_ROTATION_STATUS_INVALID;
580 goto end;
581 }
582
583 periodic_schedule = container_of(schedule,
584 struct lttng_rotation_schedule_periodic,
585 parent);
586 periodic_schedule->period.us = period_us;
587 periodic_schedule->period.set = true;
588 end:
589 return status;
590 }
591
592 void lttng_rotation_schedule_destroy(struct lttng_rotation_schedule *schedule)
593 {
594 if (!schedule) {
595 return;
596 }
597 free(schedule);
598 }
599
600 void lttng_rotation_schedules_destroy(
601 struct lttng_rotation_schedules *schedules)
602 {
603 unsigned int i;
604
605 if (!schedules) {
606 return;
607 }
608
609 for (i = 0; i < schedules->count; i++) {
610 lttng_rotation_schedule_destroy(schedules->schedules[i]);
611 }
612 free(schedules);
613 }
614
615
616 enum lttng_rotation_status lttng_rotation_schedules_get_count(
617 const struct lttng_rotation_schedules *schedules,
618 unsigned int *count)
619 {
620 enum lttng_rotation_status status = LTTNG_ROTATION_STATUS_OK;
621
622 if (!schedules || !count) {
623 status = LTTNG_ROTATION_STATUS_INVALID;
624 goto end;
625 }
626
627 *count = schedules->count;
628 end:
629 return status;
630 }
631
632 const struct lttng_rotation_schedule *lttng_rotation_schedules_get_at_index(
633 const struct lttng_rotation_schedules *schedules,
634 unsigned int index)
635 {
636 const struct lttng_rotation_schedule *schedule = NULL;
637
638 if (!schedules || index >= schedules->count) {
639 goto end;
640 }
641
642 schedule = schedules->schedules[index];
643 end:
644 return schedule;
645 }
646
647 enum lttng_rotation_status lttng_session_add_rotation_schedule(
648 const char *session_name,
649 const struct lttng_rotation_schedule *schedule)
650 {
651 return lttng_rotation_update_schedule(session_name, schedule, true);
652 }
653
654 enum lttng_rotation_status lttng_session_remove_rotation_schedule(
655 const char *session_name,
656 const struct lttng_rotation_schedule *schedule)
657 {
658 return lttng_rotation_update_schedule(session_name, schedule, false);
659 }
660
661 int lttng_session_list_rotation_schedules(
662 const char *session_name,
663 struct lttng_rotation_schedules **schedules)
664 {
665 return get_schedules(session_name, schedules);
666 }
This page took 0.04138 seconds and 4 git commands to generate.