action-executor: consider action firing policy on action execution
[lttng-tools.git] / src / common / actions / firing-policy.c
CommitLineData
347f2c91
JR
1/*
2 * Copyright (C) 2021 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only
5 *
6 */
7
8#include <assert.h>
9#include <common/buffer-view.h>
10#include <common/dynamic-buffer.h>
11#include <common/error.h>
12#include <common/macros.h>
13#include <common/payload-view.h>
14#include <common/payload.h>
15#include <limits.h>
16#include <lttng/action/firing-policy-internal.h>
17#include <lttng/action/firing-policy.h>
18#include <stdbool.h>
19#include <sys/types.h>
20
21#define IS_EVERY_N_FIRING_POLICY(policy) \
22 (lttng_firing_policy_get_type(policy) == \
23 LTTNG_FIRING_POLICY_TYPE_EVERY_N)
24
25#define IS_ONCE_AFTER_N_FIRING_POLICY(policy) \
26 (lttng_firing_policy_get_type(policy) == \
27 LTTNG_FIRING_POLICY_TYPE_ONCE_AFTER_N)
28
29typedef void (*firing_policy_destroy_cb)(
30 struct lttng_firing_policy *firing_policy);
31typedef int (*firing_policy_serialize_cb)(
32 struct lttng_firing_policy *firing_policy,
33 struct lttng_payload *payload);
34typedef bool (*firing_policy_equal_cb)(const struct lttng_firing_policy *a,
35 const struct lttng_firing_policy *b);
36typedef ssize_t (*firing_policy_create_from_payload_cb)(
37 struct lttng_payload_view *view,
38 struct lttng_firing_policy **firing_policy);
39typedef struct lttng_firing_policy *(*firing_policy_copy_cb)(
40 const struct lttng_firing_policy *source);
41
42struct lttng_firing_policy {
43 enum lttng_firing_policy_type type;
44 firing_policy_serialize_cb serialize;
45 firing_policy_equal_cb equal;
46 firing_policy_destroy_cb destroy;
47 firing_policy_copy_cb copy;
48};
49
50struct lttng_firing_policy_every_n {
51 struct lttng_firing_policy parent;
52 uint64_t interval;
53};
54
55struct lttng_firing_policy_once_after_n {
56 struct lttng_firing_policy parent;
57 uint64_t threshold;
58};
59
60struct lttng_firing_policy_comm {
61 /* enum lttng_firing_policy_type */
62 int8_t firing_policy_type;
63} LTTNG_PACKED;
64
65struct lttng_firing_policy_once_after_n_comm {
66 uint64_t threshold;
67} LTTNG_PACKED;
68
69struct lttng_firing_policy_every_n_comm {
70 uint64_t interval;
71} LTTNG_PACKED;
72
73/* Forward declaration. */
74static void lttng_firing_policy_init(struct lttng_firing_policy *firing_policy,
75 enum lttng_firing_policy_type type,
76 firing_policy_serialize_cb serialize,
77 firing_policy_equal_cb equal,
78 firing_policy_destroy_cb destroy,
79 firing_policy_copy_cb copy);
80
2d57482c
JR
81/* Forward declaration. Every n */
82static bool lttng_firing_policy_every_n_should_execute(
83 const struct lttng_firing_policy *policy, uint64_t counter);
84
85/* Forward declaration. Once after N */
86static bool lttng_firing_policy_once_after_n_should_execute(
87 const struct lttng_firing_policy *policy, uint64_t counter);
88
347f2c91
JR
89LTTNG_HIDDEN
90const char *lttng_firing_policy_type_string(
91 enum lttng_firing_policy_type firing_policy_type)
92{
93 switch (firing_policy_type) {
94 case LTTNG_FIRING_POLICY_TYPE_EVERY_N:
95 return "EVERY-N";
96 case LTTNG_FIRING_POLICY_TYPE_ONCE_AFTER_N:
97 return "ONCE-AFTER-N";
98 default:
99 return "???";
100 }
101}
102
103enum lttng_firing_policy_type lttng_firing_policy_get_type(
104 const struct lttng_firing_policy *policy)
105{
106 return policy ? policy->type : LTTNG_FIRING_POLICY_TYPE_UNKNOWN;
107}
108
109LTTNG_HIDDEN
110void lttng_firing_policy_init(struct lttng_firing_policy *firing_policy,
111 enum lttng_firing_policy_type type,
112 firing_policy_serialize_cb serialize,
113 firing_policy_equal_cb equal,
114 firing_policy_destroy_cb destroy,
115 firing_policy_copy_cb copy)
116{
117 firing_policy->type = type;
118 firing_policy->serialize = serialize;
119 firing_policy->equal = equal;
120 firing_policy->destroy = destroy;
121 firing_policy->copy = copy;
122}
123
124void lttng_firing_policy_destroy(struct lttng_firing_policy *firing_policy)
125{
126 if (!firing_policy) {
127 return;
128 }
129
130 firing_policy->destroy(firing_policy);
131}
132
133LTTNG_HIDDEN
134int lttng_firing_policy_serialize(struct lttng_firing_policy *firing_policy,
135 struct lttng_payload *payload)
136{
137 int ret;
138 const struct lttng_firing_policy_comm firing_policy_comm = {
139 .firing_policy_type = (int8_t) firing_policy->type,
140 };
141
142 ret = lttng_dynamic_buffer_append(&payload->buffer, &firing_policy_comm,
143 sizeof(firing_policy_comm));
144 if (ret) {
145 goto end;
146 }
147
148 ret = firing_policy->serialize(firing_policy, payload);
149 if (ret) {
150 goto end;
151 }
152end:
153 return ret;
154}
155
156static ssize_t lttng_firing_policy_once_after_n_create_from_payload(
157 struct lttng_payload_view *view,
158 struct lttng_firing_policy **firing_policy)
159{
160 ssize_t consumed_len = -1;
161 struct lttng_firing_policy *policy = NULL;
162 const struct lttng_firing_policy_once_after_n_comm *comm;
163 const struct lttng_payload_view comm_view =
164 lttng_payload_view_from_view(view, 0, sizeof(*comm));
165
166 if (!view || !firing_policy) {
167 consumed_len = -1;
168 goto end;
169 }
170
171 if (!lttng_payload_view_is_valid(&comm_view)) {
172 /* Payload not large enough to contain the header. */
173 consumed_len = -1;
174 goto end;
175 }
176
177 comm = (const struct lttng_firing_policy_once_after_n_comm *)
178 comm_view.buffer.data;
179
180 policy = lttng_firing_policy_once_after_n_create(comm->threshold);
181 if (policy == NULL) {
182 consumed_len = -1;
183 goto end;
184 }
185
186 *firing_policy = policy;
187 consumed_len = sizeof(*comm);
188
189end:
190 return consumed_len;
191}
192
193static ssize_t lttng_firing_policy_every_n_create_from_payload(
194 struct lttng_payload_view *view,
195 struct lttng_firing_policy **firing_policy)
196{
197 ssize_t consumed_len = -1;
198 struct lttng_firing_policy *policy = NULL;
199 const struct lttng_firing_policy_every_n_comm *comm;
200 const struct lttng_payload_view comm_view =
201 lttng_payload_view_from_view(view, 0, sizeof(*comm));
202
203 if (!view || !firing_policy) {
204 consumed_len = -1;
205 goto end;
206 }
207
208 if (!lttng_payload_view_is_valid(&comm_view)) {
209 /* Payload not large enough to contain the header. */
210 consumed_len = -1;
211 goto end;
212 }
213
214 comm = (const struct lttng_firing_policy_every_n_comm *)
215 comm_view.buffer.data;
216
217 policy = lttng_firing_policy_every_n_create(comm->interval);
218 if (policy == NULL) {
219 consumed_len = -1;
220 goto end;
221 }
222
223 *firing_policy = policy;
224 consumed_len = sizeof(*comm);
225
226end:
227 return consumed_len;
228}
229
230LTTNG_HIDDEN
231ssize_t lttng_firing_policy_create_from_payload(struct lttng_payload_view *view,
232 struct lttng_firing_policy **firing_policy)
233{
234 ssize_t consumed_len, specific_firing_policy_consumed_len;
235 firing_policy_create_from_payload_cb create_from_payload_cb;
236 const struct lttng_firing_policy_comm *firing_policy_comm;
237 const struct lttng_payload_view firing_policy_comm_view =
238 lttng_payload_view_from_view(
239 view, 0, sizeof(*firing_policy_comm));
240
241 if (!view || !firing_policy) {
242 consumed_len = -1;
243 goto end;
244 }
245
246 if (!lttng_payload_view_is_valid(&firing_policy_comm_view)) {
247 /* Payload not large enough to contain the header. */
248 consumed_len = -1;
249 goto end;
250 }
251
252 firing_policy_comm =
253 (const struct lttng_firing_policy_comm *)
254 firing_policy_comm_view.buffer.data;
255
256 DBG("Create firing_policy from payload: firing-policy-type=%s",
257 lttng_firing_policy_type_string(
258 firing_policy_comm->firing_policy_type));
259
260 switch (firing_policy_comm->firing_policy_type) {
261 case LTTNG_FIRING_POLICY_TYPE_EVERY_N:
262 create_from_payload_cb =
263 lttng_firing_policy_every_n_create_from_payload;
264 break;
265 case LTTNG_FIRING_POLICY_TYPE_ONCE_AFTER_N:
266 create_from_payload_cb =
267 lttng_firing_policy_once_after_n_create_from_payload;
268 break;
269 default:
270 ERR("Failed to create firing-policy from payload, unhandled firing-policy type: firing-policy-type=%u (%s)",
271 firing_policy_comm->firing_policy_type,
272 lttng_firing_policy_type_string(firing_policy_comm->firing_policy_type));
273 consumed_len = -1;
274 goto end;
275 }
276
277 {
278 /*
279 * Create buffer view for the firing_policy-type-specific data.
280 */
281 struct lttng_payload_view specific_firing_policy_view =
282 lttng_payload_view_from_view(view,
283 sizeof(struct lttng_firing_policy_comm),
284 -1);
285
286 specific_firing_policy_consumed_len = create_from_payload_cb(
287 &specific_firing_policy_view, firing_policy);
288 }
289
290 if (specific_firing_policy_consumed_len < 0) {
291 ERR("Failed to create specific firing_policy from buffer");
292 consumed_len = -1;
293 goto end;
294 }
295
296 assert(*firing_policy);
297
298 consumed_len = sizeof(struct lttng_firing_policy_comm) +
299 specific_firing_policy_consumed_len;
300
301end:
302 return consumed_len;
303}
304
305LTTNG_HIDDEN
306bool lttng_firing_policy_is_equal(const struct lttng_firing_policy *a,
307 const struct lttng_firing_policy *b)
308{
309 bool is_equal = false;
310
311 if (!a || !b) {
312 goto end;
313 }
314
315 if (a->type != b->type) {
316 goto end;
317 }
318
319 if (a == b) {
320 is_equal = true;
321 goto end;
322 }
323
324 assert(a->equal);
325 is_equal = a->equal(a, b);
326end:
327 return is_equal;
328}
329
2d57482c
JR
330LTTNG_HIDDEN
331bool lttng_firing_policy_should_execute(
332 const struct lttng_firing_policy *policy, uint64_t counter)
333{
334 switch (policy->type) {
335 case LTTNG_FIRING_POLICY_TYPE_EVERY_N:
336 return lttng_firing_policy_every_n_should_execute(
337 policy, counter);
338 case LTTNG_FIRING_POLICY_TYPE_ONCE_AFTER_N:
339 return lttng_firing_policy_once_after_n_should_execute(
340 policy, counter);
341 default:
342 abort();
343 break;
344 }
345}
346
347f2c91
JR
347/* Every N */
348static const struct lttng_firing_policy_every_n *
349firing_policy_every_n_from_firing_policy_const(
350 const struct lttng_firing_policy *policy)
351{
352 assert(policy);
353
354 return container_of(policy, const struct lttng_firing_policy_every_n,
355 parent);
356}
357
358static int lttng_firing_policy_every_n_serialize(
359 struct lttng_firing_policy *policy,
360 struct lttng_payload *payload)
361{
362 int ret;
363 const struct lttng_firing_policy_every_n *every_n_policy;
364 struct lttng_firing_policy_every_n_comm comm = {};
365
366 assert(policy);
367 assert(payload);
368
369 every_n_policy = firing_policy_every_n_from_firing_policy_const(policy);
370 comm.interval = every_n_policy->interval;
371
372 ret = lttng_dynamic_buffer_append(
373 &payload->buffer, &comm, sizeof(comm));
374 return ret;
375}
376
377static bool lttng_firing_policy_every_n_is_equal(
378 const struct lttng_firing_policy *_a,
379 const struct lttng_firing_policy *_b)
380{
381 bool is_equal = false;
382 const struct lttng_firing_policy_every_n *a, *b;
383
384 a = firing_policy_every_n_from_firing_policy_const(_a);
385 b = firing_policy_every_n_from_firing_policy_const(_b);
386
387 if (a->interval != b->interval) {
388 goto end;
389 }
390
391 is_equal = true;
392
393end:
394 return is_equal;
395}
396
397static void lttng_firing_policy_every_n_destroy(
398 struct lttng_firing_policy *policy)
399{
400 /* Nothing type-specific to release. */
401 free(policy);
402}
403
404static struct lttng_firing_policy *lttng_firing_policy_every_n_copy(
405 const struct lttng_firing_policy *source)
406{
407 struct lttng_firing_policy *copy = NULL;
408 const struct lttng_firing_policy_every_n *every_n_policy;
409
410 if (!source) {
411 goto end;
412 }
413
414 every_n_policy = firing_policy_every_n_from_firing_policy_const(source);
415 copy = lttng_firing_policy_every_n_create(
416 every_n_policy->interval);
417
418end:
419 return copy;
420}
421
422LTTNG_HIDDEN
423struct lttng_firing_policy *lttng_firing_policy_every_n_create(
424 uint64_t interval)
425{
426 struct lttng_firing_policy_every_n *policy = NULL;
427
2d57482c
JR
428 if (interval == 0) {
429 /*
430 * An interval of 0 is invalid since it would never be fired.
431 */
432 goto end;
433 }
434
347f2c91
JR
435 policy = zmalloc(sizeof(struct lttng_firing_policy_every_n));
436 if (!policy) {
437 goto end;
438 }
439
440 lttng_firing_policy_init(&policy->parent,
441 LTTNG_FIRING_POLICY_TYPE_EVERY_N,
442 lttng_firing_policy_every_n_serialize,
443 lttng_firing_policy_every_n_is_equal,
444 lttng_firing_policy_every_n_destroy,
445 lttng_firing_policy_every_n_copy);
446
447 policy->interval = interval;
448
449end:
450 return policy ? &policy->parent : NULL;
451}
452
453LTTNG_HIDDEN
454enum lttng_firing_policy_status lttng_firing_policy_every_n_get_interval(
455 const struct lttng_firing_policy *policy, uint64_t *interval)
456{
457 const struct lttng_firing_policy_every_n *every_n_policy;
458 enum lttng_firing_policy_status status;
459
460 if (!policy || !IS_EVERY_N_FIRING_POLICY(policy) || !interval) {
461 status = LTTNG_FIRING_POLICY_STATUS_INVALID;
462 goto end;
463 }
464
465 every_n_policy = firing_policy_every_n_from_firing_policy_const(policy);
466 *interval = every_n_policy->interval;
467 status = LTTNG_FIRING_POLICY_STATUS_OK;
468end:
469
470 return status;
471}
472
2d57482c
JR
473static bool lttng_firing_policy_every_n_should_execute(
474 const struct lttng_firing_policy *policy, uint64_t counter)
475{
476 const struct lttng_firing_policy_every_n *every_n_policy;
477 assert(policy);
478 bool execute = false;
479
480 every_n_policy = firing_policy_every_n_from_firing_policy_const(policy);
481
482 if (every_n_policy->interval == 0) {
483 abort();
484 }
485
486 execute = (counter % every_n_policy->interval) == 0;
487
488 DBG("Policy every N = %" PRIu64
489 ": execution %s. Execution count: %" PRIu64,
490 every_n_policy->interval,
491 execute ? "accepted" : "denied", counter);
492
493 return execute;
494}
495
347f2c91
JR
496/* Once after N */
497
498static const struct lttng_firing_policy_once_after_n *
499firing_policy_once_after_n_from_firing_policy_const(
500 const struct lttng_firing_policy *policy)
501{
502 assert(policy);
503
504 return container_of(policy, struct lttng_firing_policy_once_after_n,
505 parent);
506}
507
508static int lttng_firing_policy_once_after_n_serialize(
509 struct lttng_firing_policy *policy,
510 struct lttng_payload *payload)
511{
512 int ret;
513 const struct lttng_firing_policy_once_after_n *once_after_n_policy;
514 struct lttng_firing_policy_once_after_n_comm comm = {};
515
516 assert(policy);
517 assert(payload);
518
519 once_after_n_policy =
520 firing_policy_once_after_n_from_firing_policy_const(
521 policy);
522 comm.threshold = once_after_n_policy->threshold;
523
524 ret = lttng_dynamic_buffer_append(
525 &payload->buffer, &comm, sizeof(comm));
526 return ret;
527}
528
529static bool lttng_firing_policy_once_after_n_is_equal(
530 const struct lttng_firing_policy *_a,
531 const struct lttng_firing_policy *_b)
532{
533 bool is_equal = false;
534 const struct lttng_firing_policy_once_after_n *a, *b;
535
536 a = firing_policy_once_after_n_from_firing_policy_const(_a);
537 b = firing_policy_once_after_n_from_firing_policy_const(_b);
538
539 if (a->threshold != b->threshold) {
540 goto end;
541 }
542
543 is_equal = true;
544
545end:
546 return is_equal;
547}
548
549static void lttng_firing_policy_once_after_n_destroy(
550 struct lttng_firing_policy *policy)
551{
552 /* Nothing type specific to release. */
553 free(policy);
554}
555
556static struct lttng_firing_policy *lttng_firing_policy_once_after_n_copy(
557 const struct lttng_firing_policy *source)
558{
559 struct lttng_firing_policy *copy = NULL;
560 const struct lttng_firing_policy_once_after_n *once_after_n_policy;
561
562 if (!source) {
563 goto end;
564 }
565
566 once_after_n_policy =
567 firing_policy_once_after_n_from_firing_policy_const(
568 source);
569 copy = lttng_firing_policy_once_after_n_create(
570 once_after_n_policy->threshold);
571
572end:
573 return copy;
574}
575
576LTTNG_HIDDEN
577struct lttng_firing_policy *lttng_firing_policy_once_after_n_create(
578 uint64_t threshold)
579{
580 struct lttng_firing_policy_once_after_n *policy = NULL;
581
2d57482c
JR
582 if (threshold == 0) {
583 /* threshold is expected to be > 0 */
584 goto end;
585 }
586
347f2c91
JR
587 policy = zmalloc(sizeof(struct lttng_firing_policy_once_after_n));
588 if (!policy) {
589 goto end;
590 }
591
592 lttng_firing_policy_init(&policy->parent,
593 LTTNG_FIRING_POLICY_TYPE_ONCE_AFTER_N,
594 lttng_firing_policy_once_after_n_serialize,
595 lttng_firing_policy_once_after_n_is_equal,
596 lttng_firing_policy_once_after_n_destroy,
597 lttng_firing_policy_once_after_n_copy);
598
599 policy->threshold = threshold;
600
601end:
602 return policy ? &policy->parent : NULL;
603}
604
605LTTNG_HIDDEN
606enum lttng_firing_policy_status lttng_firing_policy_once_after_n_get_threshold(
607 const struct lttng_firing_policy *policy, uint64_t *threshold)
608{
609 const struct lttng_firing_policy_once_after_n *once_after_n_policy;
610 enum lttng_firing_policy_status status;
611
612 if (!policy || !IS_ONCE_AFTER_N_FIRING_POLICY(policy) || !threshold) {
613 status = LTTNG_FIRING_POLICY_STATUS_INVALID;
614 goto end;
615 }
616
617 once_after_n_policy =
618 firing_policy_once_after_n_from_firing_policy_const(
619 policy);
620 *threshold = once_after_n_policy->threshold;
621 status = LTTNG_FIRING_POLICY_STATUS_OK;
622
623end:
624 return status;
625}
626
627LTTNG_HIDDEN
628struct lttng_firing_policy *lttng_firing_policy_copy(
629 const struct lttng_firing_policy *source)
630{
631 assert(source->copy);
632 return source->copy(source);
633}
2d57482c
JR
634
635static bool lttng_firing_policy_once_after_n_should_execute(
636 const struct lttng_firing_policy *policy, uint64_t counter)
637{
638 const struct lttng_firing_policy_once_after_n *once_after_n_policy;
639 bool execute = false;
640 assert(policy);
641
642 once_after_n_policy =
643 firing_policy_once_after_n_from_firing_policy_const(
644 policy);
645
646 execute = counter == once_after_n_policy->threshold;
647
648 DBG("Policy once after N = %" PRIu64
649 ": execution %s. Execution count: %" PRIu64,
650 once_after_n_policy->threshold,
651 execute ? "accepted" : "denied", counter);
652
653 return counter == once_after_n_policy->threshold;
654}
This page took 0.046097 seconds and 4 git commands to generate.