action-executor: consider action firing policy on action execution
[lttng-tools.git] / src / common / actions / firing-policy.c
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
29 typedef void (*firing_policy_destroy_cb)(
30 struct lttng_firing_policy *firing_policy);
31 typedef int (*firing_policy_serialize_cb)(
32 struct lttng_firing_policy *firing_policy,
33 struct lttng_payload *payload);
34 typedef bool (*firing_policy_equal_cb)(const struct lttng_firing_policy *a,
35 const struct lttng_firing_policy *b);
36 typedef ssize_t (*firing_policy_create_from_payload_cb)(
37 struct lttng_payload_view *view,
38 struct lttng_firing_policy **firing_policy);
39 typedef struct lttng_firing_policy *(*firing_policy_copy_cb)(
40 const struct lttng_firing_policy *source);
41
42 struct 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
50 struct lttng_firing_policy_every_n {
51 struct lttng_firing_policy parent;
52 uint64_t interval;
53 };
54
55 struct lttng_firing_policy_once_after_n {
56 struct lttng_firing_policy parent;
57 uint64_t threshold;
58 };
59
60 struct lttng_firing_policy_comm {
61 /* enum lttng_firing_policy_type */
62 int8_t firing_policy_type;
63 } LTTNG_PACKED;
64
65 struct lttng_firing_policy_once_after_n_comm {
66 uint64_t threshold;
67 } LTTNG_PACKED;
68
69 struct lttng_firing_policy_every_n_comm {
70 uint64_t interval;
71 } LTTNG_PACKED;
72
73 /* Forward declaration. */
74 static 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
81 /* Forward declaration. Every n */
82 static 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 */
86 static bool lttng_firing_policy_once_after_n_should_execute(
87 const struct lttng_firing_policy *policy, uint64_t counter);
88
89 LTTNG_HIDDEN
90 const 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
103 enum 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
109 LTTNG_HIDDEN
110 void 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
124 void 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
133 LTTNG_HIDDEN
134 int 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 }
152 end:
153 return ret;
154 }
155
156 static 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
189 end:
190 return consumed_len;
191 }
192
193 static 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
226 end:
227 return consumed_len;
228 }
229
230 LTTNG_HIDDEN
231 ssize_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
301 end:
302 return consumed_len;
303 }
304
305 LTTNG_HIDDEN
306 bool 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);
326 end:
327 return is_equal;
328 }
329
330 LTTNG_HIDDEN
331 bool 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
347 /* Every N */
348 static const struct lttng_firing_policy_every_n *
349 firing_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
358 static 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
377 static 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
393 end:
394 return is_equal;
395 }
396
397 static 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
404 static 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
418 end:
419 return copy;
420 }
421
422 LTTNG_HIDDEN
423 struct lttng_firing_policy *lttng_firing_policy_every_n_create(
424 uint64_t interval)
425 {
426 struct lttng_firing_policy_every_n *policy = NULL;
427
428 if (interval == 0) {
429 /*
430 * An interval of 0 is invalid since it would never be fired.
431 */
432 goto end;
433 }
434
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
449 end:
450 return policy ? &policy->parent : NULL;
451 }
452
453 LTTNG_HIDDEN
454 enum 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;
468 end:
469
470 return status;
471 }
472
473 static 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
496 /* Once after N */
497
498 static const struct lttng_firing_policy_once_after_n *
499 firing_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
508 static 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
529 static 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
545 end:
546 return is_equal;
547 }
548
549 static 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
556 static 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
572 end:
573 return copy;
574 }
575
576 LTTNG_HIDDEN
577 struct 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
582 if (threshold == 0) {
583 /* threshold is expected to be > 0 */
584 goto end;
585 }
586
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
601 end:
602 return policy ? &policy->parent : NULL;
603 }
604
605 LTTNG_HIDDEN
606 enum 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
623 end:
624 return status;
625 }
626
627 LTTNG_HIDDEN
628 struct 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 }
634
635 static 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.042946 seconds and 4 git commands to generate.