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